Faire du Reverse Engineering avec l’outil EF Power Tools

8 décembre 2012 1 commentaire

L’approche Code First est une des 3 approches qu’offre l’ORM de Microsoft : Entity Framework. Cette approche permet au développeur de coder lui-même les classes qui doivent être mappées aux tables de la base de données en suivant l’une des conditions ou un mix de celles-ci :

  • respecter les conventions de l’approche Code First par exemple toute propriété de nom « Id » définie dans une classe sera par défaut considérée comme un champ représentant la clef primaire de la table à laquelle sera mappée cette classe
  • utiliser les annotations : ce sont des attributs .Net se trouvant dans l’espace de nom System.Model.Component.DataAnnotations. Par exemple : Table, Required, Key etc…
  • utiliser l’API FLuent : pour cela vous devez redéfinir la méthode OnModelCreating de votre contexte (la classe dérivant de DbContext).

Quelle que soit la manière utilisée pour faire du Code First bah … comme son nom l’indique vous coderez vous-même les classes à mapper et par la suite vous allez générer votre base de données 🙂 Mais bon il se peut que vous disposez déjà d’une base de données existante avec plus de 100 tables mais vous voulez quand même utiliser cette approche et vous avez la flemme comme moi de vous taper les 100 classes (ou moins s’il y a des tables qui font office uniquement de tables de jointures) qui doivent correspondre à chaque table existante.

Pas d’inquiétudes, deux outils de reverse enginering vous permettent de générer les classes pour le Code First à partir d’une base de données existante :

Utiliser Entity Framework Power Tools
Entity Framework Power Tools est une extension que vous pouvez télécharger via la galerie Visual Studio ou via le gestionnaire d’extensions de Visual Studio.
Une fois l’extension installée, dans le menu contextuel du projet sur lequel vous voulez générer les classes vous verrez le menu Entity Framework avec eux sous menus :
Menus EF Power Tools

  • Reverse Engineer Code First : vous permet de générer du Code First à partir de la base de données existante
  • Customize Reverse Engineer Templates : vous permet de personnaliser le template par défaut utilisé pour la génération des classes ce qui n’est pas l’objet de ce billet.

En choisissant le sous-menu Reverse Engineer Code First, vous aurez une boîte de dialogue vous invitant à choisir la base de données existante. Vous remarquerez au passage que l’outil ne permet pas de choisir les tables avec lesquelles on aimerait travailler. ça aurait été un plus non négligeable 😉
Après validation, si vous avez utilisé , comme moi, la base de données exemple AdventureWorksLT2008 vous aurez la structure suivante :

  • Un dossier Model à la racine de votre projet contenant les classes générées et le contexte (la classe dérivant de DBContext)
  • un sous-dossier Mapping du dossier Model contenant les configurations (l’outil utilise Fluent API pour la configuration des tables, relations entre elle, clefs etc…)

Le contexte porte par défaut le nom de notre base de données et le contenu est le suivant :

public class AdventureWorksLT2008Context : DbContext
    {
        static AdventureWorksLT2008Context()
        {
            Database.SetInitializer<AdventureWorksLT2008Context>(null);
        }

		public AdventureWorksLT2008Context()
			: base("Name=AdventureWorksLT2008Context")
		{
		}

        public DbSet<BuildVersion> BuildVersions { get; set; }
        public DbSet<ErrorLog> ErrorLogs { get; set; }
        public DbSet<Address> Addresses { get; set; }
        public DbSet<Customer> Customers { get; set; }
        public DbSet<CustomerAddress> CustomerAddresses { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<ProductCategory> ProductCategories { get; set; }
        public DbSet<ProductDescription> ProductDescriptions { get; set; }
        public DbSet<ProductModel> ProductModels { get; set; }
        public DbSet<ProductModelProductDescription> ProductModelProductDescriptions { get; set; }
        public DbSet<SalesOrderDetail> SalesOrderDetails { get; set; }
        public DbSet<SalesOrderHeader> SalesOrderHeaders { get; set; }
        public DbSet<vGetAllCategory> vGetAllCategories { get; set; }
        public DbSet<vProductAndDescription> vProductAndDescriptions { get; set; }
        public DbSet<vProductModelCatalogDescription> vProductModelCatalogDescriptions { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new BuildVersionMap());
            modelBuilder.Configurations.Add(new ErrorLogMap());
            modelBuilder.Configurations.Add(new AddressMap());
            modelBuilder.Configurations.Add(new CustomerMap());
            modelBuilder.Configurations.Add(new CustomerAddressMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new ProductCategoryMap());
            modelBuilder.Configurations.Add(new ProductDescriptionMap());
            modelBuilder.Configurations.Add(new ProductModelMap());
            modelBuilder.Configurations.Add(new ProductModelProductDescriptionMap());
            modelBuilder.Configurations.Add(new SalesOrderDetailMap());
            modelBuilder.Configurations.Add(new SalesOrderHeaderMap());
            modelBuilder.Configurations.Add(new vGetAllCategoryMap());
            modelBuilder.Configurations.Add(new vProductAndDescriptionMap());
            modelBuilder.Configurations.Add(new vProductModelCatalogDescriptionMap());
        }
    }

