Anyone using Ninject 2.0 as an ObjectServiceBus ObjectBuilder?

I am trying to get nServiceBus to work with Ninject 2.0 as the underlying IoC container without success. Although I can achieve basic integration, I had problems with ghost messages sent to various subscribers. I used the Autofac implementation as a kind template, replacing the necessary fragments with Ninject-specific code. In addition, I had to create a custom heuristic to get an automatic injection of properties.

No matter what I see, the first message can be published and successfully read by the subscriber; however, the next message that is published causes the message to be “received” three times.

So I'm wondering: Does anyone do anything with Ninject as an nServiceBus ObjectBuilder? Or someone saw and fixed this behavior during the integration of other IoC containers currently associated with nServiceBus 2.0 (i.e. Windsor, StructureMap or Autofac).

Edit: I looked at this one , but it did not look complete, and I thought the heuristic for injection properties should be a little different.

+4
source share
5 answers

Found a solution, although I had two problems.

The first problem is how the object was registered / configured with the Ninject core in the IContainer.Configure method of my NinjectObjectBuilder class. Having studied the existing implementations of nServiceBus ObjectBuilder using other IoC containers, I noted that the general approach to registration was to register the most specific type, as well as all the interfaces that were implemented. In Ninject, this means “binding a particular type to itself,” and then binds each interface that also implements the type. It was pretty simple, except that I found, after profiling with dotTrace , that when Singleton was activated, it didn’t seem like I really got links to Singleton. In fact, what happens is that I get a new object depending on the type of service. For example, a specific type of UnicastBus implements IBus as well as IStartableBus and registers in the Singleton area. nServiceBus expects to receive the same object if an IBus or IStartableBus , if they are single point and both are bound to the same implementation. The singleton Nindec interpretation seems to refer to a service or interface — in other words, you get the same UnicastBus instance every time you request an IBus ; however, for a request for IStartableBus you get a new, different UnicastBus . As I decided, this is to implement the IContainer.Configure method as follows:

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

This solved the problem of resolving singleton instances as expected by nServiceBus. However, I still had the problem of receiving ghost messages in my handlers. After combing the log4net log files, profiling and finally reading the problem, as discussed here and. The problem is with mutual event handlers that are bound during property input. In particular, the problem is caused by the fact that UnicastBus has the Transport property set by mutliple times. Here is a code snippet from UnicastBus.cs in the nServiceBus code base:

 public virtual ITransport Transport { set { transport = value; transport.StartedMessageProcessing += TransportStartedMessageProcessing; transport.TransportMessageReceived += TransportMessageReceived; transport.FinishedMessageProcessing += TransportFinishedMessageProcessing; transport.FailedMessageProcessing += TransportFailedMessageProcessing; } 

}

Thinking about this, I wondered why this property was asked several times. UnicastBus registered in singleton scope nServiceBus, and I just fixed this issue as discussed above. It turns out that when activating an object, Ninject, whether it be a new one or from its internal cache, will still look to introduce object properties. He will call the injection heuristic classes registered in the container of internal components of the kernel, and based on their response (i.e. the result of calling their implementation bool ShouldInject(MemberInfo member) ), add properties before each activation. Thus, the solution was to prevent Ninject from injecting properties in cases that were previously activated and were single. My solution was to create a new resource investment strategy that tracked previously activated instances that were not temporary in scope and skipped the default injection strategy for activation requests for such instances. My strategy is like this:

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

My implementation now works. The only problem I encountered is the “replacement” of an existing activation strategy for injecting properties. I was thinking of creating a custom kernel (and this might be the best way to go); however, you cannot support the "preconfigured" kernel for nServiceBus. At the moment, I have an extension method that adds new components to any Ninject kernel.

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

This is a direct hack at this point, since there is no mechanism in the container for kernel components to “replace” an existing component. And, since I wanted to override the existing behavior of the property injection strategy, I could not have more than one of those specific types of strategies in the kernel at a time. Another problem with this current implementation is that any other IActivationStrategy custom components that could be configured will be lost. I wanted to write code that will receive all the IActivationStrategy components in the list, remove them from the kernel, replace the property injection strategy in the list I created, and then add everything back to the kernel, thereby effectively replacing them. However, the kernel component container only supports the general Add method, and I didn't want to write funky code to create a dynamic call.

** EDIT ** After I posted yesterday, I decided to better cope with this strategy. Here's what I did by linking everything in the extension method to configure the 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) ); } 
+4
source

There is a thread discussing this in the nservicebus group, but there is no solution yet.

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

+5
source

Hy Peter,

I found an approach to dynamic strategy exchange (I do this in the NinjectObjectBuilder constructor):

  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); 
+1
source

Here is an example using NinjectObjectBuilder .

This example shows the commands to send a website via NServiceBus to a backend with injection of Ninject dependencies for both the website and the backed.

I copied NinjectObjectBuilder from the official repository linked by Daniel Marbach. To my knowledge, the code has not yet been released as part of the main version of NServiceBus. I want to use it today, so I copied the code and linked it to NServiceBus 2.6.

0
source

Source: https://habr.com/ru/post/1303524/


All Articles