Ninject Scope issue with Tasks/Threads - asp.net-mvc-3

I have an MVC3 project that uses Ninject, Entity Framework and the Unit of Work pattern with a Service layer.
My AsyncService class has a function that starts a background task that, as an example, adds users to the User repository.
My current problem is that the task only runs correctly for a few seconds before I get an error that the DbContext has been disposed.
My database context, which is injected with Ninject's InRequestScope() seems to be getting disposed, as InRequestScope() ties it to HttpContext.
I've read about InThreadScope(), however I'm not sure how to implement it properly in my MVC project.
My Question is: What is the correct way to use Ninject in my Task?
public class AsyncService
{
private CancellationTokenSource cancellationTokenSource;
private IUnitOfWork _uow;
public AsyncService(IUnitOfWork uow)
{
_uow = uow;
}
public void AsyncStartActivity(Activity activity)
{
...snip...
this.cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = this.cancellationTokenSource.Token;
var task = Task.Factory.StartNew(() =>
{
foreach (var user in activity.UserList)
{
this._uow.UserRepository.Add(new User() {UserID = user});
}
this._uow.Save();
}, cancellationToken);
...snip...
}
}

InRequestScope'd objects are Disposed at the end of a request so it can't be used in this case. InThreadScope also doesn't fit as that would reuse the UoW for several tasks.
What you can do though is declare your AsyncService as the Scoping Object for all the objects within using the NamedScope extension.
See http://www.planetgeek.ch/2010/12/08/how-to-use-the-additional-ninject-scopes-of-namedscope/

This is a messy solution that I've used in the past using the ChildKernel plugin (I think Named scope would much cleaner). Basically I create a child kernel, and scope everything pertaining to the UoW as singleton in the child kernel. I then create a new child kernel for each Task, handle the UoW, and commit or rollback.
IAsyncTask is an interface with 1 method, Execute()
private Task void ExecuteTask<T>() where T:IAsyncTask
{
var task = Task.Factory.StartNew(() =>
{
var taskKernel = _kernel.Get<ChildKernel>();
var uow = taskKernel.Get<IUnitOfWork>();
var asyncTask = taskKernel.Get<T>();
try
{
uow.Begin();
asyncTask.Execute();
uow.Commit();
}
catch (Exception ex)
{
uow.Rollback();
//log it, whatever else you want to do
}
finally
{
uow.Dispose();
taskKernel.Dispose();
}
});
return task;
}

Related

How to open database connection in a BackgroundJob in ABP application

Issue
For testing, I create a new job, it just use IRepository to read data from database. The code as below:
public class TestJob : BackgroundJob<string>, ITransientDependency
{
private readonly IRepository<Product, long> _productRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public TestJob(IRepository<Product, long> productRepository,
IUnitOfWorkManager unitOfWorkManager)
{
_productRepository = productRepository;
_unitOfWorkManager = unitOfWorkManager;
}
public override void Execute(string args)
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
Debug.WriteLine("test db connection");
}
}
Then I create a new application service to trigger the job. The code snippet as below:
public async Task UowInJobTest()
{
await _backgroundJobManager.EnqueueAsync<TestJob, string>("aaaa");
}
When I test the job, It will throw following exception when execute var task = _productRepository.GetAll().ToListAsync();
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.Object name: 'AbpExampleDbContext'.
Solution
S1: Add UnitOfWork attribute on execute method. It can address the issue. But it is not better for my actual scenario. In my actual scenario, the job is a long time task, and has much DB operatons, if enable UnitOfWork for Execute method, it will lock db resource for a long time. So this is not a solution for my scenario.
[UnitOfWork]
public override void Execute(string args)
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
Debug.WriteLine("test db connection");
}
S2: Execute DB operation in UnitOfWork explicitly. Also, this can address the issue, but I don’t think this is a best practice. In my example,just read data from database, no transaction is required. Even-though the issue is addressed, but I don’t think it’s a correct way.
public override void Execute(string args)
{
using (var unitOfWork = _unitOfWorkManager.Begin())
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
unitOfWork.Complete();
}
Debug.WriteLine("test db connection");
}
Question
My question is what’s the correct and best way to execute a DB operation in BackgroundJob?
There is addtional another question, I create a new application service, and disable UnitOfWrok, but it works fine. Please see the code as below. Why It works fine in application service, but doesn’t work in BackgroundJob?
[UnitOfWork(IsDisabled =true)]
public async Task<GetAllProductsOutput> GetAllProducts()
{
var result = await _productRepository.GetAllListAsync();
var itemDtos = ObjectMapper.Map<List<ProductDto>>(result);
return new GetAllProductsOutput()
{
Items = itemDtos
};
}
The documentation on Background Jobs And Workers uses [UnitOfWork] attribute.
S1: Add UnitOfWork attribute on execute method. It can address the issue. But it is not better for my actual scenario. In my actual scenario, the job is a long time task, and has much DB operatons, if enable UnitOfWork for Execute method, it will lock db resource for a long time. So this is not a solution for my scenario.
Background jobs are run synchronously on a background thread, so this concern is unfounded.
S2: Execute DB operation in UnitOfWork explicitly. Also, this can address the issue, but I don’t think this is a best practice. In my example,just read data from database, no transaction is required. Even-though the issue is addressed, but I don’t think it’s a correct way.
You can use a Non-Transactional Unit Of Work:
[UnitOfWork(isTransactional: false)]
public override void Execute(string args)
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
}
You can use IUnitOfWorkManager:
public override void Execute(string args)
{
using (var unitOfWork = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress))
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
unitOfWork.Complete();
}
}
You can also use AsyncHelper:
[UnitOfWork(isTransactional: false)]
public override void Execute(string args)
{
var items = AsyncHelper.RunSync(() => _productRepository.GetAll().ToListAsync());
}
Conventional Unit Of Work Methods
I create a new application service, and disable UnitOfWork, but it works fine.
Why it works fine in application service, but doesn’t work in BackgroundJob?
[UnitOfWork(IsDisabled = true)]
public async Task<GetAllProductsOutput> GetAllProducts()
{
var result = await _productRepository.GetAllListAsync();
var itemDtos = ObjectMapper.Map<List<ProductDto>>(result);
return new GetAllProductsOutput
{
Items = itemDtos
};
}
You are using different methods: GetAllListAsync() vs GetAll().ToListAsync()
Repository methods are Conventional Unit Of Work Methods, but ToListAsync() isn't one.
From the documentation on About IQueryable<T>:
When you call GetAll() outside of a repository method, there must be an open database connection. This is because of the deferred execution of IQueryable<T>. It does not perform a database query unless you call the ToList() method or use the IQueryable<T> in a foreach loop (or somehow access the queried items). So when you call the ToList() method, the database connection must be alive.

How to write test code for Web API in Visual Studio?

I'm a bit new to test project. I currently have a web api project which contains Get, Put, Post and Delete methods. When comes to writing test cases, I'm confused. Should I write test code to test the Http URL?
My web api code:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
using (var unitOfWork = new UnitOfWork(_db))
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
}
}
And in my test project, I kind of stuck here. We are using Xunit. How to write test code to test the Get method? Or should I write code to test the URL api/values/5 instead, but how?
[Fact]
public void GetTest()
{
using (var unitOfWork = new UnitOfWork(new MockDatabase()))
{
}
}
Any help would be appreciated.
You need to make a couple of changes before you can really unit test your controller. You need to pass an instance of your UnitOfWork class into the controller in its constructor. Then your controller method code becomes:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
Then in your unit tests you do this:
[Fact]
public void GetTest()
{
// Arrange
// You really want to mock your unit of work so you can determine
// what you are going to send back
var unitOfWork = new MockUnitOfWork();
var systemUnderTest = new Controller(unitOfWork);
system.Request = new HttpRequestMessage();
// Act
var result = systemUnderTest.Get(1);
// Assert
// Here you need to verify that you got back the expected result
}
Injecting the UnitOfWork class into the controller is probably another question. Mark Seemann has an excellent post on the subject, but it might be a little advanced. There are a number of different ways to accomplish that with simpler (but maybe not as robust methods). Google is your friend with that. But if you have questions, post another question.
Hope that helps.
You would need to make some design changes to your controller to make it easy to test. In your action you are creating an instances which will make it difficult to test with a fake dependencies to the controller. Also your controller should depend on abstractions rather than concretions which will allow the controller to be more testable.
public class MyWebApiController : ApiController {
private IUnitOfWork unitOfWork;
public MyWebApiController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id) {
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null) {
return NotFound();
} else {
return Ok(result);
}
}
//...other code
}
Notice the controller uses dependency injection to inject an IUnitOfWork. That makes the controller more testable, because you can inject mocks of its dependencies when unit testing.
From there it is just to create an instance of the controller and call the method under test with mocks of the dependencies.
[Fact]
public void GetTest() {
//Arrange (Setup the parts needed to run test)
var unitOfWork = new MockUnitOfWork(new MockDatabase());
//Or using your mocking framework of choice
//var unitOfWork = Mock.Of<IUnitOfWork>(); //this is using Moq
var controller = new MyWebApiController(unitOfWork);
var id = "Test Id value here";
//Act (call the method under test)
var result - controller.Get(id);
//Assert (check results)
//...Do your assertion pertaining to result of calling method under test
}
Reference : Unit Testing Controllers in ASP.NET Web API 2

