Unity IOC Упростите регистрацию типов с помощью InjectionConstructor - PullRequest
0 голосов
/ 27 сентября 2018

Я бы хотел упростить регистрацию типов с помощью конструктора Injection.МОК должен обычно разрешать типы самостоятельно.Некоторые типы не могут быть разрешены сами по себе автоматически, потому что их конструктору нужен некоторый именованный параметр.Им нужна таможенная регистрация.

Например:

public DatabaseLayer(string connectionString, 
                     string userName,
                     ILogger loger, 
                     TypeMapper mapper,
                     ...some other dependencies)

Тип регистрации:

container.RegisterType<DatabaseLayer>(
    new InjectionConstructor(
        new ResolvedParameter<string>("connectionString"),
        new ResolvedParameter<string>("userName"),
        new ResolvedParameter<ILoger>("file")
        typeof(TypeMapper),
        typeof(..),
        ...));

Как видите, если вам нужны какие-либо именованные разрешения в вашей регистрации, вам нужно записать десяткибезымянный параметр или приложение падает во время выполнения.И что еще хуже, вам нужно корректировать свою регистрацию каждый раз, когда вы меняете параметр конструктора.Вы ничего не заметите, пока не произойдет сбой программы во время выполнения.

Поэтому я ищу расширение или способ настройки Unity, который позволил бы мне пропустить регистрацию параметров по умолчанию (без имени), чтобы уменьшить возможные ошибки и сохранить загрузчик каккак можно меньше.

Редактировать Хорошо найдено то, что я ищу.https://outlawtrail.wordpress.com/2012/08/02/fun-with-constructor-arguments-part-1-pick-choose/

умный конструктор кажется решением

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Хорошо, еще несколько вариантов, которые помогут вам улучшить ваш код.Предполагая следующую модель:

public class DataBaseLayer
{
    private readonly string _connectionString;
    private readonly string _userName;
    private readonly ILogger _logger;
    private readonly ITypeMapper _mapper;

    public DataBaseLayer(
        string connectionString,
        string userName,
        ILogger logger,
        ITypeMapper mapper)
    {
        _connectionString = connectionString;
        _userName = userName;
        _logger = logger;
        _mapper = mapper;
    }
}

public interface ITypeMapper
{
}

public class TypeMapper : ITypeMapper
{
}

public interface ILogger
{

}

public class ConsoleLogger : ILogger
{
}

public class FileLogger : ILogger
{

}

1) Уменьшите количество магических строк в коде

Первым делом попытайтесь уменьшить количество магических строк в вашем коде, например:"connectionString" и "userName":

container.RegisterInstance(typeof(string), "userName", "my username");
container.RegisterInstance(typeof(string), "connectionString", "my connection string");

Таким образом, вместо повторения подобных вещей по всему коду ...

new InjectionConstructor(
    new ResolvedParameter<string>("connectionString"),
    new ResolvedParameter<string>("userName"),
    /* .... */
);

Вы можете создавать свои собственные классы параметров, которые выводятс ResolveParameter:

public class UsernameParameter : ResolvedParameter<string>
{
    public static UsernameParameter Instance => new UsernameParameter();

    public static string ParameterName => "userName";

    private UsernameParameter() : base(ParameterName)
    {
    }
}

public class ConnectionStringParameter : ResolvedParameter<string>
{
    public static ConnectionStringParameter Instance => new ConnectionStringParameter();

    public static string ParameterName => "connectionString";

    private ConnectionStringParameter() : base(ParameterName)
    {
    }
}

Зачем беспокоиться?Таким образом, вам не нужно повторять строки "connectionString" и "userName", и если вам нужно изменить их, все, что вам нужно сделать, это отредактировать свойство ParameterName в соответствующем классе.

Регистрация строк с использованием ConnectionStringParameter и UsernameParameter:

container.RegisterInstance(typeof(string), UsernameParameter.ParameterName, "my username");
container.RegisterInstance(typeof(string), ConnectionStringParameter.ParameterName, "my connection string");

Использование параметров:

