2015-01-12 5 views
3

Этот вопрос возникает из-за того, что я пытаюсь создать реализацию простого инжектора для MediatR: https://github.com/jbogard/MediatR/pull/14.Посредник и контравариантность с простым инжектором

У меня возникли проблемы при попытке решения реализаций интерфейса общего обработчика. Рассмотрим следующий интерфейс обработчика уведомлений:

public interface INotificationHandler<in TNotification> 
    where TNotification : INotification 
{ 
    void Handle(TNotification notification); 
} 

INotifcation просто пустой интерфейс маркера.

я определил следующие обработчики для Pinged (который реализует INotification) событие:

public class PingedHandler : INotificationHandler<Pinged> 
{ 
    public void Handle(Pinged notification) { } 
} 

public class PingedHandler2 : INotificationHandler<Pinged> 
{ 
    public void Handle(Pinged notification) { } 
} 

А также общий обработчик (обратите внимание, это должно обрабатывать каждый INotification):

public class GenericHandler : INotificationHandler<INotification> 
{ 
    public void Handle(INotification notification) { } 
} 

со следующими Регистрация:

var container = new Container(); 

container.RegisterManyForOpenGeneric(
    typeof (INotificationHandler<>), 
    (service, impls) => container.RegisterAll(service, impls), 
    AppDomain.CurrentDomain.GetAssemblies()); 

Теперь я ожидаю:

GetAllInstances<INotificationHandler<Pinged>>(); 

разрешить как PingedHandler и PingedHandler2, которые он делает. Но он не разрешает GenericHandler, поскольку он реализует INotificationHandler<INotification>, а не INotificationHandler<Pinged>. Интересно, есть ли способ позволить Simple Injector искать весь объектный граф и разрешать что-нибудь, то есть Pinged тоже.

Я нашел a blog post от Стивена о совпадении и противоречивости, но я не могу заставить его работать для моего примера.

+0

'INotificationHandler 'не совместим с' INotificationHandler ', поэтому это не удивительно. Если вы хотите, чтобы все обработчики пытались использовать 'GetAllInstances >'? – Lee

+0

@Lee 'container.GetAllInstances >()' разрешает только «GenericHandler», что я и ожидаю от него. Я хочу, чтобы «GenericHandler» всегда был разрешен, так как каждое уведомление должно внедрять «INotification». –

+1

@ Ли, но 'INotificationHandler ' _is_ совместим с 'INotificationHandler '. –

ответ

2

tl; dr: это была ошибка/недостаток в Simple Injector v2.6.0, которая исправлена ​​в версии 2.2.0. Конфигурация в вопросе (и показано ниже) теперь работает.


Резюмируя ответ @ qujck в и @ комментарий Стивена: Я был в состоянии получить работу по установке Simple Injector v2.7.0-бета2 со следующей конфигурацией (фактически то же самое, как и в вопросе):

// Simple Injector v3.x 
container.RegisterCollection(typeof(INotificationHandler<>), 
    AppDomain.CurrentDomain.GetAssemblies()); 

// Simple Injector v2.x 
container.RegisterManyForOpenGeneric(
    typeof(INotificationHandler<>), 
    container.RegisterAll, 
    AppDomain.CurrentDomain.GetAssemblies()); 

Теперь Simple Инжектор способен решить PingedHandler, PingedHandler2иGenericHandler при запросе:

container.GetAllInstances<INotificationHandler<Pinged>>(); 
+0

Я обновил ваш ответ для v2.7.0-beta2. Ошибка в 2.7.0-beta1 заставляла регистрацию становиться излишне сложной. Теперь регистрация - это то, что вы должны ожидать. – Steven

+0

Спасибо за это @Steven! Когда вы планируете выпустить это? –

+0

Henk, Simple Injector 2.7 был выпущен. – Steven

3

UPDATE

Как Простого выпуска Injector 2.7 this functionality теперь является стандартным, и вы больше не требуется обходной путь, показанный ниже.


Вы пропускаете вариацию MultipleDispatchEventHandler, описанные в этой статье. Принимая основную логику и применяя их к абстракциям действительно дает результат, который вы ожидаете:

[Fact] 
public void RegisterAll_Contravariant_Succeeds() 
{ 
    var container = new Container(); 

    container.RegisterManyForOpenGeneric(
     typeof(INotificationHandler<>), 
     (service, impls) => container.RegisterAll(service, impls), 
     AppDomain.CurrentDomain.GetAssemblies()); 

    var handlersType = typeof(IEnumerable<INotificationHandler<Pinged>>); 

    var handlersCollection = (
     from r in container.GetCurrentRegistrations() 
     where handlersType.IsAssignableFrom(r.ServiceType) 
     select r.GetInstance()) 
     .Cast<IEnumerable<INotificationHandler<Pinged>>>() 
     .ToArray(); 

    var result = 
     from handlers in handlersCollection 
     from handler in handlers 
     select handler; 

    Assert.Equal(3, result.Count()); 
} 

Но это может быть проще сделать GenericHandler родовое:

public class GenericHandler<TNotification> : INotificationHandler<TNotification> 
    where TNotification : INotification 
{ 
    public void Handle(TNotification notification) { } 
} 

Предоставление простой регистрации

[Fact] 
public void RegisterAll_WithOpenAndClosedGenerics_Succeeds() 
{ 
    var container = new Container(); 

    var types = OpenGenericBatchRegistrationExtensions 
     .GetTypesToRegister(
      container, 
      typeof(INotificationHandler<>), 
      AccessibilityOption.AllTypes, 
      AppDomain.CurrentDomain.GetAssemblies()) 
     .ToList(); 

    types.Add(typeof(GenericHandler<>)); 

    container.RegisterAll(typeof(INotificationHandler<>), types); 

    var result = container.GetAllInstances<INotificationHandler<Pinged>>().ToList(); 

    Assert.Equal(3, result.Count()); 
} 
Смежные вопросы