![](/img/trans.png)
[英]System.ObjectDisposedException: Cannot access a disposed object
[英]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.MongoCollectionImpl1.BulkWriteAsync(IClientSessionHandle session, IEnumerable
1個請求,BulkWriteOptions選項的CancellationToken的CancellationToken)在MongoDB.Driver.MongoCollectionBase1.ReplaceOneAsync(FilterDefinition
1個濾波器,TDocument更換,ReplaceOptions選項, Func3 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.