Archive

Archive for the ‘Outils’ Category

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 🙂

Publicités
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
%d blogueurs aiment cette page :