2014-06-27 10 views
6

Próbuję zacząć od SimpleInjector jako kontenera IOC i do tej pory jestem z niego całkiem zadowolony. Ale teraz utknąłem na problemie, którego nie mogę rozwiązać. Szukałem w SO i dokumentacji, ale wydaje się, że nie otrzymałem jeszcze odpowiedzi. Widziałem the howto doc from SimpleInjector, ale to nie obejmuje otwartych interfejsów ogólnych.SimpleInjector HowTo Rejestracja wielu otwartych interfejsów ogólnych do implementacji jednoogólnej

Mam dwa interfejsy generyczne, takie jak:

public interface IEventPublisher<TEvent> 
{ 
    void Publish(TEvent Event); 
} 
public interface IEventSubscriber<TEvent> 
{ 
    void Subscribe(Action<TEvent> CallBack); 
} 

i jeden otwarty rodzajowe realizację tych dwóch:

class EventMediator<T> : IEventPublisher<T>, IEventSubscriber<T> 
{ 
    List<Action<T>> Subscriptions = new List<Action<T>>(); 

    public void Publish(T Event) 
    { 
     foreach (var Subscription in this.Subscriptions) 
      Subscription.Invoke(Event); 
    } 

    public void Subscribe(Action<T> CallBack) 
    { 
     this.Subscriptions.Add(CallBack); 
    } 
} 

W mojej aplikacji mam ustanowienia SimpleInjector tak:

this.Container = new SimpleInjector.Container(); 
this.Container.RegisterOpenGeneric(typeof(IEventPublisher<>), typeof(EventMediator<>), Lifestyle.Singleton); 
this.Container.RegisterOpenGeneric(typeof(IEventSubscriber<>), typeof(EventMediator<>), Lifestyle.Singleton); 
this.Container.Verify(); 

To, co próbuję archiwizować, to: ke, aby uzyskać dokładnie to samo wystąpienie, gdy poprosimy o IEventPublisher lub IEventSubscriber. A ponadto tym przypadku powinna być pojedyncza dla każdego T.

mam przetestowane z tych linii:

class DummyEvent {} 

var p = this.Container.GetInstance<IEventPublisher<DummyEvent>>(); 
var s = this.Container.GetInstance<IEventSubscriber<DummyEvent>>(); 
var areSame = (object.ReferenceEquals(p,s)); 

Niestety p i s nie odnoszą się do tej samej instancji. Czy zdarzyło się, że ktoś zna rozwiązanie tego problemu?

Odpowiedz

5

Istnieją pewne rozwiązania dla tego, oto jedno: Tworzenie oddzielnych implementacje dla IEventPublisher<T> i IEventSubscriber<T> i niech przekazać EventMediator<T>. Na przykład z tych wdrożeń:

public class EventPublisher<TEvent> : IEventPublisher<TEvent> 
{ 
    private readonly EventMediator<TEvent> mediator; 
    public EventPublisher(EventMediator<TEvent> mediator) { 
     this.mediator = mediator; 
    } 

    public void Publish(TEvent Event) { 
     this.mediator.Publish(Event); 
    } 
} 

public class EventSubscriber<TEvent> : IEventSubscriber<TEvent> 
{ 
    private readonly EventMediator<TEvent> mediator; 
    public EventSubscriber(EventMediator<TEvent> mediator) { 
     this.mediator = mediator; 
    } 

    public void Subscribe(Action<TEvent> CallBack) { 
     this.mediator.Subscribe(Callback); 
    } 
} 

Teraz dokonać rejestracji w następujący sposób:

container.RegisterSingleOpenGeneric(typeof(EventMediator<>), typeof(EventMediator<>)); 
container.RegisterSingleOpenGeneric(typeof(IEventPublisher<>), typeof(EventPublisher<>)); 
container.RegisterSingleOpenGeneric(typeof(IEventSubscriber<>), typeof(EventSubscriber<>)); 

Teraz zarówno EventPublisher<DummyEvent> i EventSubscriber<DummyEvent> wskaże w tym samym EventMediator<DummyEvent> instancji.

Innym sposobem na osiągnięcie tego bez dodatkowego typu jest użycie zdarzenia ResolveUnregisteredType (właśnie to wykorzystuje metoda rozszerzenia RegisterOpenGeneric pod okładkami).Twoja konfiguracja będzie wyglądać następująco:

container.RegisterSingleOpenGeneric(typeof(EventMediator<>), typeof(EventMediator<>)); 

