【问题标题】:Can't figure out why Ninject Named Scope isnt working as expected无法弄清楚为什么 Ninject Named Scope 不能按预期工作
【发布时间】:2014-09-05 20:16:54
【问题描述】:

我有一个控制台应用程序,我编写了一个私有构造的 Bootstrapper 类,它公开了一个 Default 属性,该属性允许访问 Bootstrapper,只有一个可用的公共方法。我有一个问题,即我无法解析从同名的 NamedScope 注册为 InNamedScope 的实例的 NamedScope。代码如下:

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 ***

在这一点上,我不知道还有什么可以尝试的,任何帮助将不胜感激。

编辑

抱歉,我以为我知道我在这里是如何使用它的。这是一个关于如何使用它的 sn-p:

using (TaskExecutionScope scope = Bootstrapper.Default.GetResolutionRoot())
{
    var unitOfWork = scope.Get<IUnitOfWorkService>();                   
    //do things with unit of work service
}

这是一个简单的使用示例。对Get&lt;IUnitOfWorkService&gt; 的调用失败。现在,虽然这个 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;
    }

虽然这可行,但我讨厌在我的代码中包含一些我不知道为什么必须在我的代码中包含它的东西,这就是其中一个示例。我希望在我直接在NamedScope 上调用Resolve 之前,它应该可以运行,因为它是一个解析根 - 我不明白NamedScope 的解析根是如何不知道自己的名字???所以,我也很想知道-

A) 为什么我必须这样做才能让它工作?

B) 这是有史以来最糟糕的事情 - 你应该这样做!

C) 这可行,但有一个小错误...

编辑 2

所以,我尝试了@BatteryBackupUnit 的建议,但失败并出现与以前相同的错误...我将此行添加到 CreateKernel 方法:

kernel.Bind<TaskExecutionScope>().ToSelf().DefinesNamedScope(SCOPENAME);

并将 GetResolutionRoot 方法更改为 return Kernel.Get&lt;TaskExecutionScope&gt;(); 。在这一点上,我正在恢复到下面提到的工作代码。

【问题讨论】:

  • 你是如何解析对象的?使用NamedScope.Get,.. 或如何?请也显示那段代码。
  • @BatteryBackupUnit 添加了一个示例,说明我是如何使用它的,以及我是如何让它工作的。请仍然查看它并给我任何反馈 - 它是错误的、错误的、正确的和/或为什么我必须这样做......

标签: scope ninject named-scope ninject-extensions


【解决方案1】:

通常你会像这样使用 NamedScope

Bind<FooTask>().ToSelf().DefinesNamedScope(SomeScopeName);

这不需要创建NamedScope 实例。 但是,您似乎希望它适用于每项任务。您可以切换到.InCallScope(),在这种情况下也可以实现相同的效果。另一种选择是让 ninject 创建 TaskExecutionScope,然后执行以下操作:

Bind<TaskExecutionScope>().ToSelf().DefinesNamedScope(SomeScopeName);

因为你总是使用TaskExecutionScope 来创建一个作用域对象,对吧?

NamedScope 和 ContextPreservation

如果没有 ContextPreservation,您可以将参数放在请求的上下文中,但它们只会保留到对象及其依赖关系得到解决。注入Func&lt;Foo&gt;?它不会知道参数。 ContextPreservation 更改并保留上下文的参数,以便Func&lt;Foo&gt; 将参数传递给Foo 的请求。现在NamedScope 非常相似。它基本上是一个工厂。然而,它的实现方式是,在创建NamedScope 时,NamedScope 本身并不知道范围,而是与Bind&lt;NamedScope&gt;().ToSelf().DefinesNamedScope(SomeScopeName) 类似地实例化。老实说,我认为NamedScope 的命名和实施都很差。范围本身实际上是由上下文中的NamedScopeParameter 定义的!如果你这样做 Bind&lt;Foo&gt;().ToSelf().DefinesNamedScope("Foo") 实际上没有 NamedScope 涉及对象。因此,如果没有上下文保留,那么在创建 NamedScope,.. 以及命名范围定义 (NamedScopeParameter) 之后创建的任何对象实际上都已消失。 这就是为什么没有 ContextPreservation Extension 就无法工作的原因。

石英和任务范围

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

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

【讨论】:

  • 我已经看到了DefinesNamedScope 函数的第一个示例,但是由于我没有顶级对象,并且我有多个绑定,我希望每个范围只生成一次,这就是为什么我没有使用DefinesNamedScope 开始...但是,事实上,我基本上有一个顶级对象-TaskExecutionScope,这可能只是工作...我要试试出来真快,看看。您能否深入了解为什么在没有 ContextPreservation 的情况下从 NamedScope 解析无法将自己实现为活动范围?
  • 所以 - 快速提问,我是否按照上面第一个代码 sn-p 中的定义保留 TaskExecutionScope?或者,我是否删除构造函数 NamedScope 参数并接受其他内容?
  • 感谢您提供有关 Context/NamedScope 以及名称存储位置的信息。我同意在这种特定情况下这似乎是一个糟糕的实现,但至少现在我明白了我所看到的。另外,很抱歉在我的 StackTrace 中引用了 Quartz,这对于这种情况实际上是多余的,因为我在标准执行中也发生了这种情况,我只是从我的 Azure 作业而不是控制台应用程序中获取了 StackTrace。我最终离开了上面更改的方法,因为我不希望我的程序更了解 Ninject,然后是解析根。谢谢!
猜你喜欢
  • 2020-04-18
  • 1970-01-01
  • 2014-08-29
  • 2021-05-29
  • 1970-01-01
  • 2019-12-31
  • 2014-12-08
  • 2012-05-16
  • 1970-01-01
相关资源
最近更新 更多