【问题标题】:Dependency Injection of open generics with arguments带参数的开放泛型的依赖注入
【发布时间】:2021-06-22 19:40:39
【问题描述】:

This 文章讨论了如何在 .Net Core 中注册通用接口。但是,我有一个具有多个参数的通用接口,并且无法确定注册和构造函数注入。

我的接口有 4 个参数

public class TestImplementation 
{
    // Try to inject IRepository here ??????
    public TestImplementation(.......)
    {
        ...
    }
}

public class Repository : IRepository<Test1, Test2, Test3, Test4> 
{
    ...
}

public interface IRepository<T, U, V, W> where T : ITemplate1 where U : ITemplate2,...  
{
    ...
}

如果我尝试将接口注入任何类,它会给我错误,因为即使在代码的其他部分中使用下面的代码也无法解析接口

services.GetService(typeof(IRepository<,,,>))

我尝试使用构造函数注入,但它使编译器不满意(尝试激活 xxxx 时无法解析类型“....接口....”的服务),因为我想保持接口打开。但是我在代码中解析了接口

【问题讨论】:

  • 您的代码目前包含无效语法,因此很难看出这不是the question you link to 的重复。 TestRepository : Repository&lt;Test&gt; 是不可能的,因为 Repository 不是泛型类。 Repository 实现了IRepository 但似乎没有指定接口所需的类型。
  • 我清理了代表问题的代码
  • 受链接问题中答案的启发,这应该可以工作:services.AddScoped(typeof(IRepository&lt;Test1, Test2, Test3, Test4&gt;), typeof(Repository));,然后在构造函数中:public TestImplementation(IRepository&lt;Test1, Test2, Test3, Test4&gt; repo)
  • 你不能 services.GetService(typeof(IRepository&lt;,,,&gt;)) 因为你得到的服务必须是一个 constructed 泛型类型,即它必须指定所有类型参数,例如services.GetService(typeof(IRepository&lt;int, string, float, bool&gt;))。如果您只想向容器注册一个构造的泛型类型,那么这很容易:services.AddScoped&lt;IRepository&lt;int, string, float, bool&gt;, MyRepositoryImpl&gt;()。如果你想在容器中注册开放的泛型,你需要一个更高级的 DI 框架,比如 Autofac。
  • 糟糕,我错了——你可以用services.AddScoped(typeof(IRepository&lt;,,,&gt;), typeof(MyRepositoryImpl&lt;,,,&gt;))之类的东西注册开放的泛型。我假设泛型类型参数的数量必须与服务类型和实现类型相匹配。

标签: c# .net-core dependency-injection


【解决方案1】:

正确的做法是不注入Repository服务。它应该只是用作具有功能的模板。然后创建一个从Repository 类继承的附加类和一个从IRepository 继承的接口。这样您就可以分配通用值的值,然后以一种简洁且受控的方式注入它。

起初,这种模式可能看起来有点额外的工作,但它允许为每个表存储库提供自定义功能,清楚地知道您正在使用哪个表,并允许轻松替换不同的数据库。请参见下面的示例:

如你所愿,创建你的RepositoryIRepository 接口:

public abstract class Repository<T, U, V, W> : IRepository<T, U, V, W> 
{
    ...
}

public interface IRepository<T, U, V, W> where T : ITemplate1 where U : ITemplate2,...  
{
    ...
}

现在为您的特定表创建一个界面。首先是界面:

public interface ISomeTableRepository : IRepository<input_1, input_2, ...> {}

现在创建类存储库:

public class SomeTableRepository : Repository<input_1, input_2,...> , ISomeTableRepository {}

现在您注册这些在您的Startup.cs 文件中没有输入的新存储库。

services.AddScoped<ISomeTableRepository, SomeTableRepository> ();

现在您可以轻松注入,无需添加参数:

public class TestImplementation 
{
    readonly ISomeTableRepository _someTable;

    
    public TestImplementation(ISomeTableRepository someTable)
    {
        _someTable = someTable;
    }
}

【讨论】:

    【解决方案2】:

    您需要在未绑定的泛型类型中拥有哪些成员,您将其放入派生类型实现的基类型中。

    对于未知类型的值,您可以使用objectdynamic

    看这个例子:

    public interface IRepository
    {
        object TV { get; set; }
        object UV { get; set; }
        object VV { get; set; }
        object WV { get; set; }
    }
    
    public interface IRepository<T, U, V, W>: IRepository
    {
        object IRepository.TV { get => TV; set => TV = (T)value; }
        object IRepository.UV { get => UV; set => UV = (U)value; }
        object IRepository.VV { get => VV; set => VV = (V)value; }
        object IRepository.WV { get => WV; set => WV = (W)value; }
        new T TV { get; set; }
        new U UV { get; set; }
        new V VV { get; set; }
        new W WV { get; set; }
    }
    

    那么,你可以这样做:

    services.GetService(typeof(IRepository))
    

    没有任何问题。

    https://dotnetfiddle.net/kwoOOe

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-25
      • 1970-01-01
      相关资源
      最近更新 更多