The generic repository for an entity with the [Audited] decoration will cause an exception due to DbContext being disposed prematurely. I am using Hangfire.
[Audited]
public class MyAuditedEntity: FullAuditedEntity<Guid>
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
public class TroubleshootingBackgroundJob : BackgroundJob<TroubleshootingBackgroundJobArgs>, TransientDependency
{
private readonly IRepository<MyAuditedEntity, Guid> _repository;
public TroubleshootingBackgroundJob(IRepository<MyAuditedEntity, Guid> repository)
{
_repository = repository;
}
[UnitOfWork]
public override void Execute(TroubleshootingBackgroundJobArgs args)
{
repository.GetAll().ToList(); // <== Generates the exception
}
}
Exception details:
System.ObjectDisposedException HResult=0x80131622 Message=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. ObjectDisposed_ObjectName_Name Source=Microsoft.EntityFrameworkCore StackTrace: at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed() at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() at Microsoft.EntityFrameworkCore.DbContext.get_Model() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet
1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet
1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Linq.IQueryable.get_Provider() at System.Linq.Queryable.Where[TSource](IQueryable
1 source, Expression`1 predicate)
Am I missing something or is this a bug?
UPDATE It seems the problem is with all types of FullAuditedEntity, not the [Audited] decoration...
20 Answer(s)
-
0
can you share full error stacktrace?
could not find
TroubleshootingBackgroundJob
in the stacktrace you shared above -
0
@ryancyq It is the only line omitted from the Stacktrace as I actually copied it from production code. I indicated the originating line in the code snippet with the comment
// <== Generates the exception
-
0
@mightyit having the comple error stacktrace helps us to understand the problem better. e.g the job execution lifecycle.
without it, it is hard to debug the problem.
-
0
Here it is, but, as I mentioned, there really is no other useful information:
System.ObjectDisposedException HResult=0x80131622 Message=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. ObjectDisposed_ObjectName_Name Source=Microsoft.EntityFrameworkCore StackTrace: at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed() at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() at Microsoft.EntityFrameworkCore.DbContext.get_Model() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet
1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet
1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Linq.IQueryable.get_Provider() at System.Linq.Queryable.Where[TSource](IQueryable
1 source, Expression`1 predicate) at MyNameSpace.TroubleshootingBackgroundJob.Execute(TroubleshootingBackgroundJobArgs args) in REDACTED\TroubleshootingBackgroundJob.cs:line 17 -
0
@ryancyq @ismcagdas were you able to recreate the issue?
-
0
Have you marked Execute method of your BackgroundJob with UnitOfWork attribute ?
[UnitOfWork] public override void Execute(TestJobArgs args) { // ... }
-
0
Hi @ismcagdas
Yes, I have. I am still getting the same issue though.
-
0
to filter down the issue, make a simple entity like below, this will show us if there's a problem with writing audit log after DbContext disposes.
//[Audited] removed public class MyAuditedEntity: Entity<Guid> // FullAuditedEntity<Guid> { public virtual string Name { get; set; } public virtual string Description { get; set; } }
-
0
@alper the issue persists
-
0
Try this workaround
[UnitOfWork] protected override void Execute(TroubleshootingBackgroundJobArgs args) { DoJob(); } [UnitOfWork] protected virtual void DoJob() { repository.GetAll().ToList(); }
-
0
if the above solution doesn't work, try to create a new transaction like below
[UnitOfWork] public override void Execute(TroubleshootingBackgroundJobArgs args) { using(var uow = UnitOfWorkManager.Begin()) { repository.GetAll().ToList(); uow.Complete(); } }
-
0
Hi @alper
Thanks for the assistance. I have tried both ways, unsuccesfully unfortunately.
However, when I did this...
[UnitOfWork] public override void Execute(TroubleshootingBackgroundJobArgs args) { using(var uow = UnitOfWorkManager.Begin()) // <== Exception generated here... { repository.GetAll().ToList(); uow.Complete(); } }
.... I got the following exception:
Abp.AbpException HResult=0x80131500 Message=Must set UnitOfWorkManager before use it. Source=Abp StackTrace: at Abp.BackgroundJobs.BackgroundJob
1.get_UnitOfWorkManager() at Sanctions.SearchServices.Infrastructure.UnscConsolidatedSanctionList.Infrastructure.Persons.PersonMonitoringBatch.PersonMonitoringBatchSearchJob.ExecuteJob(PersonMonitoringBatchSearhJobArgs args) in D:\Projects\ZenDetect\Modules\Sanctions\Sanctions.SearchServices.Infrastructure.UnscConsolidatedSanctionList\Infrastructure\Persons\PersonMonitoringBatch\PersonMonitoringBatchSearchJob.cs:line 54 at Sanctions.SearchServices.Infrastructure.UnscConsolidatedSanctionList.Core.BatchSearchJobBase
1.Execute(TBatchSearhJobArgs args) in D:\Projects\ZenDetect\Modules\Sanctions\Sanctions.SearchServices.Infrastructure.UnscConsolidatedSanctionList\Core\BatchSearchJobBase.cs:line 20Seems like the Unit of Work object is not resolving, for some reason?
-
1
Weird! Then inject
UnitOfWorkManager
traditionally. Let's see ifUnitOfWorkManager
is being resolved successfully.public class TroubleshootingBackgroundJob : BackgroundJob<TroubleshootingBackgroundJobArgs>, TransientDependency { private readonly IRepository<MyAuditedEntity, Guid> _repository; private readonly IUnitOfWorkManager _unitOfWorkManager; public TroubleshootingBackgroundJob(IRepository<MyAuditedEntity, Guid> repository, IUnitOfWorkManager unitOfWorkManager) { _repository = repository; _unitOfWorkManager = unitOfWorkManager; } [UnitOfWork] public override void Execute(TroubleshootingBackgroundJobArgs args) { using(var uow = _unitOfWorkManager.Begin()) // <== Exception generated here... { repository.GetAll().ToList(); uow.Complete(); } } }
Also you can try disabling UnitOfWork... This might give us some clue!
[UnitOfWork(IsDisabled = true)] public override void Execute(TroubleshootingBackgroundJobArgs args) { repository.GetAll().ToList(); }
-
0
OK, interesting....
- Explicitly injecting IUnitOfWork, as you suggested, seems to work.
- Running with the attribute
[UnitOfWork(IsDisabled = true)]
throws the original exception
Why would the IUnitOfWork not resolve for the
BackgroundJob<TArgs>
base class? How do I go about fixing it? -
0
first of all I see that you use
TransientDependency
.public class TroubleshootingBackgroundJob : BackgroundJob<TroubleshootingBackgroundJobArgs>, TransientDependency
Is it some custom class that you've made? because the original one is
ITransientDependency
.And you can use GetAllList as well which also gets a predicate.
[UnitOfWork] public override void Execute(TroubleshootingBackgroundJobArgs args) { repository.GetAllList() }
-
0
@alper
Yes, that's a typo. It should be (and is)
ITransientDependency
. -
0
Hi @mightyit
Is this problem resolved ?
Thanks,
-
0
Hi @ismcagdas
No. I had to use workarounds.
-
0
[UnitOfWork] Fixed it for me.
-
0
[UnitOfWork] is the fix for me in .NET Core 3.1 template to implement a default .NET based background service.