lundi 15 octobre 2012

NuGet sous Visual Studio 2010

NuGet est une extension Visual Studio qui permet d'installer et de mettre à jour facilement les références utilisées par vos projets. Cette extension peut-être installée facilement depuis le gestionnaire d'extensions de Visual Studio 2010 ("Outils", "Gestionnaire d'extensions...") :

Chercher l'extension "NuGet Package Manager" dans les extensions disponibles et cliquer sur le bouton "Télécharger" :

Après installation, il est nécessaire de relancer Visual Studio :

Un nouveau menu ("Outils", "Gestionnaire de package de bibliothèques") est maintenant disponible :

Ouvrir un projet et lancer la console. La commande "install-package EntityFramework" permet alors d'installer automatiquement la dernière version en date d'Entity Framework :

On remarque après installation que la version correcte est installée. Entity Framework 5 installe en effet une version 4.4 dans les projets basés sur .NET Framework 4, l'intégralité des fonctionnalités de cette version n'étant disponible que sous .NET Framework 4.5 :

vendredi 7 septembre 2012

"Model Binding" avec ASP.NET 4.5, Visual Studio 2012

.NET Framework 4.5 et Visual Studio 2012 sont maintenant disponibles depuis le 15 août 2012. Il est noter que cette version remplace totalement la version 4.0 même si les "breaking changes" sont minimisés. Ce billet va exposer brièvement la nouvelle fonctionnalité de "Model Binding" ajoutée aux WebForms.

La première étape sera d'ajouter à un nouveau projet web, la base SQL Server Compact C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Samples\Northwind.sdf, puis de créer un nouveau modèle "ADO.NET Entity Data Model". On peut noter en passant que ce modèle "database first" génère du code basé sur l'API DbContext (et non plus ObjectContext).

Ce modèle permet donc d'accéder à la base. Bien que cela soit un peu surperflu dans ce cas précis, nous allons maintenant créer un "ViewModel", chargé d'adapter spécifiquement le modèle permettant d'accéder à la base aux besoins particuliers de la vue que nous allons créer. Cette vue va être constitué d'une grille et d'un formulaire permettant d'éditer la ligne sélectionnée. Notre "ViewModel" va donc exposer la liste de toutes les catégories, la possibilité de sélectionner une catégorie en particulier, de la mettre à jour et de récupérer les erreurs éventuelles de mise à jour ce qui nous donne le code ci-dessous :

using System.Collections.Generic;
using System.Linq;
namespace WebApplication1
{
    public class CategoriesViewModel
    {
        private NorthwindEntities model = new NorthwindEntities();
        public string Error;
        public IQueryable Select()
        {
            return model.Categories;
        }
        public bool Update(Category category)
        {
            model.Entry(category).State = System.Data.EntityState.Modified;
            Error = "";
            try
            {
                model.SaveChanges();
            }
            catch (System.Data.Entity.Validation.DbEntityValidationException ex)
            {
                foreach (var v in ex.EntityValidationErrors)
                {
                    if (!v.IsValid)
                        foreach (var o in v.ValidationErrors)
                        {
                            Error += o.ErrorMessage;
                        }
                }
                return false;
            }
            return true;
        }
        public Category Select(object[] id)
        {
            return model.Categories.Find(id);
        }
    }
}

Notre vue va être constituée par :

<asp:GridView runat="server"
    ID="gridView1" 
    ItemType="WebApplication1.Category"
    AllowSorting="True"
    AutoGenerateSelectButton="true"
    DataKeyNames="Category_ID"
    SelectMethod="gridView1_SelectMethod" 
/>
<asp:FormView  runat="server" 
    ID="formView1" 
    ItemType="WebApplication1.Category"
    DataKeyNames="Category_ID"
    SelectMethod="formView1_SelectMethod"
    UpdateMethod="formView1_UpdateMethod">
    <ItemTemplate>
        <asp:Button ID="Button1" runat="server" CommandName="Edit" Text="Edit"/>
        <asp:Label runat="server" Text=">%#Item.Category_Name%>" ID="a"/>
    </ItemTemplate>
    <EditItemTemplate>
        <asp:Button ID="Button1" runat="server" CommandName="Update" Text="Update"/>
        <asp:TextBox runat="server" Text="<%#BindItem.Category_Name%>" ID="a" />
    </EditItemTemplate>