running wf workflow from mvc controller

enter code hereI intend to use WF4.5 in a web application which is written by MVC Framework. I have used WorkflowApplication class instance to run my WorkFlow with. but whenever i call the method in controller that run the instance I get this error:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it
I have written this class which is resposnsible to execute workflow:
public class WorkFlowsPipeline : IWorkFlowsPipeline
{
private IUnitOfWork _unitOfWork;
private SqlWorkflowInstanceStore _instanceStore;
public WorkFlowsPipeline(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
//workflowInstanceStore
_instanceStore = new SqlWorkflowInstanceStore();
_instanceStore.ConnectionString ="data source=.;initial catalog=WFPersist;user id=sa;password=1;";
}
public void RecordPersistedInstanceForTheUser(int userId,Guid instanceId, Models.Enums.WorkFlowTypeEnum workFlowType)
{
_unitOfWork.UsersWorkFlows.Add(new UsersWorkFlowsInstance
{
UserId = userId,
WorkFlowId=instanceId,
WorkFlowType = workFlowType
});
}
public void RunCompleteProfileForUser(int userId)
{
var usersWorkFlow = _unitOfWork.UsersWorkFlows.GetAll().FirstOrDefault(x => x.UserId == userId);
if (usersWorkFlow == null)
{
Activity rentalWorkflow = new Activity1();
Dictionary<string, object> wfArg = new Dictionary<string, object>()
{
{
"UOW", _unitOfWork
},
{
"UserId",userId
}
};
var _wfApp = new WorkflowApplication(rentalWorkflow, wfArg);
_wfApp.SynchronizationContext = SynchronizationContext.Current;
_wfApp.InstanceStore = _instanceStore;
//_wfApp.Extensions.Add(this);
var instanceId=_wfApp.Id;
_wfApp.Run();
RecordPersistedInstanceForTheUser(userId, instanceId,WorkFlowTypeEnum.CompleteProfile);
}
else
{
//get id of instance load it from database and run it
}
}
}
and I called the method in my controller action in this way:
public async Task<ActionResult> Index()
{
var userId = User.Identity.GetUserId<int>();
_workFlowsPipeline.RunCompleteProfileForUser(userId);
return View();
}
Use WorkflowInvoker instead of WorkflowApplication.

How do I create a Quartz.NET’s job requiring injection with autofac

