I have a console application which I wrote a privately constructed Bootstrapper class, exposing a Default property that gives access to the Bootstrapper with only one public method available. I have an issue where the NamedScope that I can not resolve instances registered as InNamedScope from the NamedScope with the same name. Code follows:
public class Bootstrapper
{
private const string SCOPENAME = "BOOTSTRAPPED";
private static object KernelConstructionLocker = new object();
private StandardKernel Kernel;
private readonly Bootstrapper defaultBootstrapper = new Bootstrapper();
private Bootstrapper Default {get { return defaultBootstrapper; }}
private Bootstrapper()
{ }
public GetResolutionRoot()
{
if (Kernel == null)
{
//Kernel ctor not thread safe
lock(KernelConstructionLocker)
{
//double locked incase thread created while locked
if (Kernel == null)
{
Kernel = CreateKernel();
}
}
}
return new TaskExecutionScope(Kernel.CreateNamedScope(SCOPENAME));
}
private CreateKernel()
{
Kernel = new StandardKernel();
//bindings, etc...
}
}
public class TaskExecutionScope : IResolutionRoot, IDisposable
{
private readonly NamedScope scope;
internal TaskExecutionScope(NamedScope scope)
{
this.scope = scope;
}
public bool CanResolve(Ninject.Activation.IRequest request, bool ignoreImplicitBindings)
{
var canResolve = scope.CanResolve(request, ignoreImplicitBindings);
return canResolve;
}
public bool CanResolve(Ninject.Activation.IRequest request)
{
var canResolve = scope.CanResolve(request);
return canResolve;
}
public Ninject.Activation.IRequest CreateRequest(Type service, Func<Ninject.Planning.Bindings.IBindingMetadata, bool> constraint, System.Collections.Generic.IEnumerable<Ninject.Parameters.IParameter> parameters, bool isOptional, bool isUnique)
{
var request = scope.CreateRequest(service, constraint, parameters, isOptional, isUnique);
return request;
}
public bool Release(object instance)
{
var release = scope.Release(instance);
return release;
}
public System.Collections.Generic.IEnumerable<object> Resolve(Ninject.Activation.IRequest request)
{
var resolve = scope.Resolve(request);
return resolve;
}
public void Dispose()
{
scope.Dispose();
}
}
Then, when I attempt to resolve a binding that was registered in the CreateKernel method as
kernel.Bind<IUnitOfWorkService>()
.To<UnitOfWorkService>()
.InNamedScope(SCOPENAME);
It fails to resolve with the error:
Error activating IUnitOfWorkService
The scope BOOTSTRAPPED is not known in the current context.
No matching scopes are available, and the type is declared InNamedScope(BOOTSTRAPPED).
Activation path:
1) Request for IUnitOfWorkService
Suggestions:
1) Ensure that you have defined the scope BOOTSTRAPPED.
2) Ensure you have a parent resolution that defines the scope.
3) If you are using factory methods or late resolution, check that the correct IResolutionRoot is being used.
Stacktrace:
at Ninject.Extensions.NamedScope.NamedScopeExtensionMethods.GetNamedScope(IContext context, String scopeParameterName)
at Ninject.Extensions.NamedScope.NamedScopeExtensionMethods.<>c__DisplayClass1`1.<InNamedScope>b__0(IContext context)
at Ninject.Planning.Bindings.BindingConfiguration.GetScope(IContext context)
at Ninject.Planning.Bindings.Binding.GetScope(IContext context)
at Ninject.Activation.Context.GetScope()
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<>c__DisplayClass15.<Resolve>b__f(IBinding binding)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
at SomeConsoleApp.Work.ScheduledWork.ScheduledWork.Quartz.IJob.Execute(IJobExecutionContext context) in ***
At this point, I don't know what else to try, any help would be appreciated.
EDIT
Sorry, thought I had how I was using it in here.. Here is a snippet of how it would be used:
using (TaskExecutionScope scope = Bootstrapper.Default.GetResolutionRoot())
{
var unitOfWork = scope.Get<IUnitOfWorkService>();
//do things with unit of work service
}
This is a bare bones example of how it is used. It fails on that call to Get<IUnitOfWorkService>
. Now, while this WAS failing, I was able to get it to work by including the Context Preservation plugin, and changing up the TaskExecutionScope a little bit (only changed the Resolve(IRequest request)
method from the above code:
public System.Collections.Generic.IEnumerable<object> Resolve(Ninject.Activation.IRequest request)
{
var attempt = request.ParentContext.GetContextPreservingResolutionRoot().Resolve(request);
return attempt;
}
While this works, I hate having something in my code where I don't know WHY I have to have it in my code, and this is one of those examples. I would expect that since before I was calling Resolve
directly on the NamedScope
, that it should function, because it IS a resolution root - and I can't understand how a resolution root of a NamedScope
doesn't even know its own name??? So, I would love to know either-
A) Why did I have to do this to get it to work?
B) This is the worst thing ever - you should do THIS instead!
C) This will work, but there is a small bug...
EDIT 2
So, I have attempted what @BatteryBackupUnit suggested, and it is failing with the same error as before... I added this line to the CreateKernel method:
kernel.Bind<TaskExecutionScope>().ToSelf().DefinesNamedScope(SCOPENAME);
And changed the GetResolutionRoot
method to return Kernel.Get<TaskExecutionScope>();
. At this point I'm reverting back to the working code mentioned below.