En plus des atrributs habituels des versions précédentes on notera :
  • ItemType qui permet d'indiquer le type de la donnée affichée
  • SelectMethod et UpdateMethod (éventuellement DeleteMethod, InsertMethod) qui permettent d'indiquer les procédures qui permettront de sélectionner, modifier, supprimer et ajouter les données depuis ces contrôles
  • Item et BindItem qui permettent d'accéder de façon fortement typé à l'entité à afficher dans le cadre d'une expression de "binding"

Finalement, ne reste que le "code-behind" qui va donc lier cette vue à notre "ViewModel" et qui se réduit à :

using System;
using System.Linq;
using System.Web;
using System.Web.UI;
namespace WebApplication1
{
    public partial class _Default : Page
    {
        private CategoriesViewModel categories = new CategoriesViewModel();
        public IQueryable gridView1_SelectMethod()
        {
            return categories.Select();
        }
        public Category formView1_SelectMethod([System.Web.ModelBinding.Control("gridView1", "SelectedValue")] object[] id)
        {
            return categories.Select(id);
        }
        public void formView1_UpdateMethod(Category a)
        {
            categories.Update(a);
            lbl.Text = categories.Error;
        }
    }
}
La sélection des données de la grille ne fait donc que sélectionner toutes les catégories. On remarque que la méthode de sélection de l'entité à afficher dans le contrôle FormView, utilise un attribut Control qui permet d'indiquer que le paramètre de cette méthode sera à extraire automatiquement de la propriété SelectedValue du contrôle gridView1 ce qui permet de récupérer la clé primaire de la catégorie concernée. Enfin la méthode Update se contente également d'appeler la méthode de mise à jour de notre "ViewModel", puis d'afficher les éventuelles erreurs.

Nous avons vu que cette nouvelle fonctionnalité de "Model Binding", avec le respect de certaines bonnes pratiques, devrait permettre de simplifier encore l'utilisation des WebForms ASP.NET en permettant de séparer plus clairement la vue et les méthodes de "Binding" qui l'alimentent et de réduire probablement le "code-behind" au rôle de "glue" entre la vue et le "ViewModel". A suivre...

jeudi 28 juin 2012

ADO.NET - Entity Framework

Entity Framework 4.1 ou plus introduit une nouvelle API basée sur la classe DbContext qui propose trois approches :

  • "Model First" : le modèle des données est créé via le concepteur Visual Studio
  • "Database First" : le modèle des données est créé à partir d'une base de données existante, modifié dans Visual Studio. Un modèle T4 permet de générer le code des entités correspondantes (par défaut avec l'ancienne API ObjectContext ou sur demande avec la nouvelle API DbContext).
  • "Code First" : le modèle des données est défini directement dans le code de l'application.
Le fichier utilisé sera cette fois C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Samples, la version 3.5 ne permettant pas de récupérer les valeurs "identity". De même le fichier de configuration de l'application est changé pour adresser cette version :
<configuration>
  <connectionStrings>
    <add name="Data" connectionString="Data Source=Northwind.sdf" providerName="System.Data.SqlServerCe.4.0"/>
  </connectionStrings>
</configuration>
Le fichier Category.cs va permettre de décrire la classe qui va représenter nos données :
namespace ConsoleApplication1
{
    public class Category
    {
        public virtual int CategoryID { get; set; }
        public virtual string CategoryName { get; set; }
    }
}
Le fichier Data.cs décrit le "contexte" qui permettra de prendre en charge la mise à jour de la base :
using System.Data.Entity;
namespace ConsoleApplication1
{
    public class Data : DbContext
    {
        public DbSet Categories { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity().Property(p => p.CategoryID).HasColumnName("Category ID");
            modelBuilder.Entity().Property(p => p.CategoryName).HasColumnName("Category Name");
        }
    }
}
La méthode OnModelCreating permet notamment d'indiquer les noms des colonnes présentes dans la base de données. Par convention, la table porte le même nom que la classe et une propriété dont le nom se termine par "id" est une clé primaire. Au final, le code permet d'accéder très naturellement à la liste des catégories :
using System;
using System.Linq;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Data data = new Data();
            foreach (var item in from o in data.Categories orderby o.CategoryName ascending select o)
            {
                item.CategoryName = item.CategoryName.EndsWith("z") ? item.CategoryName.Substring(0,item.CategoryName.Length - 1) : item.CategoryName + "z";
                Console.WriteLine(item.CategoryName);
            }
            data.SaveChanges();
            Console.ReadKey();
        }
    }
}
La ligne foreach peut également être exprimée sous la forme foreach(var item in data.Categories.OrderBy((o)=>o.CategoryName)).

mercredi 27 juin 2012