Pour chaque table, deux classes sont en effet générées : la classe mappée à notre table et une classe ayant pour but de contenir la configuration Fluent API de la classe concernée. Exemple pour la classe Product, sa configuration se trouvera dans la classe ProductMap dans le sous-dossier Mapping.

Le contenu de la classe Product :

public class Product
    {
        public Product()
        {
            this.SalesOrderDetails = new List<SalesOrderDetail>();
        }

        public int ProductID { get; set; }
        public string Name { get; set; }
        public string ProductNumber { get; set; }
        public string Color { get; set; }
        public decimal StandardCost { get; set; }
        public decimal ListPrice { get; set; }
        public string Size { get; set; }
        public Nullable<decimal> Weight { get; set; }
        public Nullable<int> ProductCategoryID { get; set; }
        public Nullable<int> ProductModelID { get; set; }
        public System.DateTime SellStartDate { get; set; }
        public Nullable<System.DateTime> SellEndDate { get; set; }
        public Nullable<System.DateTime> DiscontinuedDate { get; set; }
        public byte[] ThumbNailPhoto { get; set; }
        public string ThumbnailPhotoFileName { get; set; }
        public System.Guid rowguid { get; set; }
        public System.DateTime ModifiedDate { get; set; }
        public virtual ProductCategory ProductCategory { get; set; }
        public virtual ProductModel ProductModel { get; set; }
        public virtual ICollection<SalesOrderDetail> SalesOrderDetails { get; set; }
    }

Le contenu de la classe ProductMap

public class ProductMap : EntityTypeConfiguration<Product>
    {
        public ProductMap()
        {
            // Primary Key
            this.HasKey(t => t.ProductID);

            // Properties
            this.Property(t => t.Name)
                .IsRequired()
                .HasMaxLength(50);

            this.Property(t => t.ProductNumber)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.Color)
                .HasMaxLength(15);

            this.Property(t => t.Size)
                .HasMaxLength(5);

            this.Property(t => t.ThumbnailPhotoFileName)
                .HasMaxLength(50);

            // Table & Column Mappings
            this.ToTable("Product", "SalesLT");
            this.Property(t => t.ProductID).HasColumnName("ProductID");
            this.Property(t => t.Name).HasColumnName("Name");
            this.Property(t => t.ProductNumber).HasColumnName("ProductNumber");
            this.Property(t => t.Color).HasColumnName("Color");
            this.Property(t => t.StandardCost).HasColumnName("StandardCost");
            this.Property(t => t.ListPrice).HasColumnName("ListPrice");
            this.Property(t => t.Size).HasColumnName("Size");
            this.Property(t => t.Weight).HasColumnName("Weight");
            this.Property(t => t.ProductCategoryID).HasColumnName("ProductCategoryID");
            this.Property(t => t.ProductModelID).HasColumnName("ProductModelID");
            this.Property(t => t.SellStartDate).HasColumnName("SellStartDate");
            this.Property(t => t.SellEndDate).HasColumnName("SellEndDate");
            this.Property(t => t.DiscontinuedDate).HasColumnName("DiscontinuedDate");
            this.Property(t => t.ThumbNailPhoto).HasColumnName("ThumbNailPhoto");
            this.Property(t => t.ThumbnailPhotoFileName).HasColumnName("ThumbnailPhotoFileName");
            this.Property(t => t.rowguid).HasColumnName("rowguid");
            this.Property(t => t.ModifiedDate).HasColumnName("ModifiedDate");

            // Relationships
            this.HasOptional(t => t.ProductCategory)
                .WithMany(t => t.Products)
                .HasForeignKey(d => d.ProductCategoryID);
            this.HasOptional(t => t.ProductModel)
                .WithMany(t => t.Products)
                .HasForeignKey(d => d.ProductModelID);

        }
    }

