Does the Ninject Conditional Self bind to change the scope (for Task-scheduler) not work correctly?

As part of the MVC web application, DbContext works correctly with InRequestScope()

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

But from the Task Scheduler, calling DbContext in InRequestScope() failed to update the Db table ( without any error ) until I go to binding to InSingletonScope() OR InThreadScope()

Question:. So they can be changed for the InSingletonScope() / InThreadScope() to call the task scheduler. ?

// For Task Scheduler Call, I tried the binding below, but did not work properly

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

** And I probably missed something. Help is needed.

Updated code snippet

 #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()); } } 

In Web.Config

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

At Global.asax

 protected void Application_Start() { /* intialize Task */ TaskConfig.Init(); TaskManager.Instance.Initialize(TaskConfig.ScheduleTasks); TaskManager.Instance.Start(); } 

Ninject Binding Syntax

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

Note: The EmailTask class also has SomeReposity as a constructor argument.

Requests: -

  • But what binding syntax TaskScheduler(IResolutionRoot resolutionRoot) allow?
  • What is the configuration code to run TaskScheduler ?
  • As they say, to put IFakeDbContext directly into the constructor, can this work with IUnitOfWork<FakeDbContext> ?

Problem

A task that cannot be called using the Overloaded Constructor can only call the TaskScheduler default constructor.

Question 4: Is there any way to call TaskScheduler(IResolutionRoot resolutionRoot) from TaskScheduler default constructor?

Sample code snippet to create a task and 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; } 

Question 5: Can I enable TaskScheduler(IResolutionRoot resolutionRoot) here?

solvable

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

OR

  public ExecuteEmailTask() : this(new Bootstrapper().Kernel) { } public ExecuteEmailTask(IResolutionRoot resolutionRoot) { _resolutionRoot = resolutionRoot; } 
+4
source share
1 answer

First, you should notice that InSingletonScope() usually a bad idea for DbContext / Sessions. What happens if any other service changes the data during this time? I would recommend to study what consequences it has.


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

As an alternative to .When(...) you can also use .Named("FooBar") . Then the designer of the scheduled task should look like this:

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

However, note that this only (easy) works if you need to inject a DbContext into a single constructor. If functions depend on dependencies and they need the same instance of DbContext , it is also a bit confusing.

Since you updated your answer and said that it was, I would recommend a completely different approach: using the query parameter as the basis for the When(...) condition in combination with the InCallScope binding. The following is an example.

Pick yourself up, this is ab this code :) Implementation requires the extension ninject.extensions.NamedScope ( nuget ). I also used the xUnit and FluentAssertions nuget packages to run 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 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); } } 

Since, as you say, the task scheduler does not use IoC to create a task, it only supports a constructor without parameters. In this case, you can use DependencyResolver.Current (however, please note that I am in no way an asp.net/MVC expert, so I am not saying that this is thread safe or works 100% reliable):

 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(); } } 
+3
source

Source: https://habr.com/ru/post/1200472/


All Articles