OPTEN, das einzige Umbraco-zertifizierte Unternehmen der Schweiz

Configuring Entity Framework with Fluent Api

Introduction

When working with Entity Framework it is possible to start with plain classes (POCOs), and let entity framework automatically generate the database, or to start with a database and let entity framework automatically generate the classes. In either case control is relinquished to Entity Framework which could result in strange database tables or unneeded classes.

There is a third way whereby control can be maintained, that is to write the mapping configuration files for each object by hand. This is not as onerous as it first sounds as Entity Framework provides a clear and understandable language to achieve this and by utilising conventions only a few properties must be explicitly mapped.

Laying the groundwork

A separate configuration can be created for each class, which makes the mapping more manageable, then each configuration file must be added to the DbModelBuilder, in a class which inherits from DbContext. The following code sample shows how this works:

public class RepositoryContext : DbContext
{
    public RepositoryContext() : base(“databaseName”)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ProductConfiguration());
        modelBuilder.Configurations.Add(new CustomerConfiguration());
        modelBuilder.Configurations.Add(new OrderConfiguration());
    }

Each configuration must inherit from the EntityTypeConfiguration object and pass in the class type which is being mapped. The code below shows this:

public class ProductConfiguration : EntityTypeConfiguration<Product>
{
    public ProductConfiguration() : base()
    {
        // configuration details go here
    }
}

The classes which are used in this example are shown below:

public class Product
{
    public int IdProduct { get; set; }
    public string Name { get; set; }

    public decimal Price { get; set; }
}

public class Customer
{
    public int IdCustomer { get; set; }

    public string FirstName { get; set; }
    public string Surname { get; set; }
    public string FullName {
        get
        {
            return string.Format("{0} {1}", FirstName, Surname);
        }
    }

    public virtual ICollection<Order> Orders { get; set; }
}

public class Order
{
    public virtual ICollection<Product> Products { get; set; }
}

The important thing to notice about these classes is that the ICollections of orders and products are virtual, this means that these collections will be implicitly lazily loaded by entity framework. If they were not virtual, they will always be null, unless explicitly loaded.

Configuring Properties

The configuration for the product class is shown below:

public class ProductConfiguration : EntityTypeConfiguration<Product>
{
    public ProductConfiguration() : base()
    {
        // IdProduct is the primary key
        HasKey(p => p.IdProduct);

        Property(p => p.IdProduct).
                HasColumnName("IdProduct").
                HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
                IsRequired();

        Property(p => p.Price)
                .HasPrecision(18,2);

        ToTable("dbo.Product");
    }
}

 

The HasKey method is used to specify the primary key. The HasColumnName method is useful if a property has a different name than the column in the database. The HasDatabaseGeneratedOption is used here because the IdProduct has Identity Specification set to yes in the database.  The IsRequired property is set because allow nulls is set to false in the database. It is also important to set the precision for decimals, otherwise the value can be altered when it is entered into the database. The  Name property is not needed here, because it utilises the convention, that if a property matches the name of the column in the database, then Entity Framework can handle it and map it automatically. It is also important to specify the name of the table to which this mapping corresponds, this is what the ToTable method is used for.

Configuring a one to many relationship

This shows the basics for mapping a simple class. However it is also possible to configure mapping for one to many and many to many relationships. A sample configuration with a one to many relationship is shown in the customer configuration below:

public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
    public CustomerConfiguration() : base()
    {
        HasKey(p => p.IdCustomer);
        Property(p => p.IdCustomer).
            HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
            IsRequired();

        // there is a many to one relationship between customer and order
        // one customer can have many orders
        HasMany(p => p.Orders)
            .WithOptional()
            .Map(p => p.MapKey("CustomerId"));

        // this column does not exist in the database,
        //so ignore if for the mapping.
        Ignore(p => p.FullName);

        ToTable("dbo.Customer");
    }
}

There are two new methods here. The Ignore method is very simple, it allows certain properties to be set to be ignored by the entity framework mapping, if there is no corresponding column in the database. The HasMany WithOptional methods are used when mapping a foreign key relationship. In this case the Customer class can have many Orders. The order table has a column named “CustomerId” which is the foreign key and is used in the mapping. WithOptional is used because it is possible that a customer has no orders. It is also possible to use IsRequired if there must always be at least one order per customer.

Configuring a many to many relationship

It is also possible to configure many to many relationships with Entity Framework. This is demonstrated in the configuration code for the Order object shown below:

class OrderCompletedConfiguration : EntityTypeConfiguration<OrderCompleted>
{
    public OrderCompletedConfiguration()
    {
        HasKey(p => p.IdOrderCompleted);
        Property(p => p.IdOrderCompleted)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
            .IsRequired();

        HasMany(p => p.Products)
            .WithMany()
            .Map(m =>
            {
                m.MapLeftKey("OrderId");
                m.MapRightKey("ProductId");
                m.ToTable("ProductToOrder");
            });

        ToTable("dbo.OrderCompleted");
    }
}

Here there is a many to many relationship between Product and Order, one order can have many products and one product can be in many orders. Therefore the HasMany WithMany methods are used. In the database there is another separate table named “ProductToOrder” which maps the products to the orders. This is used in the Map method. This “ProductToOrder” table has two columns, “OrderId” and “ProductId”. The MapLeftKey method is always the id of the object for which the configuration is, in this case the Order object. The MapRightKey method is the id of the foreign key, in this case the Product object.

Conclusion

With the fluent api it is possible to use Entity Framework without giving up any control. This is only an introduction, not an exhaustive guide. There is more information about configuring properties here and about configuring relationships here.


kommentieren


1 Kommentar(e):

Hari
Februar 19, 2016 09:01

That was a very well written one. So this approach of writing mapping configurations manually on our own can be used in case of both "EF Code First" and "EF DB First". Or it only fits to any specific approach..? Thanks in Advance..