This will be the fourth in a series of posts about bringing the features that were present in Entity Framework pre-Core into EF Core. The others are:
- Part 1: Introduction, Find, Getting an Entity’s Id Programmatically, Reload, Local, Evict
- Part 2: Explicit Loading
- Part 3: Validations
Conventions are a mechanism by which we do not have to configure specific aspects of our mappings over and over again. We just accept how they will be configured, of course, we can override them if we need, but in most cases, we’re safe.
In EF 6.x we had a number of built-in conventions (which we could remove) but we also had the ability to add our own. In Entity Framework Core 1, this capability hasn’t been implemented, or, rather, it is there, but hidden under the surface.
A lot has changed. As of Entity Framework Core, conventions are specific to the provider in use, which makes perfect sense. But then we have several kinds of conventions. Just to give you an idea, the ConventionSet class exposes 15 convention collections! This is the place where we can register our own conventions, but there are two problems:
- The current ConventionSet instance is not publicly accessible, so we need to use reflection to get hold of it;
- We don’t want to add a new “blank” instance of ConventionSet because this way we would lose all of the conventions that would have been injected by the provider.
So, the solution I came up with had to use reflection to get the current convention set and allow adding new conventions, which is not ideal. Let’s see the code.
Feel free to cache the _conventionSet field somewhere, but that is not the point of this post.
You can see that we are adding the new convention to the ModelBuiltConventions collection, basically, because this is the only collection that Entity Framework Core will go through during the OnModelCreating method, all of the other collections will have been processed before it. We’re cool with that.
Let’s see two examples of sample conventions, first, one to set the maximum length of strings, where it hasn’t been explicitly set:
Easy, easy, hey? We go through all of the mapped entities, then through all of their properties of type string, check if the maximum length annotation is present, and, if not, add a new one. Only one method, with access to all mapped entities.
Another example, turning off the table pluralization. As you know, as of EF RC2, table names of DbSet properties exposed in the DbContext are pluralized. We can get rid of this behavior if we explicitly set the name to something, in this case, it will be the entity type name:
Now, some new extension methods come handy:
And that’s it! The way to apply these conventions (one or both) is during OnModelCreating:
You can now reuse these conventions in all of your context classes very easily. Hope you enjoy it! Two final remarks about this solution:
- The reflection bit is a problem, because things may change in the future. Hopefully Microsoft will give us a workaround;
- There is no easy way to remove built-in (or provider-injected) conventions, because are applied before OnModelCreating, but I think this is not a big problem, since we can change them afterwards, as we’ve seen.
Stay tuned for more!