How to open database connection in a BackgroundJob in ABP application - aspnetboilerplate

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.

Related

Entity Framework 6 "DbContext has been disposed" exception

Something very strange is happening in production, and it only happens in production. I have a Web API running and in one of the APIs, there is a repository created in the constructor and used in the functions. This is how the flow of a request works:
HTTP request comes in
MVC API controller decides which "worker" class to instantiate and creates it using Activator.CreateInstance
API controller calls worker.OnExecute inside of a Task.Run() and returns the http response
Worker calls _engine.Execute
Each worker instantiates another "engine" class that has all of the logic.
The engine in case constructs 3 repositories created using a UnitOfWork that is created per engine instance, like so:
public class MyWorker : Worker
{
private readonly MyEngine _engine;
public MyWorker()
{
_engine = new MyEngine();
}
protected override WorkerResult OnExecute(JObject data, CancellationToken cta)
{
return new WorkerResult(HttpStatusCode.OK, _engine.Execute(data));
}
}
public class MyEngine : EngineBase
{
private BaseRepository<Order> OrderRepo { get; set; }
private BaseRepository<OrderItem> OrderItemRepo { get; set; }
public MyEngine()
{
OrderRepo = new BaseRepository<Order>(MyUnitOfWork);
OrderItemRepo = new BaseRepository<OrderItem>(MyUnitOfWork);
}
public string Execute(JObject data)
{
return IsOrderValid(data).ToString();
}
public bool IsOrderValid(JObject data)
{
var orderId = data.Value<int>("OrderId");
// Without this line it crashes. With this line it crashes
//OrderRepo = new BaseRepository<Order>(InternationalWork);
// This is where it crashes
Order order = OrderRepo.First(x => x.OrderID == orderId);
// more code
}
}
public class EngineBase : UnitOfWorker, IDisposable
{
private UnitOfWork _myUnitOfWork;
public EngineBase() { }
public UnitOfWork MyUnitOfWork
{
get
{
return _myUnitOfWork ?? (_myUnitOfWork = new UnitOfWork(new DbContextAdapter(new MyDbContext())));
}
}
}
This is the actual stack trace:
The operation cannot be completed because the DbContext has been disposed.
StackTrace1
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ObjectContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.CreateObjectQuery(Boolean asNoTracking, Nullable`1 streaming, IDbExecutionStrategy executionStrategy)
at System.Data.Entity.Internal.Linq.InternalSet`1.InitializeUnderlyingTypes(EntitySetTypePair pair)
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
The stack trace shows "FirstOrDefault" because OrderRepo.First internally calls DbSet.FirstOrDefault, like so:
public virtual T First(Expression<Func<T, bool>> query)
{
return _dbSet.FirstOrDefault(query);
}
I'm stumped because each worker is created per http request. Each DBContext is created per engine instance so I don't know how it could be disposed when it was just created in the constructor. And this only happens on the production web server where I presume it's being called more. Any tips would be greatly appreciated.

Why is ServiceStack's SaveUserAuth not saving to the database?

I am trying to give users the ability to change their display name which happens to be in IAuthSession interface and commit the change to the database.
I register a container via the AppHost:
container.Register<IUserAuthRepository>(new MongoDBAuthRepository(new MongoDBClient().MongoDB, true));
Then in my service I do the following:
public class HandlerService : Service
{
public HandlerService(IUserAuthRepository userAuthRepository)
{
this._userAuthRepository = userAuthRepository;
}
private readonly IUserAuthRepository _userAuthRepository;
public void SaveDisplayName(string displayName) {
var session = base.SessionAs<CustomUserSession>(); // CustomUserSession inherits AuthUserSession
if (!session.DisplayName.EqualsIgnoreCase(displayName))
{
session.DisplayName = displayName;
_userAuthRepository.SaveUserAuth(session);
}
}
}
Although the code hits _userAuthRepository.SaveUserAuth, no exception is raised and nothing is returned since the method is void. However the data does not actually get committed to the Database. In this particular case MongoDB.
Any ideas why it is not saving it or why no exceptions are thrown if there was a problem?

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;
}

Ninject Scope issue with Tasks/Threads

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;
}

How do I use Universal Membership Provider, EF, and MiniProfiler together?

If I use the Universal Membership Provider and a seperate database, Entity Framework and enable Mini Profiler for EF 4.2. I get error {"There is already an object named 'Applications' in the database."} when I first hit a line checking user credentials in my home view.
If I turn remove MiniProfilerEF.Initialize(); then I stop getting the error.
Any ideas?
Can I stop profiling the defaultconnection?
I have been banging my head against this issue for awhile now. Did some more digging today and was able to get it working. Here is what I did. In MiniProfiler.cs I defined two methods as follows:
public static DbConnection GetConnection()
{
var connectionString = ConfigurationManager.ConnectionStrings["MyModelConnectionString"].ConnectionString;
var entityConnStr = new EntityConnectionStringBuilder(connectionString);
var realConnection = new SqlConnection(entityConnStr.ProviderConnectionString);
return realConnection;
}
public static IMyModelsInterface GetProfiledContext()
{
var connection = new MvcMiniProfiler.Data.EFProfiledDbConnection(GetConnection(), MiniProfiler.Current);
var context = connection.CreateObjectContext<MyModel>();
return context;
}
NOTE: These two methods probably shouldn't be defined in MinProfilerPackage, but this was my first past/hack to get it working.
Then call GetProfiledContext() and use the context returned whenever you want the queries profiled. I injected this profile context into my controller factory using Ninject. My call looks something like this:
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
private void AddBindings()
{
var context = MiniProfilerPackage.GetProfiledContext();
IUnitOfWork uow = new UnitOfWork(context);
ninjectKernel.Bind<IRepository>().To<GenericRepository>().WithConstructorArgument("paramUnitOfWork", uow);
// ... rest of the method
}
NinjectControllerFactory is my controller factory that gets set in Application_Start.
protected void Application_Start()
{
// Add in DI for controller and repo associations
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
// ... rest of the method
}

Resources