Implementing Missing Features in Entity Framework Core

This is the seventh post 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
Part 4: Conventions
Part 5: Getting the SQL for a Query
Part 6: Lazy Loading
This time I’m going to cover automatic entity configuration in mapping classes, that is, outside of the DbContext.OnModelCreating method. If you remember, Entity Framework Code First supported having classes inheriting from EntityTypeConfiguration in the same assembly as the context .
  • public interface IEntityTypeConfiguration<T> where T : class
  • {
  • void Configure(EntityTypeBuilder<T> entityTypeBuilder);
  • }
The actual implementation goes like this:
  • public static class EntityTypeConfigurationExtensions
  • {
  • private static readonly MethodInfo entityMethod =
  • typeof(ModelBuilder).GetTypeInfo().GetMethods().Single(x => (x.Name == “Entity”) && (x.IsGenericMethod == true) && (x.GetParameters().Length == 0));
  • private static Type FindEntityType(Type type)
  • {
  • var interfaceType = type.GetInterfaces().First(x => (x.GetTypeInfo().IsGenericType == true) && (x.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)));
  • return interfaceType.GetGenericArguments().First();
  • }
  • private static readonly Dictionary<Assembly, IEnumerable<Type>> typesPerAssembly = new Dictionary<Assembly, IEnumerable<Type>>();
  • public static ModelBuilder ApplyConfiguration<T>(this ModelBuilder modelBuilder,
  • EntityTypeConfiguration<T> configuration) where T : class
  • {
  • var entityType = FindEntityType(configuration.GetType());
  • dynamic entityTypeBuilder = entityMethod
  • .MakeGenericMethod(entityType)
  • .Invoke(modelBuilder, new object[0]);
  • configuration.Configure(entityTypeBuilder);
  • return modelBuilder;
  • }
  • public static ModelBuilder UseEntityTypeConfiguration(this ModelBuilder modelBuilder)
  • {
  • IEnumerable<Type> configurationTypes;
  • var asm = Assembly.GetEntryAssembly();
  • if (typesPerAssembly.TryGetValue(asm, out configurationTypes) == false)
  • {
  • typesPerAssembly[asm] = configurationTypes = asm
  • .GetExportedTypes()
  • .Where(x => (x.GetTypeInfo().IsClass == true) && (x.GetTypeInfo().IsAbstract == false) && (x.GetInterfaces().Any(y => (y.GetTypeInfo().IsGenericType == true) && (y.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)))));
  • }
  • var configurations = configurationTypes.Select(x => Activator.CreateInstance(x));
  • foreach (dynamic configuration in configurations)
  • {
  • ApplyConfiguration(modelBuilder, configuration);
  • }
  • return modelBuilder;
  • }
  • }
The code essentially looks at the entry assembly and finds all non-abstract public types that implement IEntityTypeConfiguration. For each of those, it creates an instance, extracts the template argument and creates an EntityTypeBuilder from calling the Entity method of the ModelBuilder class and calls the IEntityTypeConfiguration.

Comments

Post a Comment

Popular posts from this blog

ANGULAR LAZY LOADING WITH WEB PACK 2-1

Mobile Application Development

.NET Core