Conventions
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.
- public static class ModelBuilderExtensions
- {
- public static ModelBuilder AddConvention(this ModelBuilder modelBuilder, IModelConvention convention)
- {
- var imb = modelBuilder.GetInfrastructure();
- var cd = imb.Metadata.ConventionDispatcher;
- var cs = cd.GetType().GetField(“_conventionSet”, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(cd) as ConventionSet;
- cs.ModelBuiltConventions.Add(convention);
- return modelBuilder;
- }
- public static ModelBuilder AddConvention(this ModelBuilder modelBuilder) where TConvention : IModelConvention, new()
- {
- return modelBuilder.AddConvention(new TConvention());
- }}
Now, some new extension methods come handy:
- public sealed class DefaultStringLengthConvention : IModelConvention
- {
- internal const int DefaultStringLength = 50;
- internal const string MaxLengthAnnotation = “MaxLength”;
- private readonly int _defaultStringLength;
- public DefaultStringLengthConvention(int defaultStringLength = DefaultStringLength)
- {
- this._defaultStringLength = defaultStringLength;
- }
- public InternalModelBuilder Apply(InternalModelBuilder modelBuilder)
- {
- foreach (var entity in modelBuilder.Metadata.GetEntityTypes())
- {
- foreach (var property in entity.GetProperties())
- {
- if (property.ClrType == typeof(string))
- {
- if (property.FindAnnotation(MaxLengthAnnotation) == null)
- {
- property.AddAnnotation(MaxLengthAnnotation, this._defaultStringLength);
- }}}}
- return modelBuilder;
- }
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.
About the author