Ninject条件自绑定更改范围(对于任务调度程序)无法正常工作? [英] Ninject Conditional Self bind to change scope (For Task-scheduler) not working properly?

本文介绍了Ninject条件自绑定更改范围(对于任务调度程序)无法正常工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在MVC Web应用程序中DbContext绑定正常工作与 InRequestScope()

Within MVC Web Application DbContext binding work properly with InRequestScope()

 kernel.Bind<DbContext>().ToSelf().InRequestScope();
 kernel.Bind<IUnitOfWork<DbContext>>().To<UnitOfWork<DbContext>>();

但是从任务计划程序调用 DbContext InRequestScope()无法更新Db表(,无任何错误),直到我将Binding更改为 InSingletonScope() OR InThreadScope()

But from a Task Scheduler call DbContext in InRequestScope() unable to update Db Table (without any error), until I change Binding to InSingletonScope() OR InThreadScope()

问题: / strong>对于任务计划程序调用,他们的任何方式将范围更改为 InSingletonScope() / InThreadScope()

//对于任务计划程序调用,我尝试了bellow绑定,但无法正常工作

// For Task Scheduler Call, I tried bellow bind, but not working properly

kernel.Bind<DbContext>().ToSelf()
.When(request => request.Target.Type.Namespace.StartsWith("NameSpace.ClassName"))
.InSingletonScope(); 

**可能我想念一些事情。需要帮助。

** And probably I miss some thing. Need help.

代码段更新

#region Commented Code

public EmailTask() : this
 ( DependencyResolver.Current.GetService<IMessageManager>(),
 , DependencyResolver.Current.GetService<IUnitOfWork<DbContext>>()) { }

#endregion



public EmailTask(IMessageManager messageManager, IUnitOfWork<DbContext> unitOfWork)
{
            this._messageManager = messageManager;
            this._unitOfWork = unitOfWork;
            ProcessEmail();

}

public class NonRequestScopedParameter : IParameter { ... }

public void ProcessEmail()
{
   var temp = SomeRepository.GetAll();

   SendEmail(temp);

   temp.Date = DateTime.Now;

   SomeRepository.Update(temp);

   unitOfWork.Commit();
}  

public class ExecuteEmailTask : ITask
{
  private readonly IResolutionRoot _resolutionRoot;
  private int _maxTries = 5;

  public ExecuteEmailTask(IResolutionRoot resolutionRoot)
  {
        _resolutionRoot = resolutionRoot;
  }

  public void Execute(XmlNode node)
        {
            XmlAttribute attribute1 = node.Attributes["maxTries"];
            if (attribute1 != null && !String.IsNullOrEmpty(attribute1.Value))
            {
                this._maxTries = int.Parse(attribute1.Value);
            }
            /// send email messages
            var task = _resolutionRoot.Get<EmailTask>(new NonRequestScopedParameter());
        }
}

在Web.Config

<ScheduleTasks>
     <Thread seconds="60">
        <task name="ExecuteEmailTask" type="namespace.ExecuteEmailTask, AssemblyName" enabled="true" stopOnError="false" maxTries="5"/>
      </Thread>      
    </ScheduleTasks>

Global.asax

protected void Application_Start()
{
   /* intialize Task */
            TaskConfig.Init();

            TaskManager.Instance.Initialize(TaskConfig.ScheduleTasks);
            TaskManager.Instance.Start();
}

Ninject绑定语法

kernel.Bind<DbContext>().ToSelf().InRequestScope(); // Default bind

kernel.Bind<DbContext>().ToSelf()
                  .When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
                  .InCallScope();  // For Scheduler

注意: EmailTask​​ class也有 SomeReposity 作为构造方法参数。

Note: EmailTask class also have SomeReposity as a Constructor Argument.

查询: -


  1. 但是,要解决的绑定语法是什么?code> TaskScheduler(IResolutionRoot resolutionRoot)?

  2. 运行 TaskScheduler 的配置代码是什么?

  3. c $ c> IFakeDbContext 直接进入构造函数,可以使用 IUnitOfWork< FakeDbContext>

  1. But what is the bind syntax to resolve TaskScheduler(IResolutionRoot resolutionRoot) ?
  2. What is the configuration code to run TaskScheduler ?
  3. As say to put IFakeDbContext directly into constructor, can this work with IUnitOfWork<FakeDbContext> ?

问题

任务无法使用重载构造函数调用,只能调用 TaskScheduler 默认构造函数。

Task unable to call with Overloaded Constructor , it is only able to call TaskScheduler default Constructor.

问题4:可以以任何方式调用 TaskScheduler(IResolutionRoot resolutionRoot) TaskScheduler 默认构造函数?

Question 4: Can any way to invoke TaskScheduler(IResolutionRoot resolutionRoot) from TaskScheduler default constructor ?

示例代码片段来创建任务&运行使用 System.Threading.Timer

Sample Code Snippet to create Task & run using System.Threading.Timer

private ITask createTask()
        {
            if (this.Enabled && (this._task == null))
            {
                if (this._taskType != null)
                {
                    this._task = Activator.CreateInstance(this._taskType) as ITask;
                }
                this._enabled = this._task != null;
            }
            return this._task;
        }

问题5:我可以解决 TaskScheduler(IResolutionRoot resolutionRoot)这里?

已解决

public ExecuteEmailTask​​():
this(DependencyResolver.Current.GetService< IResolutionRoot>())

OR

 public ExecuteEmailTask() : this(new Bootstrapper().Kernel) { }

        public ExecuteEmailTask(IResolutionRoot resolutionRoot)
        {
            _resolutionRoot = resolutionRoot;
        }


推荐答案

