[Entity Framework] Code First – Convention and Configurations

When you use EF designer, an edmx file contains all necessary information about how the model classes (conceptual model) and the database (store model) are structured. Also it has mappings (mapping model) between them.

Code First does not have this kind of luxury. Rather it is highly dependent on pre-built convention. If you follow the convention, it will work like magic.  If you want more than this, you need to configure the model, so that it can work as you want.

 

1. Configuring Code First Models

There are 2 ways to configure model classes:

  • Data Annotations
  • Code First Fluent API

 

2. Data Annotations

Data Annotations are attributes that you can apply directly to the class or properties.

You can find these attributes in the “System.ComponentModel.DataAnnotations” namespace. To use this namespace you need to refer to “System.ComponentModel.DataAnnotations.dll“.

public class Category
{
  [Required]
  public string Name { get; set; }
}

The “Required” annotation does two things: checks the name is not empty and also affects the database column (maps it to a non-null column).

 

3. Fluent API

Some developers like annotations but some don’t. Annotations are concise and easy to use but some think they are annoying and hard to change when classes have many properties.

For developers who want all configurations to stay in one place (rather than class by class across the whole project), there is Fluent API.

You can start with an entity and apply settings by calling methods in chain.

 

4. Where to Put Fluent API

The “OnModelCreating” event of “DBContext” is called when the model for a derived context has been initialized. You need to override this method to use Fluent API.

  • protected virtual void OnModelCreating(DbModelBuilder modelBuilder)

 

5. DbModelBuilder Class

System.Data.Entity.DbModelBuilder” is used to map CLR classes to a database schema.

DbModelBuilder” has the “Entity()” method to register an entity type as part of the model and returns an object that can be used to configure the entity.

public virtual EntityTypeConfiguration<TEntityType> Entity()
  where TEntityType : class

 

6. EntityTypeConfiguration Class

System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<TEntityType>” allows configuration to be performed for an entity type in a model. You can use this class to configure the table-level settings.  To configure columns (properties in a class), you can use the “Property()” method to get the property configuration object.

public PrimitivePropertyConfiguration Property<T>(Expression<Func<TStructuralType, T>> propertyExpression);
public void ToTable(string tableName);
public EntityTypeConfiguration<TEntityType> HasKey<TKey>(Expression<Func<TEntityType, TKey>> keyExpression);

 

7. PrimitivePropertyConfiguration Class

System.Data.Entity.ModelConfiguration.Configuration.PrimitivePropertyConfiguration” and its subclasses “DateTimePropertyConfiguration“, “DecimalPropertyConfiguration“, and “LengthPropertyConfiguration” are used to configure a property of an entity type. “PrimitivePropertyConfiguration” provides common configuration options.

public PrimitivePropertyConfiguration HasColumnName(string columnName);
public PrimitivePropertyConfiguration HasColumnType(string columnType);
public PrimitivePropertyConfiguration IsOptional();
public PrimitivePropertyConfiguration IsRequired();

As you can see, the configuration methods return the same instance of “PrimitivePropertyConfiguration” so that multiple calls can be chained.

public class ProductContext : DbContext
{
  public DbSet<Category> Categories { get; set; }
  public DbSet<Product> Products { get; set; }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Entity().Property(c => c.Name).IsRequired();
  }
}

 

8. Grouping Configurations

Configuring all classes (tables) and properties (columns) in a single method “OnModelCreating()” will be overwhelming.

The better way is to group the configurations per entity (a table or a class) and add those groups to the “DbModelBuilder” object.

  • Create a subclass of “EntityTypeConfiguration” per entity
  • In the constructor, add configurations
  • Add the instance of “EntityTypeConfiguration” to the “Configurations” property of the “DbModelBuilder” class
public virtual ConfigurationRegistrar Configurations { get; }

The “System.Data.Entity.ModelConfiguration.Configuration.ConfigurationRegistrar” class provides the “Add()” method.

public virtual ConfigurationRegistrar Add(EntityTypeConfiguration entityTypeConfiguration);

The following example shows how to configure 2 entities.

public class CategoryConfiguration : EntityTypeConfiguration<Category>
{
  public CategoryConfiguration()
  {
    HasKey(c => c.Id).ToTable("MyCategories");
    Property(c => c.Name).IsRequired();
  }
}
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
  public ProductConfiguration()
  {
    HasKey(p => p.Id).ToTable("MyProdutcs");
    Property(p => p.Name).IsRequired();
    Property(p => p.Description).HasMaxLength(256);
    Property(p => p.Image).HasColumnType("image");
  }
}

public class ProductContext : DbContext
{
  public DbSet<Category> Categories { get; set; }
  public DbSet<Product> Products { get; set; }

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s