gpt4 book ai didi

c# - 使用 ApplyConfigurationsFromAssembly() 程序集扫描时访问 IEntityTypeConfiguration 内的 DI 服务

转载 作者:行者123 更新时间:2023-12-03 21:00:08 24 4
gpt4 key购买 nike

我需要访问我的 IEntityTypeConfiguration 类中的一些 DI 服务,以便找到一些用户 session 信息并执行一些查询过滤。

我可以通过执行以下操作以“手动”方式实现这一目标......

    // setup config to use injection (everything normal here)
public class MyEntityConfig: IEntityTypeConfiguration<MyEntity>
{
private readonly IService _service;

public MyEntityConfig(IService service)
{
IService = service;
}


public void Configure(EntityTypeBuilder<MyEntity> entity)
{
// do some stuff to entity here using injected _service
}
}

//use my normal DI (autofac) to inject into my context, then manually inject into config
public class MyContext: DbContext
{
private readonly IService _service;

public MyContext(DbContextOptions options, IService service) : base(options)
{
_service = service;
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//this works no problem
modelBuilder.ApplyConfiguration(new MyEntityConfig(_service));
}
}


我在最后一部分要做的是使用程序集扫描通过...拉入我的配置
 \\modelBuilder.ApplyConfiguration(new MyEntityConfig(_service));
modelBuilder.ApplyConfigurationsFromAssembly(typeof(MyContext).Assembly);

但是这样做总是会调用 IEntityTypeConfiguration<> 的默认 ctor,所以我注入(inject)的服务都是空的。

我考虑尝试通过使用反射来获取配置然后自己调用 ctor 来滚动我自己的 ApplyConfigurationsFromAssembly 版本,但这似乎令人不快。

有任何想法吗?

最佳答案

所以这就是我在跟随@Cyril 的领导并调查源代码后想出的。我“借用”了现有的 ModelBuilder.ApplyConfigurationsFromAssembly() 方法并重新编写了一个新版本(作为模型构建器扩展),它可以采用服务参数列表。

        /// <summary>
/// This extension was built from code ripped out of the EF source. I re-jigged it to find
/// both constructors that are empty (like normal) and also those that have services injection
/// in them and run the appropriate constructor for them and then run the config within them.
///
/// This allows us to write EF configs that have injected services in them.
/// </summary>
public static ModelBuilder ApplyConfigurationsFromAssemblyWithServiceInjection(this ModelBuilder modelBuilder, Assembly assembly, params object[] services)
{
// get the method 'ApplyConfiguration()' so we can invoke it against instances when we find them
var applyConfigurationMethod = typeof(ModelBuilder).GetMethods().Single(e => e.Name == "ApplyConfiguration" && e.ContainsGenericParameters &&
e.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition() ==
typeof(IEntityTypeConfiguration<>));


// test to find IEntityTypeConfiguration<> classes
static bool IsEntityTypeConfiguration(Type i) => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>);

// find all appropriate classes, then create an instance and invoke the configure method on them
assembly.GetConstructableTypes()
.ToList()
.ForEach(t => t.GetInterfaces()
.Where(IsEntityTypeConfiguration)
.ToList()
.ForEach(i =>
{
{
var hasServiceConstructor = t.GetConstructor(services.Select(s => s.GetType()).ToArray()) != null;
var hasEmptyConstructor = t.GetConstructor(Type.EmptyTypes) != null;

if (hasServiceConstructor)
{
applyConfigurationMethod
.MakeGenericMethod(i.GenericTypeArguments[0])
.Invoke(modelBuilder, new[] { Activator.CreateInstance(t, services) });
Log.Information("Registering EF Config {type} with {count} injected services {services}", t.Name, services.Length, services);
}
else if (hasEmptyConstructor)
{
applyConfigurationMethod
.MakeGenericMethod(i.GenericTypeArguments[0])
.Invoke(modelBuilder, new[] { Activator.CreateInstance(t) });
Log.Information("Registering EF Config {type} without injected services", t.Name, services.Length);
}
}
})
);

return modelBuilder;
}

private static IEnumerable<TypeInfo> GetConstructableTypes(this Assembly assembly)
{
return assembly.GetLoadableDefinedTypes().Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition);
}

private static IEnumerable<TypeInfo> GetLoadableDefinedTypes(this Assembly assembly)
{
try
{
return assembly.DefinedTypes;
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t != null as Type).Select(IntrospectionExtensions.GetTypeInfo);
}
}
}


然后在我的 OnModelCreating() 中,我只是调用我的扩展程序......
modelBuilder.ApplyConfigurationsFromAssemblyWithServiceInjection(typeof(MyContext).Assembly, myService, myOtherService);

这种实现并不理想,因为您的所有配置都必须具有无参数构造函数或具有固定服务列表的构造函数(即不能具有 ClassA(serviceA)、ClassB(ServiceB);您只能具有 ClassA(serviceA, serviceB), ClassB(serviceA, serviceB) 但这对我的用例来说不是问题,因为这正是我目前所需要的。

如果我需要更灵活的路径,我将沿着让模型构建器容器感知的路径,然后使用 DI 容器在内部进行服务解析,但我目前不需要。

关于c# - 使用 ApplyConfigurationsFromAssembly() 程序集扫描时访问 IEntityTypeConfiguration<T> 内的 DI 服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58638358/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com