Base solution for your next web application
Open Closed

Getting TenantManager is disposed while accessing it in BackgroundJob #10402


User avatar
0
rcasaca created
  • What is your product version?
    • 10.0.3
  • What is your product type (Angular or MVC)?
    • MVC
  • What is product framework type (.net framework or .net core)?
    • .net core

Hi all, I'm trying to access TenantManager in BackgroundJob. But i'm getting is disposed error.

System.ObjectDisposedException Cannot access a disposed context instance. A common cause of this error is disposing a context instance 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 instance, or wrapping it 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: 'CustomerHubDbContext'.

System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance 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 instance, or wrapping it 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: 'CustomerHubDbContext'. at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed() at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() at Microsoft.EntityFrameworkCore.DbContext.get_Model() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator() at CustomerHub.Jobs.SyncBusinessPartners.Execute(Int32 x) in C:_GIT\SyncBusinessPartners.cs:line 42

Below the code of SyncBusinessPartners Job (highlighted the row in error - line42)

public class SyncBusinessPartners : BackgroundJob<int>, ITransientDependency
{
	private readonly TenantManager _tenantManager;
	private readonly IAbpSession _abpSession;
	private readonly UserManager _userManager;
	private readonly ILogger<SyncBusinessPartners> _logger;

	public SyncBusinessPartners(
		TenantManager tenantManager,
		IAbpSession abpSession,
		UserManager userManager,
		ILogger<SyncBusinessPartners> logger
	)
	{
		_tenantManager = tenantManager;
		_abpSession = abpSession;
		_userManager = userManager;
		_logger = logger;
		LocalizationSourceName = CustomerHubConsts.LocalizationSourceName;
	}

	public override void Execute(int x)
	{
		_logger.LogInformation(L("JobExecutionStarted", args: $"{nameof(SyncBusinessPartners)}"));

		**foreach (var t in _tenantManager.Tenants)**
		{
			var admin = _userManager.FindByNameAsync("admin").Result;
		}

		_logger.LogInformation(L("JobExecutionFinished", args: nameof(DocumentDto)));
	}
}

I've tried to use UnitOfWork attribute on my method Execute. But when i do this, all the information to be synced in bulk and not iteration by iteration.

Thanks in advance!


3 Answer(s)
  • User Avatar
    0
    gterdem created
    Support Team

    Can you try injecting IUnitOfWorkManager and try using like: <br>

    public override void Execute(int x)
    {
    	_logger.LogInformation(L("JobExecutionStarted", args: $"{nameof(SyncBusinessPartners)}"));
    
            using(var unitOfWork = _unitOfWorkManager.Begin())
            {
                foreach (var t in _tenantManager.Tenants)
    	    {
    		var admin = _userManager.FindByNameAsync("admin").Result;
    	    }
    
    	    _logger.LogInformation(L("JobExecutionFinished", args: nameof(DocumentDto)));
    
                unitOfWork.Complete();
            }	
    }
    

    Or put  using(var unitOfWork = _unitOfWorkManager.Begin()) inside foreach if you want separate UoW for each userManager operation.

  • User Avatar
    0
    rcasaca created

    Hi gterdem.

    I've already tried to use UnitOfWorks, but with no luck. Some examples below.

    public override void Execute(int x)
    {
    	_logger.LogInformation(string.Format("Starting {0}...", nameof(T)));
    
    	using (var unitOfWork = _unitOfWorkManager.Begin())
    		foreach (var t in _tenantManager.Tenants)
    			using (var tUnitOfWork = _unitOfWorkManager.Begin())
    			{
    				using (_unitOfWorkManager.Current.SetTenantId(t.Id))
    				{
    					var list = _externalWSClient.GetList<ItemDto>();
    					foreach (var i in list)
    					{
    						_localRepoService.ImportItemAsync(i).Result;
    						//Commit Here
    					}
    				}
    
    				tUnitOfWork.Complete();
    			}
    
    	_logger.LogInformation(string.Format("Finished {0}...", nameof(T)));
    }
    
    [UnitOfWork]
    public override void Execute(int x)
    {
    	_logger.LogInformation(string.Format("Starting {0}...", nameof(T)));
    
    	using (_unitOfWorkManager.Current.SetTenantId(t.Id))
    	{
    		var list = _externalWSClient.GetList<ItemDto>();
    		foreach (var i in list)
    		{
    			_localRepoService.ImportItemAsync(i).Result;
    			//Commit Here
    		}
    	}
    
    	_logger.LogInformation(string.Format("Finished {0}...", nameof(T)));
    }
    

    With both, TenantManager works. It retrieves all the tenants and i can iterate over them. What i'm missing right now, is the capability of saving on each item import. The commit only occurs when the job finishes, right after the Finished log.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @rcasaca

    You can call _unitOfWorkManager.Current.SaveChanges(); when you want to insert the records.