我想使用开放的通用实现和接口为DI注册以下项目。我知道下面的示例以及我尝试过使用MakeGenericType
或GetGenericArguments
的其他组合都无效。我想简单地调用AddRepository<MyDbContext>
,然后能够将实现注入到类中,而不必显式注册我正在使用的类型。
接口
public interface IRepository<TEntity>
{
}
实施
public class Repository<TEntity, TContext> : IRepository<TEntity>
where TEntity : class
where TContext : DbContext
{
}
注册
public static class RepositoryServiceCollectionExtensions
{
public static IServiceCollection AddRepository<TContext>(
this IServiceCollection services) where TContext : DbContext
{
services.TryAddScoped(
typeof(IRepository<>),
typeof(Repository< , TContext>));
return services;
}
}
答案 0 :(得分:0)
您不能具有部分打开的通用引用。全部或全无。换句话说,您可以尝试:
services.TryAddScoped(
typeof(IRepository<>),
typeof(Repository<,>));
但是,如果这不起作用,则可能需要在AddRepository
方法中添加类型参数:
public static IServiceCollection AddRepository<TEntity, TContext>(this IServiceCollection services)
where TEntity : class
where TContext : DbContext
{
services.TryAddScoped(
typeof(IRepository<TEntity>),
typeof(Repository<TEntity, TContext>));
return services;
}
当然,我认为这破坏了您最终要在此处实现的目标:一次性注册所有实体类型的存储库。您总是可以使用一些反射来查找程序集中的所有实体(它们需要共享一些共同的东西:基类,接口等),然后枚举它们,并使用反射在服务上调用AddScoped
每个集合。
总而言之,您在这里可以做的最好的事情就是将所有这些东西扔掉。您不需要存储库。 EF已经实现了存储库和工作单元模式。当您使用像EF这样的ORM时,实际上是在创建那个数据层,而不是创建的自定义类库。在EF周围放置您自己的自定义包装器,不仅会增加代码的熵(更多的维护,更多的测试,甚至更多的破坏),而且在很多情况下还会使EF的工作方式混乱,从而导致EF效率降低最好的情况,在最坏的情况下直接向您的应用程序引入错误。
答案 1 :(得分:0)
依赖项注入容器const addVideosToStore = function(videos) {
};
及其抽象层不支持开放的通用工厂。因此,您通常无法在此实现您想做的事情。还有no support planned。
与许多其他与依赖项注入相关的功能不同,仅通过提供正确的包装器或工厂类型,也实际上不可能对此进行修补。因此,您实际上必须在这里更改设计。
由于您要解析Microsoft.Extensions.DependencyInjection
,并且唯一的方法是注册等效的开放通用类型,因此您将必须具有实现存储库的某种类型IRepository<TEntity>
。这使得不可能从泛型类型参数中检索数据库上下文类型,因此在这里您将不得不使用其他方式。
您可以选择不同的方法。例如,您可以使用上下文类型配置您的Repository<TEntity>
(例如,使用M.E.Options),并使其动态解析Repository<TEntity>
。但是,由于您实际上可以控制数据库上下文,因此建议您添加一个标记接口或为该上下文引入另一种类型,然后可以在容器中注册:
Repository<TEntity, TContext>
然后,您的扩展方法可能如下所示:
public class Repository<TEntity> : IRepository<TEntity>
{
public Repository(IDbContext dbContextFactory)
{ … }
}
public class MyDbContext : DbContext, IDbContext
{ … }
当然,这会改变您的public static IServiceCollection AddRepository<TContext>(this IServiceCollection services)
where TContext : DbContext, IDbContext
{
services.AddTransient(typeof(IDbContext), sp => sp.GetService<TContext>());
services.TryAddScoped(typeof(IRepository<>), typeof(Repository<>));
return services;
}
实现的工作方式,但是我实际上并不认为除了注入数据库上下文类型之外,您还不需要了解Repository
类型。所以这可能仍然对您有用。
话虽这么说,但我也同意克里斯·普拉特(Chris Pratt)的观点,您可能不需要这个。您说您要介绍存储库,因为“为每个实体编码存储和实现都是一项耗时的任务” ,但是您应该真正考虑您是否真的需要。通用存储库的功能非常有限,并且通常意味着您仅在执行CRUD操作。但这正是TContext
和DbContext
已经做的:
DbContext.Add
,DbSet<T>.Add
DbContext.Find
,DbSet<T>.Find
DbContext.Update
,DbSet<T>.Update
DbContext.Remove
,DbSet<T>.Remove
此外,DbSet<T>
是一个“工作单元”,而DbContext
是一个DbSet<T>
,它比通用存储库可以提供给您的控制和功能要多得多。 / p>