依赖注入和泛型

时间:2017-07-10 22:00:37

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

我在使用泛型和DI在MVC Core项目中一起工作时遇到了麻烦。我有一个泛型类(这只是一个片段)。我需要初始化输入和输出,因为它们如何在代码的其他部分中使用,所以我使用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()约束,所以我需要一个无参数构造函数。如果我只添加一个注入构造函数,我得到

  

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

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

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

1 个答案:

答案 0 :(得分:0)

您收到错误原因的答案是new()限制。这指定参数must have a public parameterless constructor。这正是你的错误所说的。删除该约束应该可以解决该错误。但是,如果要使用DI,还有其他问题。

除了IMessagePart之外,没有一个类具有支持接口。为了有效地使用DI,您需要定义IMessageIProject等。然后您的容器可以在运行时创建特定实例,而不是像现在这样使用Activators 。所以你的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连接是一种常见的连接。