簡體   English   中英

System.ObjectDisposedException:無法訪問已處理的對象,ASP.NET Core 3.1

[英]System.ObjectDisposedException: Cannot access a disposed object, ASP.NET Core 3.1

我正在 ASP.NET Core 3.1 中使用 SignalR 編寫 API。 我對 .NET Core 完全陌生,對 SignalR 也很陌生。 我在執行在事務中運行的 MongoDB (Atlas) 查詢時遇到問題。 事務會話似乎在查詢執行之前到期。 我很確定這是一個async / await問題,但似乎無法修復它。

我的 Hub 方法如下所示:

public async Task<bool> UpdateProfile(object profileDto)
{
  try
  {
    ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
    _profile.UpdateProfile(profile);
    return true;
  }
  catch
  {
    return false;
  }
}

_profile.UpdateProfile()方法如下所示:

public void UpdateProfile(ProfileDTO profileDto)
{
  _databaseService.ExecuteInTransaction(async session =>
  {
    var profile = _mapper.Map<ProfileModel>(profileDto);
    var existingUser = (await _userCollectionService
        .FindAsync(session, user => user.Profille.Sub == profileDto.sub)
      ).FirstOrDefault();

    if (existingUser == null)
    {
      var newUser = new UserModel();
      newUser.Profille = profile;
      await _userCollectionService.CreateAsync(session, newUser);
    }
    else
    {
      existingUser.Profille = profile;
      // the error occurs on the following line
      await _userCollectionService.UpdateAsync(session, existingUser.Id, existingUser);
    }
  });
}

我的ExecuteInTransaction()方法試圖概括事務/會話過程,如下所示:

public async void ExecuteInTransaction(DatabaseAction databaseAction)
{
  using (var session = await Client.StartSessionAsync())
  {
    try
    {
      session.StartTransaction();
      databaseAction(session);
      await session.CommitTransactionAsync();
    }
    catch (Exception e)
    {
      await session.AbortTransactionAsync();
      throw e;
    }
  }
}

我已經在UpdateProfile()中指出了發生錯誤的那一行。 完整的錯誤如下所示:

System.ObjectDisposedException:無法訪問已處理的對象。 對象名稱:'MongoDB.Driver.Core.Bindings.CoreSessionHandle'。 在 MongoDB.Driver.Core.Bindings.WrappingCoreSession.ThrowIfDisposed() 在 MongoDB.Driver.Core.Bindings.WrappingCoreSession.get_IsInTransaction() 在 MongoDB.Driver.ClientSessionHandle.get_IsInTransaction() 在 MongoDB.Driver.MongoCollectionImpl 1.CreateBulkWriteOperation(IClientSessionHandle session, IEnumerable 1個請求,BulkWriteOptions選項)在MongoDB.Driver.MongoCollectionImpl 1.BulkWriteAsync(IClientSessionHandle session, IEnumerable 1個請求,BulkWriteOptions選項的CancellationToken的CancellationToken)在MongoDB.Driver.MongoCollectionBase 1.ReplaceOneAsync(FilterDefinition 1個濾波器,TDocument更換,ReplaceOptions選項, Func 3 bulkWriteAsync) at IndistinctChatter.API.Services.Business.Profile.<>c__DisplayClass5_0.<<UpdateProfile>b__0>d.MoveNext() in /Volumes/Data/Users/marknorgate/Documents/Development/source/indistinct-chatter/api-dotnet/Services/Business/Profile.cs:line 48 --- End of stack trace from previous location where exception was thrown --- at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_1(Object state) at System.Threading.QueueUserWorkItemCallback.<>c.<.cctor>b__6_0(QueueUserWorkItemCallback quwi) at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action 3 bulkWriteAsync) at IndistinctChatter.API.Services.Business.Profile.<>c__DisplayClass5_0.<<UpdateProfile>b__0>d.MoveNext() in /Volumes/Data/Users/marknorgate/Documents/Development/source/indistinct-chatter/api-dotnet/Services/Business/Profile.cs:line 48 --- End of stack trace from previous location where exception was thrown --- at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_1(Object state) at System.Threading.QueueUserWorkItemCallback.<>c.<.cctor>b__6_0(QueueUserWorkItemCallback quwi) at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action 1 callback, TState& state) at System.Threading.QueueUserWorkItemCallback.Execute() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

因此, session對象似乎在await _userCollectionService.UpdateAsync(session, existingUser.Id, existingUser);之前到期await _userCollectionService.UpdateAsync(session, existingUser.Id, existingUser); 線正在執行。 該行await session.CommitTransactionAsync(); 首先執行。

我哪里錯了? 我感覺有Task要來了...

您的DatabaseAction不是異步委托。 它似乎是Action<T>但你真正需要的是Func<T, Task> 因此,當您執行async session => ,該代碼會變成async void簽名。 由於它是void ,你不能await它。 這會導致您的async代碼被調度到線程池線程並且調用立即返回。 這會產生await session.CommitTransactionAsync()提交的連鎖反應,而您的委托甚至還沒有開始運行。 您在ExecuteInTransaction代碼現在被視為“完成”並退出,由於using塊而在此過程中處理您的session

要解決此問題,您需要更改DatabaseAction的簽名,然后await databaseAction(session);

好的,我找到了解決方法,盡管我對解決方案並不完全滿意。

我發現了AutoResetEvent() ,所以修改了我的代碼:

public async void ExecuteInTransaction(DatabaseAction databaseAction)
{
  AutoResetEvent autoResetEvent = new AutoResetEvent(false);

  using var session = await Client.StartSessionAsync();

  try
  {
    session.StartTransaction();
    databaseAction(session, autoResetEvent);
    autoResetEvent.WaitOne();
    await session.CommitTransactionAsync();
  }
  catch (Exception e)
  {
    await session.AbortTransactionAsync();
    throw e;
  }
}

public void UpdateProfile(ProfileDTO profileDto)
{
  _databaseService.ExecuteInTransaction(async (session, autoResetEvent) =>
  {
    var profile = _mapper.Map<ProfileModel>(profileDto);
    var existingUser = (await _userCollectionService
        .FindAsync(session, user => user.Profille.Sub == profileDto.sub)
      ).FirstOrDefault();

    if (existingUser == null)
    {
      var newUser = new UserModel();
      newUser.Profille = profile;
      await _userCollectionService.CreateAsync(session, newUser);
    }
    else
    {
      existingUser.Profille = profile;
      await _userCollectionService.UpdateAsync(session, existingUser.Id, existingUser);
    }

    autoResetEvent.Set();
  });
}

似乎有效,但這是我需要記住的每個數據庫操作結束時的額外步驟。 如果有人可以改進這一點,我會很高興聽到它!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM