When you are introducing an ORM to communicate with your database. You will need to know upfront where in your application your objects will be placed and where the configuration belongs. The objects are represented in the domain layer in the following drawing.
As you can see in the above drawing the domain layer is the heart of the application. It should have as few dependencies as possible. This also means that the domain layer should be storage agnostic and have no knowledge about the storage medium or ORM used. Concrete: your domain layer should not know Entity Framework.
Convention
If you just started using Entity Framework you might have noticed that it just seems to work out of the box. That's a good and a bad thing. This means that you can get started pretty fast as everything is configured via conventions. Entity Framework does this by looking at the name of properties and it will try to figure out what you are doing.
The risk of using conventions is that they are not explicit and that you have no guarantee that with the next version everything will be working exactly the same. It might be that due to a bug fix something works a little bit different. If you work with conventions you are also not able to rename any property as you have no mapping configured with the database column names. If you give a property a wrong name it will also silently fail as Entity Framework cannot figure out if you wanted it to do something.
Attributes
Working with the Domain Model is already hard enough. Developers should not be overwhelmed with irrelevant things like the configuration between the ORM and the actual database. When you are working with Domain-Driven Design you are focussing on delivering business value by automating existing processes. This is the only thing you want to focus on in the domain layer.
Another downside of using attributes is that you cannot combine multiple properties as the attribute is placed above 1 property (if property A has value X than property B cannot have value Y). On top of that your domain layer now has a vendor lock to a specific ORM and/or database technology.
EntityTypeConfiguration - Explicit configuration
The last and preferred way is to use explicit configuration. You know very well what property is mapped to what column and table in your database.
Entity Framework configurations have an order in which configurations are applied. At first there are conventions for when you do not configure anything, after that you can work with attributes but these have limitations. The final way is to use Entity Type Configurations that also overrules all the previous configurations.
You only have 1 place to look if you only use explicit configuration. If you use attributes you will always have to verify that they are not overridden.
The place to configure this is the infrastructure code that glues the domain layer and the domain layer together. This might also be the same place where your repositories are located.
public class StudentEntityConfiguration : EntityTypeConfiguration<Student>
{
public StudentEntityConfiguration()
{
ToTable("StudentInfo");
HasKey<int>(s => s.StudentKey);
Property(p => p.DateOfBirth)
.HasColumnName("DoB")
.HasColumnOrder(3)
.HasColumnType("datetime2");
Property(p => p.StudentName)
.HasMaxLength(50);
Property(p => p.Version)
.IsConcurrencyToken();
HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("StudentId");
cs.MapRightKey("CourseId");
cs.ToTable("StudentCourse");
});
}
}
Now you need to tell Entity Framework where to look for these explicit configurations. You do this by adding the configurations to the model builder.
public class SchoolDBContext: DbContext
{
public SchoolDBContext(): base()
{
}
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Moved all Student related configuration to StudentEntityConfiguration class
modelBuilder.Configurations.Add(new StudentEntityConfiguration());
}
}