new InjectionConstructor(
    ConnectionStringParameter.Instance,
    UsernameParameter.Instance,
    /* .... */
);

2) Использовать методы расширения или фабрикичтобы удалить дублирование кода

Например, предположим, что вы хотите зарегистрировать различные экземпляры DataBaseLayer.Единственная разница между этими экземплярами - тип используемого регистратора.Регистрация экземпляров ILogger:

container.RegisterType<ILogger, FileLogger>("file");
container.RegisterType<ILogger, ConsoleLogger>("console");

Регистрация экземпляров DataBaseLayer с использованием метода расширения:

container.RegisterDataBaseLayer(instanceName: "fileDatabaseLayer", loggerType: "file");
container.RegisterDataBaseLayer(instanceName: "consoleDatabaseLayer", loggerType: "console");

Реализация метода расширения RegisterDataBaseLayer:

public static class UnityContainerExtensions
{
    public static IUnityContainer RegisterDataBaseLayer(
        this IUnityContainer container, 
        string instanceName, 
        string loggerType)
    {
        container.RegisterType<DataBaseLayer>(
            instanceName,
            new InjectionConstructor(
                ConnectionStringParameter.Instance,
                UsernameParameter.Instance,
                new ResolvedParameter<ILogger>(loggerType),
                new ResolvedParameter<ITypeMapper>()
            )
        );

        return container;
    }
}

3) Если возможно, сгруппируйте параметры

Вместо ....

public class FooService
{
    public FooService(string userName, string password)
    {
        // ...
    }
}

Do:

public class FooService
{
    public FooService(Credentials credentials)
    {
        // ...
    }
}

public class Credentials
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Это будетупростите свою жизнь при регистрации типов с использованием контейнера IoC и сократите код Bootstrapper.

4) Создайте модульные тесты для своего контейнера Bootstrapper / IoC

Это чрезвычайно важно и может избавить вас от головной боли,Если вы чувствуете, что ваш код хрупкий и может произойти сбой, если / когда класс будет изменен (например, при изменении или добавлении нового конструктора), то вам следует добавить несколько тестов для каждого зарегистрированного типа и / или именованного экземпляра.

Вы должны по крайней мере проверить, может ли контейнер разрешить конкретный экземпляр, без выдачи исключений.

0 голосов
/ 27 сентября 2018

Один из вариантов - использовать InjectionFactory вместо InjectionConstructor.

При условии, что эта модель:

public class DataBaseLayer
{
    private readonly string _connectionString;
    private readonly string _userName;
    private readonly ILogger _logger;
    private readonly ITypeMapper _mapper;

    public DataBaseLayer(
        string connectionString,
        string userName,
        ILogger logger,
        ITypeMapper mapper)
    {
        _connectionString = connectionString;
        _userName = userName;
        _logger = logger;
        _mapper = mapper;
    }
}

public interface ITypeMapper
{
}

public class TypeMapper : ITypeMapper
{
}

public interface ILogger
{

}

public class Logger : ILogger
{

}

И при условии, что Username и ConnectionString хранятся вфайл конфигурации (вы не указали, откуда поступают эти параметры), я бы создал Bootstrapper класс, подобный следующему:

public static class Bootstrapper
{
    // assuming Username is a configuration setting stored in the config file
    private static string Username => ConfigurationManager.AppSettings["username"];

    // assuming ConnectionString is a connection string stored in the config file
    private static string ConnectionString => ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;

    public static IUnityContainer Setup()
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<ILogger, Logger>();
        container.RegisterType<ITypeMapper, TypeMapper>();
        container.RegisterType<DataBaseLayer>(new InjectionFactory(CreateDataBaseLayer));

        return container;
    }

    private static DataBaseLayer CreateDataBaseLayer(IUnityContainer container)
    {
        ILogger logger = container.Resolve<ILogger>();
        ITypeMapper mapper = container.Resolve<ITypeMapper>();

        return new DataBaseLayer(ConnectionString, Username, logger, mapper);
    }
}

Используя код:

IUnityContainer container = Bootstrapper.Setup();
DataBaseLayer dbLayer = container.Resolve<DataBaseLayer>(); 
...