批量注册IEntityTypeConfiguration<>实体框架核心

时间:2017-10-30 11:07:54

标签: c# entity-framework-core

好的,我正在使用带有dot net core和代码优先迁移的实体框架。这不是一个问题,我只是想知道是否有人遇到过更好的方法。

目前我有很多实体类型配置,如此

public class ExampleEntityConfiguration : IEntityTypeConfiguration<ExampleEntity>
{
   public void Configure(EntityTypeBuilder<ExampleEntity> builder)
   {
      builder.Property(p => p.Id).ValueGeneratedNever();

      // more options here
   }
}

并在我的dbcontext中注册它们,如此

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

  modelBuilder.ApplyConfiguration(new ExampleEntityConfiguration());

  // lot's more configurations here
}

有没有人遇到或知道注册所有IEntityTypeConfiguration接口的方法?

看起来很多重复的代码可以通过获取配置列表,循环遍历并在上下文中应用来解决。我只是不知道从哪里开始获取特定名称空间中存在的IEntityTypeConfiguration类列表。

任何帮助/建议都会很棒。

6 个答案:

答案 0 :(得分:14)

可以用这样的反射来完成:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);    
    // get ApplyConfiguration method with reflection
    var applyGenericMethod = typeof(ModelBuilder).GetMethod("ApplyConfiguration", BindingFlags.Instance | BindingFlags.Public);            
    // replace GetExecutingAssembly with assembly where your configurations are if necessary
    foreach (var type in Assembly.GetExecutingAssembly().GetTypes()
        .Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters)) 
    {
        // use type.Namespace to filter by namespace if necessary
        foreach (var iface in type.GetInterfaces()) {
            // if type implements interface IEntityTypeConfiguration<SomeEntity>
            if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) {
                // make concrete ApplyConfiguration<SomeEntity> method
                var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
                // and invoke that with fresh instance of your configuration type
                applyConcreteMethod.Invoke(modelBuilder, new object[] {Activator.CreateInstance(type)});
                break;
            }
        }
    }
}

答案 1 :(得分:8)

来自@Evk kan的精彩工作将进一步封装在可重用的扩展方法中:

     public LayoutController(
        IPerSessionCache sessionCache,
        IUserNavigationManager userNavigationManager,
        IMultiTenancyConfig multiTenancyConfig,
        ILanguageManager languageManager,
        ITenancyNameFinder tenancyNameFinder,
        TenantManager tenantManager,
        IUserLinkAppService userLinkAppService,
        UserManager userManager)

可以这样称呼:

[ChildActionOnly]
    public PartialViewResult AppHeader()
    {
        var headerModel = new Areas.Mpa.Models.Layout.HeaderViewModel
        {
            LoginInformations = AsyncHelper.RunSync(_sessionCache.GetCurrentLoginInformationsAsync),
            Languages = _languageManager.GetLanguages(),
            CurrentLanguage = _languageManager.CurrentLanguage,
            IsMultiTenancyEnabled = _multiTenancyConfig.IsEnabled,
            IsImpersonatedLogin = AbpSession.ImpersonatorUserId.HasValue,
            HasLinkedAccounts = AsyncHelper.RunSync(_userLinkAppService.HasLinkedAccounts)
        };

        return PartialView("~/Views/Layout/_AppHeader.cshtml", headerModel);
    }

[ChildActionOnly]
    public PartialViewResult AppNavbar(string currentPageName = "")
    {
        var sidebarModel = new Areas.Mpa.Models.Layout.NavbarViewModel
        {        
            Menu = AsyncHelper.RunSync(() => _userNavigationManager.GetMenuAsync(MeNavigationProvider.MenuName, AbpSession.ToUserIdentifier())),
            CurrentPageName = currentPageName
        };

        return PartialView("_AppNavbar", sidebarModel);
    }

答案 2 :(得分:5)

@Evk的回答有点过时了,不再起作用了,@ paul van bladel的回答是可行的,尽管它包含硬编码的字符串,但我非常讨厌。如果有人有我的感觉,我想以创建静态扩展方法的形式提出我的解决方案:

public static ModelBuilder ApplyAllConfigurationsFromAssembly(
    this ModelBuilder modelBuilder, Assembly assembly)
{   
    var applyGenericMethod = typeof(ModelBuilder)
        .GetMethods(BindingFlags.Instance | BindingFlags.Public)
        .Single(m => m.Name == nameof(ModelBuilder.ApplyConfiguration)
            && m.GetParameters().Count() == 1
            && m.GetParameters().Single().ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));        
    foreach (var type in assembly.GetTypes()
        .Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters)) 
    {
        foreach (var iface in type.GetInterfaces())
        {
            if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) 
            {
                var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
                applyConcreteMethod.Invoke(modelBuilder, new object[] {Activator.CreateInstance(type)});
                break;
            }
        }
    }
}

并且,如果您的配置类与DbContext存储在同一程序集中,则可以按以下方式使用此方法:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyAllConfigurationsFromAssembly(GetType().Assembly);           
    base.OnModelCreating(modelBuilder);
}

答案 3 :(得分:5)

使用EF Core 2.2+,它变得更加简单:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   Assembly assemblyWithConfigurations = GetType().Assembly; //get whatever assembly you want
   modelBuilder.ApplyConfigurationsFromAssembly(assemblyWithConfigurations);
}

答案 4 :(得分:2)

-DbContext

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());

    base.OnModelCreating(builder);
}

答案 5 :(得分:0)

我非常喜欢@paul van bladel为将代码移动到扩展方法而提供的答案。我还需要从其他程序集中调用它,因此我创建了一个枚举并更改了适用的类型,以便进行不同的设置。

IEnumerable<Type> assemblyTypeList;

switch (pAssemblyMethodType)
{
    case AssemblyMethodType.CallingAssembly:
        assemblyTypeList = Assembly.GetCallingAssembly()
            .GetTypes()
            .Where(c => c.IsClass
                && !c.IsAbstract
                && !c.ContainsGenericParameters);
        break;
    case AssemblyMethodType.ExecutingAssembly:
        assemblyTypeList = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(c => c.IsClass
                && !c.IsAbstract
                && !c.ContainsGenericParameters);
        break;
    default:
        throw new ArgumentOutOfRangeException(nameof(pAssemblyMethodType), pAssemblyMethodType, null);
}
相关问题