首先,你应该注意对于DbContext / Sessions来说, InSingletonScope()通常是一个坏主意。如果某些其他服务同时更改数据,会发生什么?我会建议您调查这些影响。

First of, you should note that InSingletonScope() is usually a bad idea for DbContext's/Sessions. What happens if some other service changes data in the meantime? I would recommend investigating what effects this has.

对于您首先描述的场景,正确配置。(...)应该工作。

For the scenario you first described, a correctly formulated .When(...) should work.

作为对的改变。 )绑定你也可以使用一个 .Named(FooBar)绑定。
计划任务的构造函数需要如下所示:

As an alterantive to the .When(...) binding you could also use a .Named("FooBar") binding. The constructor of the scheduled task would then need to look like:

ctor(Named["FooBar"] DbContext dbContext);

但是,请注意,只有(轻松)才可以注册 DbContext 转换为单个构造函数。如果任务的功能依赖性,并且这些需要相同的 DbContext 实例,那么它会变得有点棘手。

However, note, that this only (easily) works in case you need to inject the DbContext into a single constructor. If the task features dependencies and these need the same DbContext instance, too, it gets a bit tricker.

你更新了你的答案,并说是这样的情况,我建议一个完全不同的方法:使用请求参数作为基础的当(...)条件结合 InCallScope 绑定。请参见下面的示例。

Since you updated your answer and say that this is the case, i would recommend an entirely different approach: Using a request parameter as basis for the When(...) condition combined with InCallScope binding. See below for an example.

支持自己,这是代码:)实现需要ninject.extensions.NamedScope扩展( nuget )。
我还使用xUnit和FluentAssertions nuget包来执行测试。

Brace yourself, this is ab it of code :) The implementation requires the ninject.extensions.NamedScope extension (nuget). I've also used xUnit and FluentAssertions nuget packages to execute the tests.

public class Test
{
    // the two implementations are just for demonstration and easy verification purposes. You will only use one DbContext type.
    public interface IFakeDbContext { }
    public class RequestScopeDbContext : IFakeDbContext { }
    public class CallScopeDbContext : IFakeDbContext { }

    public class SomeTask
    {
        public IFakeDbContext FakeDbContext { get; set; }
        public Dependency1 Dependency1 { get; set; }
        public Dependency2 Dependency2 { get; set; }

        public SomeTask(IFakeDbContext fakeDbContext, Dependency1 dependency1, Dependency2 dependency2)
        {
            FakeDbContext = fakeDbContext;
            Dependency1 = dependency1;
            Dependency2 = dependency2;
        }
    }

    public class Dependency1
    {
        public IFakeDbContext FakeDbContext { get; set; }

        public Dependency1(IFakeDbContext fakeDbContext)
        {
            FakeDbContext = fakeDbContext;
        }
    }

    public class Dependency2
    {
        public IFakeDbContext FakeDbContext { get; set; }

        public Dependency2(IFakeDbContext fakeDbContext)
        {
            FakeDbContext = fakeDbContext;
        }
    }

    public class TaskScheduler
    {
        private readonly IResolutionRoot _resolutionRoot;

        public TaskScheduler(IResolutionRoot resolutionRoot)
        {
            _resolutionRoot = resolutionRoot;
        }

        public SomeTask CreateScheduledTaskNow()
        {
            return _resolutionRoot.Get<SomeTask>(new NonRequestScopedParameter());
        }
    }

    public class NonRequestScopedParameter : Ninject.Parameters.IParameter
    {
        public bool Equals(IParameter other)
        {
            if (other == null)
            {
                return false;
            }

            return other is NonRequestScopedParameter;
        }

        public object GetValue(IContext context, ITarget target)
        {
            throw new NotSupportedException("this parameter does not provide a value");
        }

        public string Name
        {
            get { return typeof(NonRequestScopedParameter).Name; }
        }

        // this is very important
        public bool ShouldInherit
        {
            get { return true; }
        }
    }

    [Fact]
    public void FactMethodName()
    {
        var kernel = new StandardKernel();

        // this is the default binding
        kernel.Bind<IFakeDbContext>().To<RequestScopeDbContext>();

        // this binding is _only_ used when the request contains a NonRequestScopedParameter
        // in call scope means, that all objects built in the a single request get the same instance
        kernel.Bind<IFakeDbContext>().To<CallScopeDbContext>()
            .When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
            .InCallScope();

        // let's try it out!
        var task = kernel.Get<SomeTask>(new NonRequestScopedParameter());

        // verify that the correct binding was used
        task.FakeDbContext.Should().BeOfType<CallScopeDbContext>();

        // verify that all children of the task get injected the same task instance
        task.FakeDbContext.Should()
            .Be(task.Dependency1.FakeDbContext)
            .And.Be(task.Dependency2.FakeDbContext);
    } 
}

因为,就像你所说,任务调度程序不利用IoC来创建任务,它只支持一个无参数的构造函数。在这种情况下,您可以使用 DependencyResolver.Current (但是,请注意,我绝对不是asp.net / MVC的专家,所以我没有提出任何索赔这是线程安全或100%可靠工作):

Since, as you say, the task scheduler does not make use of the IoC to create the task, it only supports a parameterless constructor. In that case you can make use DependencyResolver.Current (however, note that i'm in no way an expert on asp.net /MVC so i'm not making any claims that this is thread safe or working 100% reliably):

public class TaskExecutor : ITask
{
    public TaskExecutor()
        : this(DependencyResolver.Current.GetService<IResolutionRoot>())
    {}

    internal TaskExecutor(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }

    public void Execute()
    {
        IFooTask actualTask = this.resolution.Get<IFooTask>(new NonRequestScopedParameter());
        actualTask.Execute();
    }
}

这篇关于Ninject条件自绑定更改范围(对于任务调度程序)无法正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