Vous pouvez commencez à utiliser les classes et votre contexte pour toutes opérations qu’offre l’ORM Entity Framework sans problèmes cependant il y a quelques petites fonctionnalités intéressantes lorsque vous faîtes un clic-droit sur le fichier contenant la classe dérivant de DbContext.
EF Power Tools Context Menu

  • View Entity Data Model (read only) : vous permet de visualiser le modèle EDM en lecture seule dans le designer. Le modèle est tout simplement l’EDMX que vous auriez si vous aviez choisi l’approche Model First ou Database First. Le fait de visualiser l’EDM est utile parce que cela vous permet d’avoir une représentation graphique des entités et des relations qu’il y a entre elles.
    EF Power Tools EDM
  • View Entity Data Model XML : vous permet de voir le XML du modèle EDM. EF Power Tools XML
  • View Entity Data Model DDL SQL : vous permet de voir le script SQL qui sera généré à partir de l’EDM pour créer les objets de votre base de données.
  • Generate Views : vous permet de générer des vues pré-compilées. Pour infos l’utilisation des vues pré-compilées permet au runtime d’Entity Framework d’améliorer les performances de démarrage. Utilisée, cette fonctionnalité ajoute le fichier [Le nom de vote contexte].Views.cs dans mon cas AdventureWorksLT2008Context.Views.cs.

Voilà il s’agit d’un outil qui vous permet de gagner de beaucoup de temps si vous disposez déjà d’une base de données mais que vous passer par l’approche Code First pour utiliser l’ORM Entity Framework !

Bon code 😉

Catégories :Entity Framework, Outils

Comment étendre le binding d’un endpoint par défaut ?

D’habitude lorsque je fais un petit test en WCF, j’ai l’habitude, pour aller plus vite, de laisser mon ServiceHost créer automatiquement les endpoints dont j’aurai besoin suivant l’adresse de base que j’aurai fournie dans le fichier de configuration. C’est cool et les endpoints créés de façon automatique sont appelés en fait des default endpoints (les points de terminaison par défaut).

Sauf que j’ai voulu, lors d’un de mes tests, changer l’encodage utilisé pour les messages alors je me suis dit qu’il fallait obligatoirement passer par un endpoint créé manuellement dans le fichier de configuration et par la suite utiliser l’attribut bindingConfiguration pour lui attribuer le nom de la nouvelle configuration créée. Mais il y a deux méthodes plus simples :

  • Passer par une configuration de binding par défaut. Pour info une configuration de binding par défaut est une configuration qui n’a pas de nom. L’attribut name n’est pas renseigné donc tout endpoint dont l’attribut bindingConfiguration n’est pas défini, utilisera cette configuration associée si son binding lui correspondant. Cela est valable pour les endpoints par défaut.
  • Un peu plus complexe mais le problème avec la première méthode est que si on a déjà une configuration par défaut déjà disponible utilisée par d’autres endpoints et qu’on n’aimerait pas modifier alors cette deuxième méthode nous permet de pallier à ce problème. Pour cela il faut passer par le mappage des endpoints par défaut c’est à dire utiliser l’élément protocolMappings, dans le mappage ajouté pour le protocole http et le binding basicHttpBinding dans notre cas, j’utilse l’attribut bindingConfiguration fourni.

Voilà j’espère que ce billet vous a été utile 😉

Catégories :WCF Étiquettes :

Comment déboguer un service windows sous Visual Studio ?

Lorsque vous créez un projet de type Service Windows sous Visual Studio, vous avez remarqué que la boîte de dialogue ci-dessous s’affiche quand on essaie d’exécuter le service :

En résumé il est tout simplement impossible d’exécuter un service windows sous Visual Studio, on doit obligatoirement passer par la commande NET START pour démarrer le service après avoir préalablement installé celui-ci grâce à la commande INSTALLUTIL. Sauf que cette méthode nous empêche de faire facilement un deboguage du service Windows. En effet il faut installer le service, le démarrer, puis dans Visual Studio attacher un processus (qui sera bien sûr le processus du service windows) pour pouvoir déboguer notre code. Sans oublier qu’il va falloir aussi arrêter le service windows pour pouvoir compiler suite à quelques modifications qu’on aura effectuées dans le code du service. Bref c’est un peu lourd :D.

