Click here to Skip to main content
15,881,898 members
Articles / Web Development / ASP.NET
Tip/Trick

Entity Framework: Separate Entity Class Mapping using Fluent API

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
6 Nov 2018CPOL1 min read 13.5K   8   2
Separate entity class mapping using Fluent API

Introduction

Separating entity class mapping using Fluent API will make modification and maintenance easier for midiup or large application/project.

Background

When working with lot of Entity classes in a project, it is difficult to maintain all of the Entity configuration in DbContext's OnModelCreating method. For example:

C#
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
        : base()
    {
    }

    public DbSet<Trainees> Trainees { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);
    }
}

Now if you have hundreds of Entities, then you have to add all of the entities here which makes modification and maintenance much harder.

Moreover, if you want to override the relationship, Indexing or field property mapping, you may modify the OnModelCreating method in the following way:

C#
        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Trainee>().ToTable("Trainee");
            builder.Entity<Trainee>().HasKey(t => t.Id).Property
                (t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            builder.Entity<Trainee>().Property(t => t.TraineeNo).
                             HasMaxLength(10).IsRequired().IsUnicode();

            //HasRequired(t=>t.Batch)
            //    .WithMany(b=>b.Trainees)
            //    .HasForeignKey(t=>t.BatchId);

            builder.Entity<Trainee>().Property(t => t.Gender);
            builder.Entity<Trainee>().Property(t => t.Name).HasMaxLength(500).IsRequired();
            builder.Entity<Trainee>().Property(t => t.FatherName).HasMaxLength(500);
            builder.Entity<Trainee>().Property(t => t.MotherName).HasMaxLength(500);
            builder.Entity<Trainee>().Property(t => t.NationalId).HasMaxLength(50);

            builder.Entity<Trainee>().Property(t => t.MobilNo).HasMaxLength(50);
            builder.Entity<Trainee>().Property(t => t.PholeNo).HasMaxLength(50);
            builder.Entity<Trainee>().Property(t => t.Email).HasMaxLength(150);
            builder.Entity<Trainee>().Property(t => t.LastEducation).HasMaxLength(50);
            builder.Entity<Trainee>().Property(t => t.TraineeStatus);
            builder.Entity<Trainee>().Property(t => t.RetakeAvailable);
            builder.Entity<Trainee>().Property(t => t.Note).HasMaxLength(750);
            builder.Entity<Trainee>().Property(t => t.Deleted);
            builder.Entity<Trainee>().Property(t => t.DeleteDate);

            builder.Entity<Trainee>().HasMany(pr => pr.TraineeRoles)
                .WithMany(cr => cr.Trainees)
                .Map(m => m.ToTable("Trainee_TraineeRole_Mapping"));

            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
        }

So we can customize Entity by EntityTypeConfiguration class. But it is horrible to maintain when it crosses 10 to 20 entities.

That's why we need to separate each entity configuration in different cs class files.

Using the Code

Create a CS class named TraineeMap.cs. It will inherit EntityTypeConfiguration. For EntityTypeConfiguration, you need to use System.Data.Entity.ModelConfiguration namespace.

C#
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using TIS.Entity.Participant;

namespace TIS.Data.Mapping.Participant
{
    public class TraineeMap : EntityTypeConfiguration<Trainee>
    {
        public TraineeMap()
        {
            ToTable("Trainee");
            HasKey(t => t.Id).Property(t => t.Id).HasDatabaseGeneratedOption
                                                  (DatabaseGeneratedOption.Identity);
            Property(t => t.TraineeNo).HasMaxLength(10).IsRequired().IsUnicode();

            //HasRequired(t=>t.Batch)
            //    .WithMany(b=>b.Trainees)
            //    .HasForeignKey(t=>t.BatchId);

            Property(t => t.Gender);
            Property(t => t.Name).HasMaxLength(500).IsRequired();
            Property(t => t.FatherName).HasMaxLength(500);
            Property(t => t.MotherName).HasMaxLength(500);
            Property(t => t.NationalId).HasMaxLength(50);

            Property(t => t.MobilNo).HasMaxLength(50);
            Property(t => t.PholeNo).HasMaxLength(50);
            Property(t => t.Email).HasMaxLength(150);
            Property(t => t.LastEducation).HasMaxLength(50);
            Property(t => t.TraineeStatus);
            Property(t => t.RetakeAvailable);
            Property(t => t.Note).HasMaxLength(750);
            Property(t => t.Deleted);
            Property(t => t.DeleteDate);

            this.HasMany(pr => pr.TraineeRoles)
                .WithMany(cr => cr.Trainees)
                .Map(m => m.ToTable("Trainee_TraineeRole_Mapping"));
        }
    }
}

Now change the OnModelCreating method as follows:

C#
protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Configurations.Add(new TraineeMap());

    base.OnModelCreating(builder);
    // Customize the ASP.NET Identity model and override the defaults if needed.
    // For example, you can rename the ASP.NET Identity table names and more.
    // Add your customizations after calling base.OnModelCreating(builder);
}

Again, the problem here is that for each Entity, we have to repeat the following line:

C#
builder.Configurations.Add(new {EntityClassName}());

How can we eliminate redundancy? Reflection may help us by finding which classes are derived from EntityTypeConfiguration generic class.

C#
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
    .Where(type => !String.IsNullOrEmpty(type.Namespace))
    .Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
        type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
    foreach (var type in typesToRegister)
    {
        dynamic configurationInstance = Activator.CreateInstance(type);
        modelBuilder.Configurations.Add(configurationInstance);
    }

    base.OnModelCreating(modelBuilder);
}

Above, we are filtering from assembly and finding those classes whose type is typeof(EntityTypeConfiguration<>) and then adding to configuration.

Points of Interest

Separating each entity mapping makes modification and maintenance easier. By applying Reflection, the automation is fulfilled.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionAn easier way... Pin
Richard Deeming6-Nov-18 8:43
mveRichard Deeming6-Nov-18 8:43 
GeneralMy vote of 5 Pin
johannesnestler6-Nov-18 4:32
johannesnestler6-Nov-18 4:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.