我有一个控制台应用程序,我编写了一个私有构造的Bootstrapper类,公开了一个Default属性,只需一个公共方法即可访问Bootstrapper。我有一个问题,我无法解析NamedScope从NamedScope注册为具有相同名称的InNamedScope实例。代码如下:
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();
}
}
然后,当我尝试解析在CreateKernel方法中注册为
的绑定时kernel.Bind<IUnitOfWorkService>()
.To<UnitOfWorkService>()
.InNamedScope(SCOPENAME);
无法解决错误:
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.
堆栈跟踪:
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 ***
此时,我不知道还有什么可以尝试,任何帮助都将不胜感激。
修改
抱歉,我以为我在这里使用它的方式..以下是如何使用它的片段:
using (TaskExecutionScope scope = Bootstrapper.Default.GetResolutionRoot())
{
var unitOfWork = scope.Get<IUnitOfWorkService>();
//do things with unit of work service
}
这是如何使用它的一个简单例子。它调用Get<IUnitOfWorkService>
时失败。现在,虽然这个WAS失败了,但我能够通过包含Context Preservation插件并稍微更改TaskExecutionScope(仅从上面的代码中更改Resolve(IRequest request)
方法)来使其工作:
public System.Collections.Generic.IEnumerable<object> Resolve(Ninject.Activation.IRequest request)
{
var attempt = request.ParentContext.GetContextPreservingResolutionRoot().Resolve(request);
return attempt;
}
虽然这有效,但我讨厌在我的代码中有一些东西,我不知道为什么我必须在我的代码中使用它,这就是其中一个例子。我希望在我直接在Resolve
上调用NamedScope
之前,它应该起作用,因为它是一个解析根 - 我无法理解一个解析根的来源{ {1}}甚至不知道自己的名字???所以,我很想知道 -
A)为什么我必须这样做才能让它发挥作用?
B)这是有史以来最糟糕的事情 - 你应该这样做!
C)这会有效,但有一个小错误...
编辑2
所以,我已经尝试了@BatteryBackupUnit建议的内容,并且它失败并出现与之前相同的错误...我将此行添加到CreateKernel方法中:
NamedScope
并将kernel.Bind<TaskExecutionScope>().ToSelf().DefinesNamedScope(SCOPENAME);
方法更改为GetResolutionRoot
。在这一点上,我将回到下面提到的工作代码。
答案 0 :(得分:1)
通常你会使用像
这样的NamedScopeBind<FooTask>().ToSelf().DefinesNamedScope(SomeScopeName);
这不需要创建NamedScope
实例。
但是,您似乎希望它能够完成每项任务。您可以切换到.InCallScope()
,这将在此方案中实现相同的目标。另一种方法是让ninject创建TaskExecutionScope
,然后执行:
Bind<TaskExecutionScope>().ToSelf().DefinesNamedScope(SomeScopeName);
因为你总是使用TaskExecutionScope
创建一个范围对象,对吗?
NamedScope
和ContextPreservation 如果没有ContextPreservation,您可以将参数放在请求的上下文中,但只有在解析了对象及其依赖项之后才能保留这些参数。注入Func<Foo>
?它不会知道参数。 ContextPreservation会更改并保留上下文的参数,以便Func<Foo>
将参数传递给Foo
的请求。现在NamedScope
非常相似。它基本上是一个工厂。然而,实现它的方式是,在创建NamedScope
时,NamedScope
本身并不知道范围,而是与Bind<NamedScope>().ToSelf().DefinesNamedScope(SomeScopeName)
类似地实现。老实说,我认为NamedScope
的命名和实施都很糟糕。范围本身实际上由上下文中的NamedScopeParameter
定义!如果您执行Bind<Foo>().ToSelf().DefinesNamedScope("Foo")
,则实际上不涉及NamedScope
个对象。因此,如果没有上下文保留,那么在创建NamedScope
之后创建的任何对象,以及命名范围定义(NamedScopeParameter
)实际上都已消失。
这就是没有ContextPreservation Extension就无法工作的原因。
最简单的方法是调整石英作业工厂(参见how to inject quartz's job with ninject?)。
您不应该只执行IResolutionRoot.Get<TTask>()
,而应该IResolutionRoot.Get<TTask>(new NamedScopeParameter(scopeName);
(另请参阅CreateNamedScope(string scopeName)
method)。
提示:如果您的任务是按工厂创建对象 - 并且对象需要知道范围 - 您仍然需要ContextPreservation扩展。