Skip to content

Instantly share code, notes, and snippets.

@amkherad
Last active July 16, 2020 18:01
Show Gist options
  • Select an option

  • Save amkherad/dbf00c3e4eb45d30237c4cf790ce1178 to your computer and use it in GitHub Desktop.

Select an option

Save amkherad/dbf00c3e4eb45d30237c4cf790ce1178 to your computer and use it in GitHub Desktop.
EntityFrameworkConfigurationsApplier
public class DataContext : DbContext {
private static readonly Lazy<Action<ModelBuilder>> EntityConfigurationApplier =
new Lazy<Action<ModelBuilder>>(
() => EntityConfigurationsApplier.CreateAutoApplierBuilder(typeof(DataContext))
);
protected override void OnModelCreating(
ModelBuilder modelBuilder
)
{
base.OnModelCreating(modelBuilder);
EntityConfigurationApplier.Value.Invoke(modelBuilder);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Context
{
public class EntityConfigurationsApplier
{
public static Action<ModelBuilder> CreateAutoApplierBuilder(
Type targetContextType
)
{
var configurations = targetContextType.Assembly
.GetTypes()
.Where(t => t.GetInterfaces()
.Any(i => i.IsGenericType &&
i.GetGenericTypeDefinition() ==
typeof(IEntityTypeConfiguration<>)))
.ToArray();
//uncomment these if you want to filter which EntityConfigurations should be applied on the target context
//by creating a ApplyOnContextAttribute attribute class and decorate the EntityConfigurations with it to
//specify which EntityConfiguration should be applied on which DbContext.
//It's usefull in multi-context implementations.
// configurations = configurations
// .Where(t =>
// {
// var attributes = t.GetCustomAttributes();
// var context = attributes.FirstOrDefault(a => a is ApplyOnContextAttribute);
// if (context is ApplyOnContextAttribute applyOnContext)
// {
// return applyOnContext.TargetContext == targetContextType;
// }
// return false;
// })
// .ToArray();
var applyConfigMethod = typeof(ModelBuilder)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(m =>
m.Name == nameof(ModelBuilder.ApplyConfiguration));
if (applyConfigMethod is null)
{
throw new InvalidOperationException();
}
var modelBuilderParameter = Expression.Parameter(typeof(ModelBuilder), "modelBuilder");
var commands = new List<Expression>();
foreach (var config in configurations)
{
var entityType = config.GetInterfaces()
.Single(
i => i.IsGenericType &&
i.GetGenericTypeDefinition() ==
typeof(IEntityTypeConfiguration<>))
.GetGenericArguments()
.Single();
var target = applyConfigMethod.MakeGenericMethod(entityType);
var instance = Expression.New(config);
var call = Expression.Call(modelBuilderParameter, target, instance);
commands.Add(call);
}
var body = Expression.Block(commands);
var lambda = Expression.Lambda<Action<ModelBuilder>>(body, modelBuilderParameter);
return lambda.Compile();
}
}
}
@amkherad

amkherad commented Jul 6, 2020

Copy link
Copy Markdown
Author

Here's a piece of code to apply multiple IEntityTypeConfiguration without explicitly calling them.

This method creates a delegate that you can call to configure all entities in a specific ModelBuilder.

This method does nothing for caching, you can cache the generated delegate like this:

public class DataContext : DbContext {
    private static readonly Lazy<Action<ModelBuilder>> EntityConfigurationApplier =
            new Lazy<Action<ModelBuilder>>(
                () => EntityConfigurationsApplier.CreateAutoApplierBuilder(typeof(DataContext))
            );

    protected override void OnModelCreating(
        ModelBuilder modelBuilder
    )
    {
        base.OnModelCreating(modelBuilder);

        EntityConfigurationApplier.Value.Invoke(modelBuilder);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment