Domanda

Ho cercato di ottenere NServiceBus per lavorare con Ninject 2.0 come il contenitore IoC sottostante senza successo. Mentre posso realizzare l'integrazione di base, ho avuto problemi con i messaggi "fantasma" sempre inviati ai vari abbonati. Ho usato l'attuazione Autofac come modello di sorta, sostituendo i pezzi necessari con il codice Ninject-specifica. Inoltre, ho dovuto creare un'euristica personalizzato per ottenere l'iniezione auto-immobile a verificarsi.

In ogni caso, il comportamento che vedo è che un primo messaggio può essere pubblicato e con successo letta da un abbonato; tuttavia il messaggio successivo che viene pubblicato i risultati nel messaggio di essere "ricevuto" per tre volte.

Quindi, mi chiedo: C'è qualcuno che sta facendo qualcosa con Ninject come NServiceBus ObjectBuilder? Oppure, qualcuno ha visto e corretto questo comportamento durante l'integrazione degli altri contenitori IoC attualmente in bundle con NServiceBus 2.0 (vale a dire Windsor, StructureMap o Autofac).

Modifica Ho fatto dare un'occhiata a questo ma non sembrava completa e ho pensato che l'euristica per l'iniezione di proprietà dovrebbe essere un po 'diverso.

È stato utile?

Soluzione 2

trovato la soluzione, anche se ho avuto due problemi.

Il primo problema derivava dal modo in cui sono stati registrati oggetto / configurato con il kernel Ninject nel metodo IContainer.Configure della mia classe NinjectObjectBuilder. Dopo aver esaminato le implementazioni esistenti di ObjectBuilder di NServiceBus utilizzando altri contenitori CIO, ho notato l'approccio generale alla registrazione era quello di registrare il tipo calcestruzzo stesso, così come tutte le interfacce del tipo attuato. In Ninject, ciò equivale a "legare il tipo concreto a se stesso" e quindi vincolante ciascuna interfaccia che implementa tipo al tipo pure. Questo è stato abbastanza semplice, ad eccezione di quello che stavo trovando dopo profilatura con dotTrace era che, nel caso di attivazioni Singleton, non sembrava che stavo veramente diventando riferimenti Singleton. In realtà, ciò che accadrebbe è che vorrei ottenere è stato richiesto un nuovo oggetto a seconda del tipo di servizio. Ad esempio, il tipo concreto UnicastBus implementa IBus così come IStartableBus ed è registrata con l'ambito Singleton. NServiceBus si aspetta di ricevere lo stesso oggetto se è richiesto un IBus o IStartableBus, se sono single e sia "legato" alla stessa implementazione. L'interpretazione di Ninject di Singleton sembra essere per quanto riguarda il servizio o l'interfaccia - in altre parole, si ottiene la stessa istanza di un UnicastBus ogni volta che si richiede un IBus; tuttavia, si riceve un nuovo, UnicastBus diverso per una richiesta di IStartableBus. Il modo risolto questo era implementare il metodo IContainer.Configure come segue:

void IContainer.Configure(Type concreteComponent, 
                                 ComponentCallModelEnum callModel) {

  if (HasComponent(concreteComponent))
    return;

  var services = concreteComponent.GetAllServices()
    .Where(t => t != concreteComponent);

  var instanceScope = GetInstanceScopeFrom(callModel);
  // Bind the concrete type to itself ...
  kernel
    .Bind(concreteComponent)
    .ToSelf()
    .InScope(instanceScope);

  // Bind "aliases" to the binding for the concrete component
  foreach (var service in services)
    kernel
      .Bind(service)
      .ToMethod(ctx => ctx.Kernel.Get(concreteComponent))
      .InScope(instanceScope);
}

Che ha risolto il problema di risolvere casi Singleton in modo coerente con le aspettative di NServiceBus. Tuttavia, ho ancora avuto un problema di ricezione di messaggi "fantasma" nei miei gestori. Dopo pettinatura attraverso i file di log log4net, profiling e, infine, la lettura del problema come discusso qui e qui . Il problema deriva dal specifcially gestori eventi mutliple essendo attaccata durante l'iniezione struttura. Nello specifico, il problema è dovuta alla UnicastBus avente proprietà è impostata Transport mutliple volte. Ecco il frammento di codice da UnicastBus.cs nella base di codice NServiceBus:

public virtual ITransport Transport
{
  set
  {
    transport = value;

    transport.StartedMessageProcessing += TransportStartedMessageProcessing;
    transport.TransportMessageReceived += TransportMessageReceived;
    transport.FinishedMessageProcessing += TransportFinishedMessageProcessing;
    transport.FailedMessageProcessing += TransportFailedMessageProcessing;
  }

}

Dopo averci pensato, mi sono chiesto perché questa struttura è stata stato impostato più volte. UnicastBus è registrato in ambito singleton NServiceBus, e avevo appena fissato quel problema come discusso sopra. Risulta, Ninject, quando si attiva un oggetto - nuove o da esso è della cache interna - continua a guardare per iniettare le proprietà dell'oggetto. Chiamerà classi euristiche iniezione registrati con il suo contenitore interno componente del kernel e in base alla loro risposta (cioè il risultato della chiamata al loro attuazione bool ShouldInject(MemberInfo member),) iniettare le proprietà prima di ogni attivazione. Quindi, la soluzione era quella di evitare che Ninject di eseguire l'iniezione di proprietà sulle istanze che erano state precedentemente attivate ed erano single. La mia soluzione era quella di creare una nuova strategia di iniezione proprietà che tenuto traccia delle istanze precedentemente attivate, che non sono stati transitori di portata e saltare la strategia di iniezione proprietà predefinita per le richieste di attivazione di tali superamenti. La mia strategia è simile al seguente:

/// <summary>
/// Only injects properties on an instance if that instance has not 
/// been previously activated.  This forces property injection to occur 
/// only once for instances within a scope -- e.g. singleton or within 
/// the same request, etc.  Instances are removed on deactivation.
/// </summary>
public class NewActivationPropertyInjectStrategy : PropertyInjectionStrategy {
  private readonly HashSet<object> activatedInstances = new HashSet<object>();

  public NewActivationPropertyInjectStrategy(IInjectorFactory injectorFactory)
    : base(injectorFactory) { }

  /// <summary>
  /// Injects values into the properties as described by 
  /// <see cref="T:Ninject.Planning.Directives.PropertyInjectionDirective"/>s
  /// contained in the plan.
  /// </summary>
  /// <param name="context">The context.</param>
  /// <param name="reference">A reference to the instance being 
  /// activated.</param>
  public override void Activate(IContext context, 
                                         InstanceReference reference) {

    if (activatedInstances.Contains(reference.Instance)) 
      return;    // "Skip" standard activation as it was already done!

    // Keep track of non-transient activations...  
    // Note: Maybe this should be 
    //       ScopeCallback == StandardScopeCallbacks.Singleton
    if (context.Binding.ScopeCallback != StandardScopeCallbacks.Transient)
      activatedInstances.Add(reference.Instance);

    base.Activate(context, reference);
  }

  /// <summary>
  /// Contributes to the deactivation of the instance in the specified context.
  /// </summary>
  /// <param name="context">The context.</param>
  /// <param name="reference">A reference to the instance being 
  /// deactivated.</param>
  public override void Deactivate(IContext context, 
                                  InstanceReference reference) {

    activatedInstances.Remove(reference.Instance);
    base.Deactivate(context, reference);
  }
}

La mia applicazione sta lavorando. L'unica altra sfida che ho avuto è stata la strategia di attivazione esistente "sostituzione" per l'iniezione di proprietà. Ho pensato di creare un kernel personalizzato (e che può essere il modo migliore per andare); tuttavia, quindi non sono in grado disostenere un kernel "pre-configurato" per NServiceBus. Per ora ho un metodo di estensione che aggiunge i nuovi componenti a qualsiasi kernel Ninject.

public static void ConfigureForObjectBuilder(this IKernel kernel) {
  // Add auto inject heuristic
  kernel.Components.Add<IInjectionHeuristic, AutoInjectBoundPropertyTypeHeuristic>();

  // Replace property injection activation strategy...
  /* NOTE: I don't like this!  Thinking about replacing the pipeline component
   * in Ninject so that it's smarter and selects our new activation 
   * property inject strategy for components in the NServiceBus DLLs and 
   * uses the "regular strategy" for everything else.  Also, thinking of 
   * creating a custom kernel.
   */
  kernel.Components.RemoveAll<IActivationStrategy>();
  kernel.Components.Add<IActivationStrategy, 
                            NewActivationPropertyInjectStrategy>();
  // The rest of the "regular" Ninject strategies ...
  kernel.Components.Add<IActivationStrategy, MethodInjectionStrategy>();
  kernel.Components.Add<IActivationStrategy, InitializableStrategy>();
  kernel.Components.Add<IActivationStrategy, StartableStrategy>();
  kernel.Components.Add<IActivationStrategy, BindingActionStrategy>();
  kernel.Components.Add<IActivationStrategy, DisposableStrategy>();
}

Questo un mod a titolo definitivo, a questo punto, come non esiste un meccanismo sul contenitore componente del kernel per "sostituire" un componente esistente. E, dal momento che ho voluto ignorare il comportamento attuale della strategia di iniezione di proprietà, non ho potuto avere più di uno di quei particolari tipi di strategie nel kernel alla volta. L'altro problema con questa implementazione corrente è che tutti gli altri componenti IActivationStrategy personalizzati che potrebbero essere stati configurati verranno persi. Volevo scrivere codice che otterrebbe tutti i componenti IActivationStrategy in un elenco, rimuoverli dal kernel, sostituire la strategia di iniezione proprietà nell'elenco ho creato e quindi aggiungere tutti indietro nel kernel, così efficacemente la loro sostituzione. Tuttavia, il contenitore di componente del kernel supporta solo il metodo Add generico e non ho voglia di scrivere il codice funky per creare una chiamata dinamica.

** EDIT ** Dopo che ho postato ieri, ho deciso di gestire la strategia migliore. Ecco quello che ho fatto, bundling tutto in un metodo di estensione per configurare un Ninject Kernel:

public static void ConfigureForObjectBuilder(this IKernel kernel) {
  // Add auto inject heuristic
  kernel.Components.Add<IInjectionHeuristic, AutoInjectBoundPropertyTypeHeuristic>();

  // Get a list of all existing activation strategy types
  // with exception of PropertyInjectionStrategy
  var strategies = kernel.Components.GetAll<IActivationStrategy>()
    .Where(s => s.GetType() != typeof (PropertyInjectionStrategy))
    .Select(s => s.GetType())
    .ToList();
  // Add the new property injection strategy to list
  strategies.Add(typeof (NewActivationPropertyInjectStrategy));

  // Remove all activation strategies from the kernel
  kernel.Components.RemoveAll<IActivationStrategy>();

  // Add the list of strategies 
  var addMethod = kernel.Components.GetType().GetMethod("Add");
  strategies
    .ForEach(
    t => addMethod
           .MakeGenericMethod(typeof (IActivationStrategy), t)
           .Invoke(kernel.Components, null)
    );
}

Altri suggerimenti

C'è un filo discutere di questo sul gruppo NServiceBus, nessuna soluzione ancora però.

http://tech.groups.yahoo.com/group/nservicebus / messaggio / 6253

Hy Peter,

Ho trovato un approccio per scambiare dinamicamente le strategie (faccio questo nel costruttore della NinjectObjectBuilder):

            this.kernel.Bind<NewActivationPropertyInjectStrategy>().ToSelf().WithPropertyValue("Settings", ctx => ctx.Kernel.Settings);
        this.kernel.Bind<IInjectorFactory>().ToMethod(ctx => ctx.Kernel.Components.Get<IInjectorFactory>());

        this.kernel.Components.Get<ISelector>().InjectionHeuristics.Add(this.propertyHeuristic);

        IList<IActivationStrategy> activationStrategies = this.kernel.Components.Get<IPipeline>().Strategies;

        IList<IActivationStrategy> copiedStrategies = new List<IActivationStrategy>(
            activationStrategies.Where(strategy => !strategy.GetType().Equals(typeof(PropertyInjectionStrategy)))
            .Union(new List<IActivationStrategy> { this.kernel.Get<NewActivationPropertyInjectStrategy>() }));

        activationStrategies.Clear();
        copiedStrategies.ToList().ForEach(activationStrategies.Add);

Ecco un esempio di come utilizzare il NinjectObjectBuilder .

Questo esempio mostra un sito web attraverso l'invio di comandi NServiceBus ad un backend con Ninject iniezione di dipendenza sia per il sito web e il backend.

Ho copiato il NinjectObjectBuilder da repository ufficiale come legate da Daniel Marbach. Per quanto ne so il codice non è stato rilasciato come parte della versione mainstream del NServiceBus ancora. Voglio usare oggi così ho copiato il codice e linkato contro NServiceBus 2.6.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top