You mention "EF auto generated classes", which suggest you're using a DB-first approach. But you mentioned in the comments that you're open to anything, so I want to suggest switching to a Code First approach.
Robert Harvey's answer isn't wrong, I simply want to offer another viable solution to the problem.
Note: I don't know much about EF Core specifically, my answer uses EF but I assume that there aren't too many differences between the two.
EF has two approaches:
- Database First - EF generates C# based on an existing database
- Code First - EF generates a databased based on existing C# classes.
So the idea is simple: create your classes, and then tell EF that you want it to create the database accordingly. This gives you direct control over your classes and where you store them.
A basic example
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public MyContext() : base("MyConnectionStringName")
{
}
public DbSet<User> Users { get; set; }
}
This is enough information for EF to generate a database for you. It will generate a table for every DbSet<T>
you specify, and it will generate that table's columns based on the class definition of T
.
EF will make some assumptions (e.g. that a property named Id
is intended to be the PK of your table). You can change these default behaviors; more on that further in the answer.
There is a lot more information on this subject than I can provide in the answer. Plenty of tutorials and guides exist online (example), I suggest you look into these to learn about the more detailed configuration options that EF puts at your disposal.
However, there is one thing I still want to point out. When you want to adjust your db columns, e.g. putting a max length on a certain string property, there are two ways of doing so.
Firstly, and most commonly found online, you can use attributes on your class:
public class User
{
public int Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
}
These attributes are mostly provided by EF. Some others (e.g. [NotMapped]
) are not provided by EF but EF does observe and respond to them (in the case of [NotMapped]
, the property will not generate a table column).
As you may suspect, many attributes are available.
But there is a second way, using the Fluent API. This will initially feel more contrived, but it's actually the better option when you want to take a DDD approach. Instead of decorating your entity with attributes, you instead "register" these configurations on the context itself:
public class MyContext : DbContext
{
public MyContext() : base("MyConnectionStringName")
{
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Property(p => p.Name).HasMaxLength(50);
}
}
As far as I'm aware, every attribute has an equivalent Fluent API alternative.
But why use Fluent for DDD?
The reason I want to suggest this is because the attributes require you to have access to the EF dependency in your class definition, which means that your entity will be defined in the data layer.
But you don't want that. You want to have a separate domain layer where your classes are defined and then consumed by the data layer. By using the Fluent API, you can ensure that this is what happens. The MyContext
class is already required to be part of the data layer (since it derives from EF's DbContext
), so it makes more sense to have it also take care of the EF-specific configuration of the table columns.
DataContext
object is a unit of work implementation. Treat it accordingly.