container.ResolveUnregisteredType += (s, e) => 
{ 
    if (e.UnregisteredServiceType.IsGenericType) 
    { 
     var def = e.UnregisteredServiceType.GetGenericTypeDefinition(); 

     if (def == typeof(IEventPublisher<>) || def == typeof(IEventSubscriber<>)) 
     { 
      var mediatorType = typeof(EventMediator<>) 
       .MakeGenericType(e.UnregisteredServiceType.GetGenericArguments()[0]); 
      var producer = container.GetRegistration(mediatorType, true); 
      e.Register(producer.Registration); 
     } 
    } 
}; 

Można nawet wyodrębnić ten kod do bardziej ogólnej metody rozszerzenia. W ten sposób Twoja rejestracja będzie wyglądać następująco:

container.RegisterSingleOpenGeneric(typeof(EventMediator<>), typeof(EventMediator<>)); 
container.ForwardOpenGenericTo(typeof(IEventPublisher<>), typeof(EventMediator<>)); 
container.ForwardOpenGenericTo(typeof(IEventSubscriber<>), typeof(EventMediator<>)); 

Metoda rozszerzenie będzie wyglądać następująco:

public static void ForwardOpenGenericTo(this Container container, 
    Type openGenericServiceType, Type openGenericServiceTypeToForwardTo) 
{ 
    container.ResolveUnregisteredType += (s, e) => 
    { 
     var type = e.UnregisteredServiceType; 
     if (type.IsGenericType) 
     { 
      if (type.GetGenericTypeDefinition() == openGenericServiceType) 
      { 
       var forwardToType = openGenericServiceTypeToForwardTo.MakeGenericType(
        type.GetGenericArguments()); 
       var producer = container.GetRegistration(forwardToType, true); 
       e.Register(producer.Registration); 
      } 
     } 
    }; 
} 
+2

Witaj Steven, dziękuję bardzo. To faktycznie działa i rozwiązuje mój problem, chociaż wyobrażałem sobie, że będzie sposób archiwizowania tego bez tworzenia oddzielnych implementacji. – Kai

+0

Cudowne, okrzyki za aktualizację – Kai

+0

@ka: ale zauważ, że zgadzam się z Qujck na temat rozdziału w twoim projekcie. Myślę, że twój projekt poprawia się wraz z jego sugestią. – Steven

3

Rejestrujesz się IEventPublisher i IEventSubscriber jako oddzielne pojedynki. Będziesz musiał zmienić kod w taki czy inny sposób. Jednym rozwiązaniem jest oddzielenie 3 Obowiązki swojej mediatora:

Abonent

public interface IEventSubscriber<TEvent> 
{ 
    void Subscribe(Action<TEvent> CallBack); 
} 

public class EventSubscriber<T> : IEventSubscriber<T> 
{ 
    public readonly ISubscriptions<T> subscriptions; 

    public EventSubscriber(ISubscriptions<T> subscriptions) 
    { 
     this.subscriptions = subscriptions; 
    } 

    public void Subscribe(Action<T> CallBack) 
    { 
     this.subscriptions.Add(CallBack); 
    } 
} 

Wydawca

public interface IEventPublisher<TEvent> 
{ 
    void Publish(TEvent Event); 
} 

public class EventPublisher<T> : IEventPublisher<T> 
{ 
    public readonly ISubscriptions<T> subscriptions; 

    public EventPublisher(ISubscriptions<T> subscriptions) 
    { 
     this.subscriptions = subscriptions; 
    } 

    public void Publish(T Event) 
    { 

     foreach (var subscription in this.subscriptions) 
     { 
      subscription.Invoke(Event); 
     } 
    } 
} 

W Subskrypcje

public interface ISubscriptions<T> : IList<Action<T>> { } 

public class Subscriptions<T> : List<Action<T>>, ISubscriptions<T> { } 

subskrypcje wystarczy być zarejestrowany jako pojedyncza

var container = new Container(); 
container.RegisterOpenGeneric(typeof(IEventSubscriber<>), typeof(EventSubscriber<>)); 
container.RegisterOpenGeneric(typeof(IEventPublisher<>), typeof(EventPublisher<>)); 
container.RegisterSingleOpenGeneric(typeof(ISubscriptions<>), typeof(Subscriptions<>)); 
container.Verify(); 

var p = container.GetInstance<IEventPublisher<DummyEvent>>(); 
var s = container.GetInstance<IEventSubscriber<DummyEvent>>(); 
Assert.That(
    (p as EventPublisher<DummyEvent>).subscriptions == 
    (s as EventSubscriber<DummyEvent>).subscriptions); 
+1

Hi qujck. Twoje podejście jest podobne do Stevensa, chociaż jest nieco czystsze dzięki rozdzieleniu obaw. – Kai

Powiązane problemy