在控制台应用程序中使用Unity属性[动态]进行属性注入无法正常工作

时间:2019-06-14 00:36:18

标签: c# dependency-injection console-application

public class Program { //Entrypoint
    public static void Main(string[] args) {
            var container = new UnityContainer();
        container.RegisterType<IMetric>(new InjectionFactory(c => BuildMetric()));
          ...
          SomeClassThatCallsLoader kk = new SomeClassThatCallsLoader();
          kk.DoSomething();  //Loader gets instantiated in here..
    }
}

   public class Loader {
     [Dynamic]
     public IMetric Metric { get; set;}
    }

为什么未设置Metric属性?控制台应用程序。我必须注册容器吗?在哪里以及如何?

1 个答案:

答案 0 :(得分:1)

依赖注入容器(例如Unity)没有任何作用-它们通过在容器中解析实例及其依赖来工作。这意味着所有类型都必须在容器中注册(显式或使用约定)。

        // Composition Root
        var container = new UnityContainer();
        container.RegisterFactory<IMetric>(c => BuildMetric());
        container.RegisterType<ILoader, Loader>();
        container.RegisterType<ISomeClassThatDependsOnLoader, SomeClassThatDependsOnLoader>();

推荐的注入依赖项的方法是通过类构造函数接受它们,而不是使用属性注入。仅在合理时才使用属性注入。

但这是您的示例,重写后既包含属性注入也包含构造函数注入,还包含工厂方法(在此处仅是静态的,因为您未提供如何执行此操作的示例-我不建议使用静态方法(如果可以避免的话)。

using System;
using Unity;
class Program
{
    static void Main(string[] args)
    {
        // Composition Root
        var container = new UnityContainer();
        container.RegisterFactory<IMetric>(c => BuildMetric());
        container.RegisterType<ILoader, Loader>();
        container.RegisterType<ISomeClassThatDependsOnLoader, SomeClassThatDependsOnLoader>();

        // Application (runtime)
        var kk = container.Resolve<ISomeClassThatDependsOnLoader>(); //Loader gets instantiated in here..
        kk.DoSomething();
    }

    public static IMetric BuildMetric()
    {
        return new Metric();
    }
}

public interface ILoader
{
    IMetric Metric { get; set; } // Property Injection
}

public class Loader : ILoader
{
    [Dependency]
    public IMetric Metric { get; set; }
}

public interface IMetric
{
}

public class Metric : IMetric
{
}

public interface ISomeClassThatDependsOnLoader
{
    void DoSomething();
}

public class SomeClassThatDependsOnLoader : ISomeClassThatDependsOnLoader
{
    private readonly ILoader loader;
    public SomeClassThatDependsOnLoader(ILoader loader) // Constructor Injection
    {
        this.loader = loader ?? throw new ArgumentNullException(nameof(loader));
    }
    public void DoSomething()
    {
        // Do something with this.loader.Metric...
    }
}

因此,您的属性注入示例有两个问题:

  1. 您没有在Unity容器中注册Loader类型。
  2. 您使用了错误的属性。应该是[Dependency],而不是[Dynamic]

请注意,由于所有依赖IMetric的类型都应引用ILoader而不是Loader的引用(否则它不能被交换或嘲笑)。但是,如果依赖ILoader的类需要访问IMetric,则ILoader必须公开IMetric作为其接口的一部分。我不建议您像上面那样做,将IMetric注入依赖于它的每个类的类构造函数会更有意义。我只是在做上述操作,向您展示属性注入的工作原理,但这并不是我在大多数情况下建议的设计选择。

推荐方式

以下是使用“常规”依赖项注入技术的示例:

using System;
using Unity;
class Program
{
    static void Main(string[] args)
    {
        // Composition Root
        var container = new UnityContainer();
        container.RegisterType<IMetric, Metric>();
        container.RegisterType<IApplication, Application>();

        // Application (runtime)

        // Note that in a console application, you generally only call 
        // container.Resolve() once followed by a method to set things 
        // in motion. The type you resolve here should represent the
        // ENTIRE console application, and you would typically pass 
        // the args (if used) through to that class to process them. 
        // No business logic should go here, only code to read config files,
        // register types, and set the application in motion.
        var app = container.Resolve<IApplication>(); // Application and Metric get instantiated here...
        app.Run(args);
    }
}
public interface IMetric
{ }

public class Metric : IMetric
{ }

public interface IApplication
{
    void Run(string[] args);
}

public class Application : IApplication
{
    private readonly IMetric metric;
    public Application(IMetric metric) // Constructor Injection
    {
        this.metric = metric ?? throw new ArgumentNullException(nameof(metric));
    }
    public void Run(string[] args)
    {
        // Do something with this.metric...
    }
}

请注意,如果使用构造函数注入,则可以完全消除Loader类型(假设您可以不使用它)。您还可以删除factory方法,这使代码更简单。