Imagine that I want to add an IsDeleted colum or some auditing columns to all of my entities. I could create a base class from which all of my entities will inherit and this will solve my problem, however I cannot specify the order in which the column will be created so I will end up with all the auditing fields before the fields of my entity, which I do not want. I want them to be at the end of the table.
In the standard version of entity framework we can do this by using annotations that specify the order of the columns. However, such a thing does not exist for EF core at the moment.
I could do it with the fluent api on the OnModelCreating() method, the problem is that I only know how to do it individually for each of my entities, which means I would have to write the same code for every entity I have.
Is there any way I can do it generically for all of my entities? Some sort of for loop that iterates through all the entities registered in the DbSets on my dbcontext?
Your question title is about adding the same properties to multiple entities. However, you actually know how to achieve this (use a base type) and your actual question is how to ensure that these properties come last in the generated tables' columns.
Although column order shouldn't really matter nowadays, I'll show an alternative that you may like better than a base type and also positions the common properties at the end of the table. It makes use of shadow properties:
Most of the times, auditing properties don't need much visibility in the application, so I think shadow properties is exactly what you need. Here's an example:
I have two classes:
As you see: they don't have auditing properties, they're nicely mean and lean POCOs. (By the way, for convenience I lump
IsDeleted
together with "audit properties", although it isn't one and it may require another approach).And maybe that's the main message here: the class model isn't bothered with auditing concerns (single responsibility), it's all EF's business.
The audit properties are added as shadow properties. Since we want to do that for each entity we define a base
IEntityTypeConfiguration
:The concrete configurations are derived from this base class:
These should be added to the context's model in
OnModelCreating
:This will generate database tables having columns
InsertDateTime
,IsDeleted
andUpdateDateTime
at the end (independent of whenbase.Configure(builder)
is called, BTW), albeit in that order (alphabetical). I guess that's close enough.To make the picture complete, here's how to set the values fully automatically in a
SaveChanges
override:Small detail: I make sure that when an entity is inserted the database defaults set both fields (see above:
ValueGeneratedOnAdd()
, and hence the exclusion of added entities) so there won't be confusing differences caused by client clocks being slightly off. I assume that updating will always be well later.And to set
IsDeleted
you could add this method to the context:...or turn to one of the proposed mechanisms out there to convert
EntityState.Deleted
toIsDeleted = true
.You can always generate an initial migration for the model and manually rearrange the column order in the Migration.
Here is the open issue tracking support for explicit column ordering in EF Core: https://github.com/aspnet/EntityFrameworkCore/issues/10059
Also see this question and answer on using Shadow Properties and Query Filters for soft deletes. EF Core: Soft delete with shadow properties and query filters