ASP.Net核心开放式部分通用依赖项注入

时间:2018-07-24 04:25:40

标签: asp.net-core dependency-injection

我想使用开放的通用实现和接口为DI注册以下项目。我知道下面的示例以及我尝试过使用MakeGenericTypeGetGenericArguments的其他组合都无效。我想简单地调用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;
    }
}

2 个答案:

答案 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操作。但这正是TContextDbContext已经做的:

此外,DbSet<T>是一个“工作单元”,而DbContext是一个DbSet<T>,它比通用存储库可以提供给您的控制和功能要多得多。 / p>