Within MVC Web Application DbContext binding work properly with InRequestScope()
kernel.Bind<DbContext>().ToSelf().InRequestScope();
kernel.Bind<IUnitOfWork<DbContext>>().To<UnitOfWork<DbContext>>();
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()
Question: So is their any way change scope to InSingletonScope()
/ InThreadScope()
for a Task Scheduler Call. ?
// 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.
Code Snippet Updated
#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>
In Global.asax
protected void Application_Start()
{
/* intialize Task */
TaskConfig.Init();
TaskManager.Instance.Initialize(TaskConfig.ScheduleTasks);
TaskManager.Instance.Start();
}
Ninject Bind Syntax
kernel.Bind<DbContext>().ToSelf().InRequestScope(); // Default bind
kernel.Bind<DbContext>().ToSelf()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope(); // For Scheduler
Note: EmailTask
class also have SomeReposity
as a Constructor Argument.
Queries:-
- But what is the bind syntax to resolve
TaskScheduler(IResolutionRoot resolutionRoot)
? - What is the configuration code to run
TaskScheduler
? - As say to put
IFakeDbContext
directly into constructor, can this work withIUnitOfWork<FakeDbContext>
?
Problem
Task unable to call with Overloaded Constructor , it is only able to call TaskScheduler
default Constructor.
Question 4: Can any way to invoke TaskScheduler(IResolutionRoot resolutionRoot)
from TaskScheduler
default constructor ?
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;
}
Question 5: Can I resolve TaskScheduler(IResolutionRoot resolutionRoot)
here ?
Solved
public ExecuteEmailTask() :
this(DependencyResolver.Current.GetService<IResolutionRoot>())
OR
public ExecuteEmailTask() : this(new Bootstrapper().Kernel) { }
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
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.As an alternative to the
.When(...)
binding you could also use a.Named("FooBar")
binding. The constructor of the scheduled task would then need to look like: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 sameDbContext
instance, too, it gets a bit tricker.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 withInCallScope
binding. See below for an example.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.
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):