Il y a plus simple. on utilisera les symboles de compilation pour détecter dans quel mode nous sommes : RELEASE ou DEBUG. Si nous sommes en :

  • DEBUG : nous allons traiter notre service windows comme une simple application windows forms en affichant une boîte de dialogue indiquant que le service est démarré
  • RELEASE : le fonctionnement sera le même que quand nous avons essayé d’exécuter notre service windows sous Visual Studio. En d’autres termes, ce mode doit être utilisé en production une fois le débogage terminé

Pour notre cas d’utilisation ce sera très simple. Il s’agira ici de rendre possible l’exécution du service Windows et de déboguer un service WCF qu’il héberge. Voici les étapes à suivre :

  1. Modification du code du service windows : nous allons ajouter deux méthodes StartWCFService et StopWCFService qui ont pour tâches respectives de démarrer et d’arrêter l’écoute des requêtes entrantes WCF. La première méthode sera appelée dans la rédéfinition de la méthode OnStart et la seconde dans celle de la méthode OnStop. Ci-dessous le code :
    private ServiceHost host;
    
    public MyWindowsService() {
        InitializeComponent();
    }
    
    protected override void OnStart(string[] args) {
        this.StartWCFService();
    }
    
    protected override void OnStop() {
        this.StopWCFService();
    }
    
    public void StartWCFService() {
        this.host = new ServiceHost(typeof(IWCFService));
        this.host.Open();
    }
    
    public void StopWCFService() {
        if (this.host != null && this.host.State == CommunicationState.Opened)
            this.host.Close();
    }
    
  2. Modification du fichier Program.cs : c’est dans la méthode Main que nous allons faire la détection du mode de compilation dans lequel nous sommes. Si nous sommes en mode RELEASE, alors le service windows s’exéctute comme d’habitude et forcément on aura la fameuse boîte de dialogue si on essaie de l’exécuter sous Visual Studio avec ce mode. Si par contre, nous sommes en mode DEBUG, alors c’est très simple on fait appel directement à la méthode StartWCFService de l’instance de notre service windows (donc la méthode OnStart ne sera pas appelée) puis on affiche une boîte de dialogue pour en informer l’utilisateur. si ce dernier ferme la boîte de dialogue alors la méthode StopWCFService est appelée pour arrêter le service WCF (donc la méthode OnStop du service Windows ne sera pas appelée).
    Ci-dessous le code de la méthode Main :

    static void Main() {
    
        MyWindowsService service = new MyWindowsService();
    
    #if DEBUG
        service.StartWCFService();
        MessageBox.Show("Le service a démarré...");
        service.StopWCFService();
    #else
    
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
    	{
    		service
    	};
        ServiceBase.Run(ServicesToRun);
    #endif
    }
    

J’espère que ce billet vous a été utile 😉

Catégories :.Net, WCF Étiquettes : , , ,

ExtendedWebClient : une extension de la classe WebClient

[EDIT]

La méthode Abort ajoutée précédemment ne sert pas à grand chose 😀 Apparemment je suis passé à côté de l’existence de la méthode CancelAsync.

Récemment j’ai voulu augmenter le Timeout d’exécution des méthodes (téléchargement et téléversement) de la classe WebClient. Ce besoin est venu du fait que je travaillais avec une requête qui prenait pas de mal de temps parce que le serveur devait effectuer différentes tâches qui prenaient pas mal de temps or la classe WebClient telle que définie n’expose pas et donc ne permet la modification de la propriété Timeout alors qu’elle utilise bien en interne un objet de Type WebRequest qui lui expose une propriété Timeout qui peut être modifiée.

J’ai trouvé une solution à ce problème en créant une classe ExtendedWebClient qui dérive de la classe de base WebClient et par la suite j’ai redéfini la méthode GetWebRequest (qui est chargée de créer l’objet WebRequest qui sera utilisé par la suite par notre WebClient). Dans la redéfinition j’attribue la valeur de mon Timeout à la propriété Timeout de l’instance WebRequest créée . Voilà.

Suite à cette discussion postée ici, j’ai par la suite ajouté une propriété CookieContainer pour de la gestion des cookies (uniquement pour les requêtes HTTP/S).

Ci-dessous le code complet de classe ExtendedWebClient :

/// <summary>
/// Classe dérivée de la classe WebClient
/// </summary>
public class ExtendedWebClient : WebClient
{
    #region Fields

    private WebRequest request;

    #endregion // Fields

    #region Properties

    /// <summary>
    /// Obtient la durée du timeout
    /// </summary>
    public int Timeout { get; private set; }

