【问题标题】:Dependency Injection and Generics依赖注入和泛型
【发布时间】:2017-07-10 22:00:36
【问题描述】:

我无法让泛型和 DI 在 MVC Core 项目中协同工作。我有一个通用类(这只是一个片段)。我需要初始化 Input 和 Output,因为它们在代码的其他部分中是如何使用的,所以我使用 Activator 来提供初始值和 new() 约束。

public class Message<TIn, TOut> : 
    where TIn : class, IMessagePart, new()
    where TOut : class, IMessagePart, new() {

    public Message(){}    
    public Message(TIn inpart, TOut outpart) {
        Input = inpart;
        Output = outpart;
    }

    public TIn Input { get; set; } = (TIn)Activator.CreateInstance(typeof(TIn));
    public TOut Output { get; set; } = (TOut)Activator.CreateInstance(typeof(TOut));
}

我还有其他使用的类,它们有一些静态实用程序类。我正在尝试使用 DI 替换这些静态类。

public class Project : IMessagePart{
    int y = 1; 
    var x = StaticUtilityClass.StaticMethod(y);
}

这样使用

var projectMessage = new Message<Project, Project>();

我正在将静态实用程序类转换为实例类并注入它们。我正在使用内置的 .Net 核心容器。我将实用程序转换为实例类,并将它们注册为容器中的具体单例。对于大多数事情,我可以做正常的-

public class SomeClass{
    private readonly UtilityClass _utility;
    public SomeClass(UtilityClass utility){
        _utility = utility;

    var x = _utility.Method(1);
}

在我使用泛型之前一切正常。我不能在 projectMessage 上进行构造函数注入,因为泛型需要对其进行 new 并且它具有 new() 约束,所以我需要一个无参数的构造函数。如果我只添加一个注入构造函数,我会得到

'Project' 必须是具有公共无参数的非抽象类型 构造函数,以便将其用作泛型类型中的参数“TIn” 或方法“消息”。

如果我添加两个构造函数,Activator 只会调用不带参数的构造函数,因此不会调用 DI。我尝试了几种不同的方式使用 CreateInstance 的重载,但没有成功。

这里有什么建议吗?我不知道我是否应该继续使用静态,尝试某种服务定位器方法,或者是否有不同的方法来编写泛型。

【问题讨论】:

  • 消息对我来说似乎是运行时数据。您不应该使用 DI 容器来构造运行时数据,并且 should not 将运行时数据注入到您的组件中。您为什么要尝试使用 DI 容器来完成此任务?
  • 是的,消息是运行时的。它携带对象对到视图。问题的根源在于消息是运行时的并且不使用 DI,但是内部的类(例如 Project)具有对注入有意义的依赖项——比如数据访问。它们是 ModelBound(因此是 Activator)并且需要 noparam。消息不是 DI 层次结构的一部分,服务对内部对象不可用。在某些方面,DI 使参数化构造函数看起来“未参数化”——没有错误。在更一般的意义上,我试图“跳过” DI 层次结构中的一个节点 - 在它上面使用 DI,在它下面使用 DI,但它没有。

标签: c# asp.net-mvc generics dependency-injection


【解决方案1】:

您看到错误的原因是new() 约束。这指定参数must have a public parameterless constructor。这正是你的错误所说的。删除该约束应该可以解决该错误。但是,如果你想使用 DI,你还有另一个问题。

除了IMessagePart,您的所有类都没有支持接口。为了有效地使用 DI,您需要定义 IMessageIProject 等。然后您的 容器 可以在运行时创建特定实例,而不是像现在这样使用激活器。所以你的Message 声明看起来像:

public class Message<TIn, TOut> : IMessage,
    where TIn : class, IMessagePart
    where TOut : class, IMessagePart
{
    public TIn input { get; set; }
    public TOut output { get; set; } 

    public Message(TIn inpart, TOut outpart) {
        this.input = inpart;
        this.output = outpart;
    }
}

您将设置您的 DI 容器,如下所示:

public Startup()
{
    var container = new DiContainer(); // I know this is the wrong name; I'm not familiar with the built in container naming and functionality.

    container.Register<IMessage>();
    container.Register<IMessagePart>();
    container.Register<IProject>();

    // Register other stuff here
}

为您的特定容器更改语法。您还可以选择注册您的实例,例如:

container.Register<Message>(() => Message(part1, part2));

以便您专门注入一个在启动时更新的Message,但在大多数情况下这并不理想。通常,您希望 DI 容器根据需要动态创建实例(因此是接口),而不是使用单个具体实例化。当然也有例外; SQL 连接是一种常见的连接。

【讨论】:

  • 没有必要总是对 DI 中的所有内容都使用接口。容器将直接从类中实例化,这样做很常见——这就是 EF 对 DataContext 所做的。 Message 是一个容器,其中包含在整个代码中定义的许多不同类型。有点像列表。我确实有一个接口,但它不是你要 DI 的类类型,更常见的是创建它们并用初始化器填充它们。这里还有其他不使用它们的原因,因为我需要输入消息 - 我需要使用 out 创建许多 ifaces 来实现它。
  • 是的,我在回答的最后部分指出了这一点。你说得对,接口不是必需的。但它们确实使 DI 和单元测试变得更加容易,非常。此外,您的 List 示例很差。 List 实现了 8 个不同的(尽管相关的)接口。如果没有看到您的代码库的其余部分,我无法确定,但我怀疑您是否需要 out 参数。您的类可能需要特定的消息类型(IXMessage 与 IYMessage,或两者),它们的实现可能会有所不同。
  • 我知道如果没有看到整个事情很难提供帮助,但是谢谢。我在上面的回复中添加了更多信息。更普遍的问题是当我有一个不是由 DI 创建的对象时,有没有办法为下面的对象“启动”DI?在这种情况下,我有要注入而不是用作静态的实用程序类型的类。他们现在是 DI 并且自己使用 DI 服务。我可以将容器公开为“全局”并在非 DI 类的构造函数主体中拾取它,它“恢复”DI,但它不是很漂亮。
  • 我认为@Steven 是正确的,Messages 不应该被注入/作为运行时数据。我会根据需要(在运行时代码中,而不是在组合根中)将它们创建为轻量级对象,这些对象本身不包含Project 对象,而是具有ProjectId。然后,这些 id 将能够键回由您的 DI 容器创建的现有 Projects。无论处理那些Messages 的东西,都可以访问注入的List&lt;Project&gt; 来做它需要对Message 做的任何事情。
  • 问题是,项目需要是对象。一条消息是新的,并被发送到带有填充项目的视图。 View 需要数据,而不是 Id。视图填充第二个项目并将消息发送到控制器。然后,我可以同时使用 pre 和 post 状态。 Binder 尝试对Message&lt;Project, Project&gt; 进行水合,但由于 Message 是新的(不是 DI'ed),它不了解容器,并且容器无法访问 Project。我不能在 Project 上使用 DI。我在构造函数主体中使用 ServiceLocator 来公开容器,这样我就可以在 Project 中进行 DI。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-09
  • 1970-01-01
  • 2020-03-25
相关资源
最近更新 更多