Base solution for your next web application
Open Closed

Working with multiple repositories without losing DBcontext #5522


User avatar
0
davidharrison created

Hi Guys,

We're working on a function that needs to create or update records across 4 different entities.

When running each CreateOrEdit method synchronously we were receiving an "Existing task is running on the same context" error, and when running each CreateOrEdit method asynchronously, the DB context gets disposed of after the completion of the first method, thus raising "DBcontext disposed of" errors in the subsequent method calls.

We have tried implementing different unit of work methods around the CreateOrEdit method calls, to varying degrees of success, but really need all of the methods to operate under a single transaction, as all are required to succeed in order to continue.

If anyone has worked with a scenario like this before and can explain how to make this work or if there is a direction we can be pointed in, any help would be greatly appreciated.


8 Answer(s)
  • User Avatar
    0
    aaron created
    Support Team

    Can you show the code for both "synchronously" and "asynchronously"?

  • User Avatar
    0
    davidharrison created

    Running the functions synchronously

    _recordsFolderManager.CreateOrEditFolder(NewFolder);
    _recordManager.CreateOrEditRecord(NewRecord);
    _recordManager.CreateOrEditRecordMatter(NewRecordMatter);
    _recordManager.CreateOrEditRecordMatterItem(NewRecordMatterItem);
    

    Running the functions asynchronously

    await _recordsFolderManager.CreateOrEditFolder(NewFolder);
    await _recordManager.CreateOrEditRecord(NewRecord);
    await _recordManager.CreateOrEditRecordMatter(NewRecordMatter);
    await _recordManager.CreateOrEditRecordMatterItem(NewRecordMatterItem);
    

    We're also tried runnning the functions in a new unit of work

    using (var unitOfWork = _unitOfWorkManager.Begin())
                    {
                        await _recordsFolderManager.CreateOrEditFolder(NewFolder);
                        await _recordManager.CreateOrEditRecord(NewRecord);
                        await _recordManager.CreateOrEditRecordMatter(NewRecordMatter);
                        await _recordManager.CreateOrEditRecordMatterItem(NewRecordMatterItem);
                        unitOfWork.Complete();
                    }
    
  • User Avatar
    0
    aaron created
    Support Team

    If CreateOrEditFolder etc. are async, then you have to await them. Not putting await doesn't mean you run them synchronously. It means you don't wait for it to finish executing before starting another, which causes the "Existing task is running on the same context" error.

    Can you show the implementation of one of those methods?

  • User Avatar
    0
    davidharrison created

    Hi Aaron

    Thanks for the clarification around async awaiting.

    Here is the code we're currently trying to execute our functions from within:

    using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
                    {
                        NewRecord = _recordRepository.Get(Guid.Parse(RecordID));
                        await CreateOrEditRecord(NewRecord);
                        await _unitOfWorkManager.Current.SaveChangesAsync();
    
                        NewRecordMatter = _recordMatterRepository.Get(Guid.Parse(RecordMatterID));
                        await CreateOrEditRecordMatter(NewRecordMatter);
                        await _unitOfWorkManager.Current.SaveChangesAsync();
    
                        await uow.CompleteAsync();
                    }
    

    Here is one of the functions sets that get called. Each of the function sets follows the same pattern as this one.

    public async Task CreateOrEditRecord(Record Record)
            {
                if (Record.Id == null || !_recordRepository.GetAll().Any(i => i.Id == Record.Id))
                {
                    await CreateRecord(Record);
                }
                else
                {
                    await UpdateRecord(Record);
                }
            }
    
            [AbpAuthorize(AppPermissions.Pages_Records_Create)]
            private async Task CreateRecord(Record Record)
            {
                await _recordRepository.InsertAsync(Record);
            }
    
            [AbpAuthorize(AppPermissions.Pages_Records_Edit)]
            private async Task UpdateRecord(Record Record)
            {
                var record = await _recordRepository.FirstOrDefaultAsync((Guid)Record.Id);
                ObjectMapper.Map(Record, record);
            }
    

    The current UOW seems to get closed when returning from CreateOrEditRecord(), which disrupts the subsequent actions shown in the top code block.

  • User Avatar
    0
    aaron created
    Support Team

    Can you show the stack trace?

  • User Avatar
    0
    davidharrison created

    Here is the stack trace info in question:

    {System.NullReferenceException: Object reference not set to an instance of an object. at Syntaq.Falcon.Documents.DocumentsAppService.Automate(Object JSONObject) in D:\Source\Syntaq.Falcon\src\Syntaq.Falcon.Application\Documents\DocumentsAppService.cs:line 88}

    The null object is the _unitOfWorkManager.Current.

    Line 88 is marked below:

    84: using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
    85:                {
    86:                    NewRecord = _recordRepository.Get(Guid.Parse(RecordID));
    87:                    await CreateOrEditRecord(NewRecord);
    88:                    await _unitOfWorkManager.Current.SaveChangesAsync();
    
  • User Avatar
    0
    grinay created

    Try to put [UnitOfWork(IsDisabled = true)] attribute before you method. It will disable any other unit of work.

  • User Avatar
    0
    alper created
    Support Team

    make your methods virtual and add [UnitOfWork] attribute to them.