Héro du mois de juillet sur AfricaApp

J’écris de moins en moins de billets :( J’espère que la donne changera sur le courant du mois d’août :D

J’écris ce billet pour vous informer que j’ai été élu héro du mois de juillet pour le magazine AfricaApp.

J’ai surtout été choisi à la suite de la publication de mon application Windows Phone Awalé. Awalé, pour ceux qui ne connaissent pas, est la version Windows Phone du jeu de semailles le plus répandu en Afrique. Ce jeu appartient à la famille des Mancala. Il s’agit d’un jeu de stratégie qui se joue à 2 et où on distribue des graines dans des trous parfois creusés à même le sol. Plusieurs règles existent suivant les régions ou pays et celle utilisée par mon application est la règle commune aux Akan et aux Diolas.

Si vous possédez un Windows Phone alors je ne peux que vous récommander de télécharger cette app :)

Catégories:Perso

WCF 4.5 – Récupérer tout le contenu WSDL en un seul et unique fichier

La version 4.5 de WCF a introduit une nouvelle manière de récupérer le contenu des métadonnées. Auparavant si vous avez activé l’exposition des métadonnées vous y accéder en utilisant une url du genre http://addresipdemonservice?wsdl. Le hic avec cette méthode est que cela posait des soucis d’interopérabilités si on était amené à travailler avec des clients pas développés avec la technologie .Net. En effet ces clients n’arrivent pas à lire contenu du wsdl. Pourquoi ? Il suffit d’observer le contenu du wsdl, vous verrez que tout n’y est pas par exemple les métadonnées sur les types utilisés par votre service. Les métadonnées concernant les types sont en effet importées via la balise xsd:import et l’attribut schemaLocation spécifie où chercher ces données. Bref ce procédé n’est pas compréhensible par les clients faits en Java, Php etc…

Pour récupérer toutes les métadonnées dans un seul et unique fichier vous pouvez au lieu de mettre ?wsdl utiliser ?singleWsdl à la place donc http://addresipdemonservice?singlewsdl pour assurer l’interopérabilité.

J’espère que ce post vous a servi :)

Catégories:WCF Tags:

Code First : Entity Framework Reverse POCO Generator

9 décembre 2012 1 commentaire

Dans un précédent article j’ai expliqué comment faire du reverse engeneering Code First en utilisant l’extension Visual Studio Entity Framework Power Tools. Dans ce billet je parlerai d’une autre extension qui permet de faire la même chose : Entity Framework Reverse POCO Generator.

Vous pouvez installer cette extension à partir de la galerie ou via le gestionnaire d’extensions de Visual Studio. Une fois installé vous aurez un template de fichier qui sera ajouté à la liste de templates déjà existants.

Pour utiliser la fonctionnalité de reverse engeneering de cet outil, créer d’abords votre projet de préférence une bibliothèques de classes. Après vous faîtes un clic-droit sur le projet puis "Ajouter un élément". Dans la boîte de dialogue qui apparaît choisissez le template suivant :
EF Generator
Donnez un nom à votre élément. Dans mon cas vu que j’utilise la base de données AdventureWorksLT2008 j’ai mis AdventureWorksLT.tt. Après validation, vous aurez 3 fichiers de template T4 (fichiers d’extensions .tt) qui seront ajoutés à votre projet :
EF Generator T4

Le seul fichier T4 que vous aurez à toucher et modifier est AdventureWorksLT.tt. Ouvrez ce fichier et suivre les étapes suivantes :

  • Configuration de la chaine de connexion : en début de fichier trouvez le paramètre ConnectionStringName et mettez le nom de la chaîne de connexion à utiliser. Notez que cette chaîne de connexion doit exister dans le fichier de configuration App.config.
    ConnectionStringName = "AdventureWorksLT2008Context";
  • Configuration du nom du contexte : trouvez le paramètre DbContextName et attribuez lui le nom que vous voudriez avoir comme nom de contexte (la clasese dérivant de DbContext)
    DbContextName = "AdventureWorksContext";
  • Rendre vos classes partielles : trouvez le paramètre MakeClassesPartial et attribuez lui la valeur true. Cela à pour but de rendre les classes partielles par défaut elles ne le sont pas.
    MakeClassesPartial = true;

La sauvegarde du fichier vous génère automatiquement le fichier AdventureWorksLT.cs. Ce fichier continent toutes les classes pour utiliser l’approche Code First. Dans ce fichier ces classes sont groupées en 3 catégories. D’abords nous avons le contexte de la base de données (AdventureWorksLT), suivi de toutes les classes POCO et enfin viennent les classes de configuration de nos classes POCO. Les classes de configuration ont pour but, à travers l’API FLuent, de définir les tables, colonnes, clefs et relations entre ces classes et tables existantes dans votre base de données.

Cette extension n’effectue qu’une tâche : faire du reverse engeneering alors que Entity Framework Power Tools en offre d’autres comme visualiser le modèle EDM, le XML de ce dernier ou générer des vues pré-compilées. Mais il faut noter qu’EF Power Tools ne permet pas de filtrer ou exclure les tables qu’on veut ou qu’on ne veut pas utiliser dans notre projet alors Entity Framework Reverse POCO Generator permet de le faire à travers des expressions régulières que vous devez configurer en modifiant les paramètres TableFilterExclude et TableFilterInclude du template T4 AdventureWorksLT.tt.

Bon code ;)

Catégories:Entity Framework Tags:

Des bases de données exemples : AdventureWorks !

Si comme moi, vous lisez pas mal de tutoriels vous verrez que pas mal de ces tutoriels (du moins ceux portant sur la plateforme .Net et utilisant une base de données SQL Server) utilisent souvent les bases de données AdeventureWorks comme exemple. Par défaut ces bases de données ne sont pas fournies avec le setup de SQL Server 2008, 2008 R2 ou 2012. Vous pouvez les récupérer sur Codeplex sur cette page :)

Catégories:.Net, Outils

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 Tags:

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 Tags:, , ,
Suivre

Recevez les nouvelles publications par mail.

%d bloggers like this: