2014-06-27 12 views
6

Sto cercando di iniziare con SimpleInjector come contenitore IOC e fino ad ora sono abbastanza soddisfatto. Ma in questo momento sono bloccato su un problema che non riesco a risolvere. Ho cercato su SO e nella documentazione, ma sembra che non ci sia ancora risposta. Ho visto the howto doc from SimpleInjector ma questo non copre le interfacce generiche aperte.SimpleInjector HowTo Registrazione di più interfacce generiche aperte per una singola implementazione generica

Ho due interfacce generiche come questi:

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

E uno aperto implementazione generica per quei due:

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); 
    } 
} 

nella mia applicazione Sono la creazione di SimpleInjector come questo:

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(); 

Quello che sto cercando di archiviare è: I'd li per ottenere esattamente la stessa istanza quando si richiede un IEventPublisher o un IEventSubscriber. E inoltre questo grado è un Singleton per qualsiasi T.

Ho provato questo con queste righe:

class DummyEvent {} 

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

p e s Purtroppo non si riferiscono alla stessa istanza. Qualcuno capita di sapere una soluzione a questo problema?

risposta

5

Ci sono alcune soluzioni per questo, eccone uno: creare implementazioni separate per IEventPublisher<T> e IEventSubscriber<T> e far loro delegare alla EventMediator<T>. Per esempio con queste implementazioni:

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); 
    } 
} 

Ora si effettua la registrazione come segue:

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

Ora sia il EventPublisher<DummyEvent> e EventSubscriber<DummyEvent> punterà allo stesso EventMediator<DummyEvent> istanza.

Un altro modo per ottenere ciò senza il tipo in più consiste nell'utilizzare l'evento ResolveUnregisteredType (che è quello che utilizza il metodo di estensione RegisterOpenGeneric sotto le copertine).La configurazione sarà simile a questa:

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); 
     } 
    } 
}; 

si potrebbe anche estrarre questo codice in un metodo di estensione più generale. In questo modo la tua registrazione sarà simile a questa:

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

Il metodo di estensione sarebbe simile a questa:

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

Ciao Steven, grazie mille. Questo in realtà funziona e risolve il problema anche se avevo immaginato che sarebbe stato possibile archiviarlo senza creare implementazioni separate. – Kai

+0

meraviglioso, evviva l'aggiornamento – Kai

+0

@ka: ma nota che sono d'accordo con Qujck sulla separazione nel tuo progetto. Penso che il tuo design migliori con il suo suggerimento. – Steven

3

Si sta registrando IEventPublisher e IEventSubscriber come singleton separati. Dovrai rifattorizzare il tuo codice in un modo o nell'altro. Una soluzione è quella di separare le 3 responsabilità del vostro mediatore:

Il Subscriber

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); 
    } 
} 

Il Editore

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); 
     } 
    } 
} 

I Sottoscrizioni

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

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

Solo le sottoscrizioni devono essere registrati come Singleton

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. Il tuo approccio è molto simile a Stevens, anche se è un po 'più pulito attraverso la separazione delle preoccupazioni. – Kai

Problemi correlati