ADO.NET - Mode déconnecté

La classe Data reste globalement inchangée et n'ajoute qu'une méthode GetAdapter. L'objet retourné par cette méthode répertorie les différentes commandes qui vont permettre de supprimer, créer, mettre à jour les données, commandes générés à partir de la requête de sélection :

        public DbDataAdapter GetAdapter(string commandText,params object[] values)
        {
            var builder = factory.CreateCommandBuilder();
            builder.DataAdapter = factory.CreateDataAdapter();
            builder.DataAdapter.SelectCommand = GetCommand(commandText, values);
            builder.DataAdapter.DeleteCommand = builder.GetDeleteCommand();
            builder.DataAdapter.InsertCommand = builder.GetInsertCommand();
            builder.DataAdapter.UpdateCommand = builder.GetUpdateCommand();
            builder.DataAdapter.SelectCommand.Connection.Close();
            return builder.DataAdapter;
        }
Cet objet peut alors être utilisé dans le programme principal pour lire les données dans un objet DataTable ou DataSet (qui représentent un cache en lecture/écriture d'une table ou d'un ensemble de tables). Les données stockées par cet objet peuvent alors être modifiées à volonté avant d'appeler la méthode Update qui va examiner l'état des différentes lignes (modifiée, supprimée, insérée) pour envoyer les modifications correspondantes vers la base de données :
using System;
using System.Data;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var Data= new App.Data();
            var adapter=Data.GetAdapter("SELECT [Category ID],[Category Name] FROM Categories ORDER BY [Category Name]");
            var table = new DataTable();
            adapter.Fill(table);
            // Déconnecté
            foreach(DataRow row in table.Rows)
            {
                var value = (string)row["Category Name"];
                value=value.EndsWith("z")?value.Substring(0,value.Length-1):value+"z";
                row["Category Name"] = value;
                Console.WriteLine(value);
            }
            // Exécuter toutes les mises à jour
            adapter.Update(table);
            Console.ReadKey();
        }
    }
}>
Dans le mode déconnecté comme connecté, on note l'utilisation d'objets spécifiques au transfert de données entre la base et l'application, qu'il s'agisse d'un "Reader", "DataTable" ou "DataSet". Même si les "DataSet typés" permettent d'exposer ces données d'une façon plus fortement typée, cela reste une solution hybride.

L'évolution ultime de ADO.NET, Entity Framework, a pour but de résoudre cette friction en exposant les données de la base sous forme d'objets .NET tout à fait ordinaires et c'est ce que nous allons voir maintenant.

mardi 26 juin 2012

ADO.NET - Mode connecté

Une application "Console" va illustrer les principes de base d'une application ADO.NET en mode connecté. Dans cette solution nous allons d'abord ajouter le fichier C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Samples\Northwind.sdf. SQL Server Compact est une base de données orientée fichier, installable facilement sous forme d'une DLL et idéale pour les applications de bureau qui nécessitent une base de données locale. Le fichier App.config ajouté également à la solution, va alors indiquer les informations de connection à ce fichier base de données :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Data" connectionString="Data Source=Northwind.sdf" providerName="System.Data.SqlServerCe.3.5"/>
  </connectionStrings>
</configuration>
Bien que la plupart des exemples simples ne montrent pas cette approche, pour faciliter le plus possible une adaptation à d'autres bases de données, la connection sera créée en utilisant une "fabrique" qui va permettre de créer une connection dont le type dépend de la valeur de l'attribut providerName présente dans le fichier de configuration ci-dessus. On retrouvera donc dans le code suivant :
  • Le constructeur Data initialise un accès au fichier de configuration et la fabrique correspondant à ce fournisseur.
  • La méthode GetConnection initialise la connection à la base de données.
  • La méthode GetCommand s'appuie sur cette méthode pour initialiser une commande SQL paramétrée.
  • La méthode ExecuteReader s'appuie sur la méthode précédente pour initialiser le "lecteur" qui permettra de lire les données en provenance de la base