I am trying to get Quartz.net (2.1.2) to work with an IoC container (autofac), as I have services I need to use in the scheduled jobs. I have found similar posts on the subject, but I can't seem to find one with a specific registration example for autofac.
The following post deals with the same issue I am having:
How to schedule task using Quartz.net 2.0?
However, the part I believe I am missing is when the answer says "And don't forget to register the job in the IoC container". I am unsure how to do this exactly, as everything I have tried so far hasn't worked.
In the following example, the "HelloJob" will run, but whenever I try to inject the releaseService into the "ReleaseJob" it refuses to run.
Update:
I marked the code in the DependencyRegistration.cs section where I believe the issue is.
Update 2:
Some related links that are related to what I need to do and might help (I've already gone through them all but still cannot figure out how to get this working with autofac):
HOW TO use Quartz.NET in PRO way?
- http://blog.goyello.com/2009/09/21/how-to-use-quartz-net-in-pro-way/
Autofac and Quartz.NET
- http://blog.humann.info/post/2013/01/30/Autofac-and-QuartzNET.aspx
Constructor injection with Quartz.NET and Simple Injector
- Constructor injection with Quartz.NET and Simple Injector
ASP.Net MVC 3, Ninject and Quartz.Net - How to?
- ASP.Net MVC 3, Ninject and Quartz.Net - How to?
Here is the relevant code:
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
var dependencyRegistration = new DependencyRegistration();
dependencyRegistration.Register();
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new ValidatorFactory()));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
}
DependencyRegistration.cs
public class DependencyRegistration
{
public void Register()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterAssemblyModules(Assembly.GetExecutingAssembly());
// Validation
builder.RegisterType<ValidatorFactory>()
.As<IValidatorFactory>()
.InstancePerHttpRequest();
AssemblyScanner findValidatorsInAssembly = AssemblyScanner.FindValidatorsInAssembly(Assembly.GetExecutingAssembly());
foreach (AssemblyScanner.AssemblyScanResult item in findValidatorsInAssembly)
{
builder.RegisterType(item.ValidatorType)
.As(item.InterfaceType)
.InstancePerHttpRequest();
}
// Schedule
builder.Register(x => new StdSchedulerFactory().GetScheduler()).As<IScheduler>();
// Schedule jobs
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(x => typeof(IJob).IsAssignableFrom(x));
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
//Schedule
IScheduler sched = container.Resolve<IScheduler>();
sched.JobFactory = new AutofacJobFactory(container);
sched.Start();
IJobDetail job = JobBuilder.Create<ReleaseJob>()
.WithIdentity("1Job")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("1JobTrigger")
.WithSimpleSchedule(x => x
.RepeatForever()
.WithIntervalInSeconds(5)
)
.StartNow()
.Build();
sched.ScheduleJob(job, trigger);
job = JobBuilder.Create<HelloJob>()
.WithIdentity("2Job")
.Build();
trigger = TriggerBuilder.Create()
.WithIdentity("2JobTrigger")
.WithSimpleSchedule(x => x
.RepeatForever()
.WithIntervalInSeconds(5)
)
.StartNow()
.Build();
sched.ScheduleJob(job, trigger);
}
}
JobFactory.cs
public class AutofacJobFactory : IJobFactory
{
private readonly IContainer _container;
public AutofacJobFactory(IContainer container)
{
_container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob)_container.Resolve(bundle.JobDetail.JobType);
}
public void ReturnJob(IJob job)
{
}
}
ReleaseJob.cs
public class ReleaseJob : IJob
{
private readonly IReleaseService _releaseService;
public ReleaseJob(IReleaseService releaseService)
{
this._releaseService = releaseService;
}
public void Execute(IJobExecutionContext context)
{
Debug.WriteLine("Release running at " + DateTime.Now.ToString());
}
}
public class HelloJob : IJob
{
public void Execute(IJobExecutionContext context)
{
Debug.WriteLine("Hello job at " + DateTime.Now.ToString());
}
}
ReleaseServiceModel.cs
public class ReleaseServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ReleaseService>()
.As<IReleaseService>()
.InstancePerLifetimeScope();
}
}
I finally tracked down what the issue was.
My release service was using a data repository which was being created with a different scope.
I discovered this by creating a new test service that did nothing but return a string, and that worked being injected into a quartz job.
On discovering this, I changed the scope of the repository called upon by the release service , and then the release service started working inside the quartz job.
My apologies to anyone that looked at this question to try and help me out. Because the code of the release service was not listed, it would have been difficult to figure out what was wrong.
I have updated the code with the final working bindings I used for quartz with autofac.
The problem is that your AutofacJobFactory is not creating the ReleaseJob (you are doing this with JobBuilder.Create<ReleaseJob>() instead), so the IoC container is not aware of it's instantiation meaning dependency injection cannot occur.
The following code should work:
sched = schedFact.GetScheduler();
sched.JobFactory = new AutofacJobFactory(container);
sched.Start();
// construct job info
JobDetailImpl jobDetail = new JobDetailImpl("1Job", null, typeof(ReleaseJob ));
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("1JobTrigger")
.WithSimpleSchedule(x => x
.RepeatForever()
.WithIntervalInSeconds(5)
)
.StartNow()
.Build();
sched.ScheduleJob(jobDetail, trigger);
Note that in this example we are not using JobBuilder.Create<ReleaseJob>() anymore, instead we pass the details of the job to be created via the JobDetailImpl object and by doing this the scheduler's jobfactory (the AutofacJobFactory) is responsible for instantiating the job and Dependency injection can occur.

