Внедрение свойств с использованием атрибута Unity [Dynamic] в консольном приложении не работает - PullRequest
0 голосов
/ 14 июня 2019
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 Ответ

1 голос
/ 14 июня 2019

Контейнеры для инъекций зависимостей (такие как 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...
    }
}

Итак, у вашего примера внедрения свойства есть 2 проблемы:

  1. Вы не регистрируете тип Loader в контейнере Unity.
  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 (при условии, что вы можете обходиться без него). Вы также можете удалить фабричный метод, что значительно упрощает код.

...