ce qui donne donc au final le code suivant :
using System;
using System.Configuration;
using System.Data.Common;
namespace ConsoleApplication1.App
{
    class Data
    {
        private const string ConnectionStringName="Data";
        private ConnectionStringSettings connectionSettings;
        private DbProviderFactory factory;
        public Data()
        {
            // Lecture du fichier de configuration
            connectionSettings=System.Configuration.ConfigurationManager.ConnectionStrings[ConnectionStringName];
            // "Fabrique" pour le fournisseur indiqué
            factory = DbProviderFactories.GetFactory(connectionSettings.ProviderName);
        }
        private DbConnection GetConnection()
        {
            // Créer et ouvrir une connexion
            DbConnection cnn = factory.CreateConnection();
            cnn.ConnectionString = connectionSettings.ConnectionString;
            cnn.Open();
            return cnn;
        }
        public DbCommand GetCommand(string commandText,params object[] values)
        {
            // Créer une commande paramétrée
            DbConnection cnn = GetConnection();
            DbCommand cmd = cnn.CreateCommand();
            cmd.CommandText = commandText;
            for (int i = 0; i < values.Length; i++)
            {
                DbParameter prm = cmd.CreateParameter();
                prm.ParameterName = i.ToString();
                prm.Value = values[i];
                cmd.Parameters.Add(prm);
            }
            return cmd;
        }
        public DbDataReader ExecuteReader(string commandText,params object[] values)
        {
            // Créer un "lecteur"
            DbCommand cmd = GetCommand(commandText,values);
            DbDataReader dataReader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
            return dataReader;
        }
    }
}
Cette classe peut alors être utilisée par le code principal qui modifie le libellé en ajoutant ou supprimant une lettre "z" finale :
using System;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var Data= new App.Data();
            var cmd = Data.GetCommand("UPDATE Categories SET [Category Name]=@0 WHERE [Category ID]=@1", "", 0);
            var rdr = Data.ExecuteReader("SELECT [Category ID],[Category Name] FROM Categories ORDER BY [Category Name]");
            while (rdr.Read())
            {
                var value=rdr.GetString(1);
                value=value.EndsWith("z")?value.Substring(0,value.Length-1):value+"z";
                cmd.Parameters[0].Value = value;
                cmd.Parameters[1].Value = rdr.GetInt32(0);
                Console.WriteLine(rdr.GetString(1)+", "+cmd.ExecuteNonQuery()+", "+value);
            }
            rdr.Close();
            cmd.Dispose();
            Console.ReadKey();
        }
    }
}
Cet exemple permet de voir rapidement les bases de l'utilisation du mode connecté qui permet de "consommer" immédiatement les données en provenance de la base. Nous allons voir maintenant rapidement le mode déconnecté.

Windows 8

Ce mois ci a vu plusieurs annonces importantes :

  • 1er juin 2012 : "Windows 8 Release Preview" est disponible...
  • 18 juin 2012 : Microsoft commercialisera ses propres tablettes Windows 8 "Surface".
  • 21-22 juin 2012 : Windows Phone Summit
    • Windows Phone 8 ne tournera malheureusement pas sur le matériel Windows Phone 7.x. Windows Phone 7.8 apportera le nouvel écran de démarrage de Windows Phone 8.
    • Shared Windows Core : Windows Phone 8 partage une partie de son code avec Windows.
    • Les applications Windows Phone 7 fonctionnent malgré un changement de résolution.
    • Support des cartes SD.
    • Internet Explorer 10.
    • Plateforme commune DirectX en code natif pour Windows 8 et Windows Phone 8
    • Transfert sans contact (NFC - Near Field Communication)
    • Porte-monnaie électronique notamment avec Orange
    • Cartographie Nokia
    • Fonctionalités d'administration pour les entreprises (dont notamment le déploiement d'applications métier)
    • Nouvel écran de démarrage

vendredi 1 juin 2012

Bienvenue

Introduction

Des informations détaillées sur les différentes technologies proposées par Microsoft, sont déjà largement disponibles sur le web, soit via l'incontournable site "Microsoft Developper Network" (MSDN), soit via de nombreux sites ou blogs communautaires. Par contre, la palette des technologies de développement s'élargie sans cesse et il reste sans doute assez difficile pour un développeur, de suivre les progrès des différentes technologiques disponibles, ou à fortiori pour un débutant, d'acquérir rapidement une vue d'ensemble de ces mêmes technologies. Dans ce blog, je vais donc plutôt essayer d'aborder progressivement, de façon structurée, les différentes étapes de réalisation d'une application et donner un panorama des solutions techniques disponibles pour traiter chacune de ces étapes.

Plan du site

  • Bonnes pratiques
  • Accès aux données
    • ADO.NET
    • Entity Framework
  • Technologies du web
    • HTML
    • CSS
    • JavaScript
    • HTTP
    • ASP.NET
      • Dynamic Data
      • MVC
      • Web Forms
      • Silverlight (XAML)
  • Clients Windows
    • Windows Forms
    • Windows Presentation Foundation (XAML)
    • Metro (XAML)