无法弄清楚为什么Ninject Named Scope没有按预期工作

时间:2014-09-05 20:16:55

标签: scope ninject named-scope ninject-extensions

我有一个控制台应用程序,我编写了一个私有构造的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。在这一点上,我将回到下面提到的工作代码。

1 个答案:

答案 0 :(得分:1)

通常你会使用像

这样的NamedScope
Bind<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就无法工作的原因。

Quartz和TaskScopes

最简单的方法是调整石英作业工厂(参见how to inject quartz's job with ninject?)。 您不应该只执行IResolutionRoot.Get<TTask>(),而应该IResolutionRoot.Get<TTask>(new NamedScopeParameter(scopeName);(另请参阅CreateNamedScope(string scopeName) method)。

提示:如果您的任务是按工厂创建对象 - 并且对象需要知道范围 - 您仍然需要ContextPreservation扩展。