    /// <summary>
    /// Obtient le container de cookies
    /// </summary>
    public CookieContainer CookieContainer { get; private set; }

    #endregion // Properties

    #region  Consctuctors

    /// <summary>
    /// Constructeur de la classe ExtendedWebClient
    /// </summary>
    /// <param name="timeout">Durée en millisecondes </param>
    public ExtendedWebClient(int timeout)
    {
        this.Timeout = timeout;
        this.CookieContainer = new CookieContainer();
    }

    #endregion // Constructors

    #region Overriden methods

    protected override WebRequest GetWebRequest(Uri address)
    {
        this.request = base.GetWebRequest(address);

        // S'il s'agit d'une instance de type HttpWebRequest alors on instancie le container de cookie
        if (this.request is HttpWebRequest)
        {
            (this.request as HttpWebRequest).CookieContainer = this.CookieContainer;
        }

        // Si this.Timeout <= 0 alors la valeur par défaut qui sera utilisée.
        this.request.Timeout = this.Timeout <= 0 ? this.request.Timeout : this.Timeout;
        return this.request;
    }

    #endregion // Overriden methods
}

J’espère que cette classe vous sera utilse :).

Catégories :.Net, Astuces

Astuce : [WCF] Exposer son service WCF sous IIS sans passer par la directive ServiceHost

Lorsqu’on veut héberger son service WCF sous IIS nous utilisons la plupart du temps le template de fichier sous Visual Studio portant l’extension *.svc. Dans ce template nous devons effectuer un certain nombre de paramétrages pour que IIS puisse héberger le service et l’activer lors de la réception de la première requête client en utilisant la directive ServiceHost comme dans l’exemple ci-dessous :

<%@ ServiceHost Language= »C# » Debug= »true »  Service= »WebApplicationTest.MyService » CodeBehind= »MyService.svc.cs » %>

Une nouveauté venue avec la sortie de WCF 4.0 permet de ne pas utiliser le fichier template *.svc et par conséquent de ne pas utiliser la directive ServiceHost, mais de tout paramétrer via le fichier de configuration web.config comme suit :

<system.serviceModel>
  <serviceHostingEnvironment>
    <serviceActivations>
      <add service="WebApplicationTest.MyService" relativeAddress="MyService.svc" />
    </serviceActivations>
  </serviceHostingEnvironment>
</system.serviceModel>

Dans l’exemple ci-dessus, nous venons d’héberger notre service MyService sans passer par la directive ServiceHost du traditionnel fichier *.svc. Les seules choses dont il faut faire attention lors de la configuration sont :

  1. la présence de l’espace de noms de notre service dans l’attribut service.
  2. la présence obligatoire de l’extension .svc dans la valeur de l’attribut relativeAddress

Notez que vous pouvez aussi configurer le factory à utiliser via l’attribut factory.

Catégories :Astuces, WCF Étiquettes :

Livre gratuit pour développer sous Windows Phone avec Silverlight

Le site WindowsPhoneGeek a publié gratuitement un livre en anglais parlant du développement Silverlight pour Windows Phone. Il s’agit d’un e-book de 22 chapitres repartis sur 246 pages parlant du SDK Windows Phone 7.1 Mango et du toolkit d’Août 2011. Le livre peut être téléchargé ici.
Bonne lecture 😉

Catégories :Windows Phone 7

Activer définitivement la « pluralisation » des noms des entités sous EF

4 septembre 2011 1 commentaire

Lorsqu’on ajoute un modèle ADO.Net Entity Framework dans son application, Visual Studio n’active pas la « pluralisation » des noms des entités. Ce comportement est celui pour la version française de Visual Studio alors qu’avec la version anglaise elle est activée par défaut.
Pour ne pas avoir à activer la « pluralisation » des noms à chaque fois qu’on essaie d’ajouter un fichier EDMX ou tout simplement lorsqu’on essaie de mettre à jour notre modèle Entity Framework, il suffit d’aller dans le menu « Outils » puis « Options.. ». Dans la boîte de dialogue qui s’affiche, dans l’arborescence qui se trouve à gauche, déplier le noeud « Outils de base de données », sélectionner l’élément « O/R Designer » et dans la partie de droite activer tout simplement la « pluralisation » en mettant sa valeur à « True » comme dans l’image ci-dessous et cliquer sur « OK ».

Configuration de la pluralisation des entités

Configuration de la pluralisation des entités

Catégories :Entity Framework Étiquettes : ,
%d blogueurs aiment cette page :