SimpleInjector — зарегистрировать тип для всех его интерфейсов

Можно ли зарегистрировать тип для всех реализующих его интерфейсов? Например, у меня есть:

public class Bow : IWeapon
{

    #region IWeapon Members

    public string Attack()
    {
        return "Shooted with a bow";
    }

    #endregion
}

public class HumanFighter 

{
    private readonly IWeapon weapon = null;
    public HumanFighter(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public string Fight()
    {

        return this.weapon.Attack();
    }

}

    [Test]
    public void Test2b()
    {
        Container container = new Container();
        container.RegisterSingle<Bow>();
        container.RegisterSingle<HumanFighter>();

        // this would match the IWeapon to the Bow, as it
        // is implemented by Bow
        var humanFighter1 = container.GetInstance<HumanFighter>(); 

        string s = humanFighter1.Fight();
    }

person Karl Cassar    schedule 18.10.2012    source источник


Ответы (1)


Это полностью зависит от ваших потребностей, но обычно вам нужно использовать нестандартный метод регистрации Container. Вы можете определить свои собственные запросы LINQ для запроса метаданных приложения, чтобы получить правильные типы, и зарегистрировать их, используя неуниверсальные методы регистрации. Вот пример:

var weaponsAssembly = typeof(Bow).Assembly;

var registrations =
    from type in weaponsAssembly.GetExportedTypes()
    where type.Namespace.Contains(".Weapons")
    from service in type.GetInterfaces()
    select new { Service = service, Implementation = type };

foreach (var reg in registrations)
{
    container.Register(reg.Service, reg.Implementation);
}

Если вам нужно пакетно зарегистрировать набор реализаций на основе общего универсального интерфейса, вы можете использовать RegisterManyForOpenGeneric:

// include the SimpleInjector.Extensions namespace.

container.RegisterManyForOpenGeneric(typeof(IValidator<>),
    typeof(IValidator<>).Assembly);

Это будет искать все (не общие) общедоступные типы в предоставленной сборке, которые реализуют IValidator<T>, и регистрировать каждый из них по их закрытой универсальной реализации. Если тип реализует несколько закрытых универсальных версий IValidator<T>, все версии будут зарегистрированы. Взгляните на следующий пример:

interface IValidator<T> { }
class MultiVal1 : IValidator<Customer>, IValidator<Order> { }
class MultiVal2 : IValidator<User>, IValidator<Employee> { }

container.RegisterManyForOpenGeneric(typeof(IValidator<>),
    typeof(IValidator<>).Assembly);

Предполагая данный интерфейс и определения классов, показанная регистрация RegisterManyForOpenGeneric эквивалентна следующей ручной регистрации:

container.Register<IValidator<Customer>, MultiVal1>();
container.Register<IValidator<Order>, MultiVal1>();
container.Register<IValidator<User>, MultiVal2>();
container.Register<IValidator<Employee>, MultiVal2>();

Также было бы легко добавить удобные методы расширения. Возьмем, к примеру, следующий метод расширения, который позволяет зарегистрировать одну реализацию всеми ее реализованными интерфейсами:

public static void RegisterAsImplementedInterfaces<TImpl>(
    this Container container)
{
    foreach (var service in typeof(TImpl).GetInterfaces())
    {
        container.Register(service, typeof(TImpl));
    }
}

Его можно использовать следующим образом:

container.RegisterAsImplementedInterfaces<Sword>();
person Steven    schedule 18.10.2012
comment
Я хотел бы сделать автоматическую проводку для используемых компонентов - я могу сделать это с помощью отражения, но есть ли что-нибудь, что может автоматически регистрировать тип и автоматически подключать его для всех его интерфейсов реализации? - person Karl Cassar; 18.10.2012
comment
Пакетная регистрация поддерживается по умолчанию для универсальных типов, но не для неуниверсальных интерфейсов. Это обдуманное дизайнерское решение. Подробнее об этом можно узнать здесь. Он расскажет вам, почему он не поддерживается и как это сделать с помощью отражения. Если у вас есть какие-либо проблемы с этим, пожалуйста, дайте мне знать. Я буду рад помочь вам. - person Steven; 18.10.2012
comment
Я решил это с помощью отражения. Однако я хотел знать, можно ли это сделать изначально, как вы могли бы сделать с некоторыми другими контейнерами Ioc. Опять же, это не большая проблема и может быть легко реализована с помощью отражения. - person Karl Cassar; 18.10.2012
comment
Вместо того, чтобы изучать совершенно новый API (и у каждого контейнера DI для этого есть свой довольно неуклюжий API), я хотел предоставить только минимальный API, который позволяет вам использовать запросы LINQ + отражение (язык и API, которые вы уже знаете). сделать это. Это также легко переносится на другие фреймворки, поскольку перезапись в другой контейнер DI будет тривиальной. Некоторым разработчикам нравится использовать сложные инструменты; Я не. Я люблю, чтобы все было просто :-) - person Steven; 19.10.2012