How to fake an HttpContext and its HttpRequest to inject them in a service constructor

In a console application, I would like to use a service that would normally need the current http context to be passed to its constructor. I am using Ninject, and I think I can simply fake an http context and define the proper binding, but I have been struggling with this for a few hours without success.
The details:
The service is actually a mailing service that comes from an ASP.Net MVC project. I am also using Ninject for IoC. The mail service needs the current http context to be passed to its constructor. I do the binding as follows:
kernel.Bind<IMyEmailService>().To<MyEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new HttpContextWrapper(HttpContext.Current));
However, I would like now to use this mailing service in a console application that will be used to run automated tasks at night. In order to do this, I think I can simply fake an http context, but I have been struggling for a few hours with this.
All the mailing service needs from the context are these two properties:
httpContext.Request.UserHostAddress
httpContext.Request.RawUrl
I thought I could do something like this, but:
Define my own fake request class:
public class AutomatedTaskHttpRequest : SimpleWorkerRequest
{
public string UserHostAddress;
public string RawUrl;
public AutomatedTaskHttpRequest(string appVirtualDir, string appPhysicalDir, string page, string query, TextWriter output)
: base(appVirtualDir, appPhysicalDir, page, query, output)
{
this.UserHostAddress = "127.0.0.1";
this.RawUrl = null;
}
}
Define my own context class:
public class AutomatedTasksHttpContext
{
public AutomatedTaskHttpRequest Request;
public AutomatedTasksHttpContext()
{
this.Request = new AutomatedTaskHttpRequest("", "", "", null, new StringWriter());
}
}
and bind it as follows in my console application:
kernel.Bind<IUpDirEmailService>().To<UpDirEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new AutomatedTasksHttpContext());
Unfortunately, this is not working out. I tried various variants, but none was working. Please bear with me. All that IoC stuff is quite new to me.
I'd answered recently about using a HttpContextFactory for testing, which takes a different approach equally to a console application.
public static class HttpContextFactory
{
[ThreadStatic]
private static HttpContextBase _serviceHttpContext;
public static void SetHttpContext(HttpContextBase httpContextBase)
{
_serviceHttpContext = httpContextBase;
}
public static HttpContextBase GetHttpContext()
{
if (_serviceHttpContext!= null)
{
return _serviceHttpContext;
}
if (HttpContext.Current != null)
{
return new HttpContextWrapper(HttpContext.Current);
}
return null;
}
}
then in your code to this:
var rawUrl = HttpContextFactory.GetHttpContext().Request.RawUrl;
then in your tests use the property as a seam
HttpContextFactory.SetHttpContext(HttpMocks.HttpContext());
where HttpMocks has the following and would be adjusted for your tests:
public static HttpContextBase HttpContext()
{
var context = MockRepository.GenerateMock<HttpContextBase>();
context.Stub(r => r.Request).Return(HttpRequest());
// and stub out whatever else you need to, like session etc
return context;
}
public static HttpRequestBase HttpRequest()
{
var httpRequest = MockRepository.GenerateMock<HttpRequestBase>();
httpRequest.Stub(r => r.UserHostAddress).Return("127.0.0.1");
httpRequest.Stub(r => r.RawUrl).Return(null);
return httpRequest;
}

Resources