System.ObjectDisposedException:无法访问已处置的对象,ASP.NET Core 3.1 [英] System.ObjectDisposedException: Cannot access a disposed object, ASP.NET Core 3.1

查看:139
本文介绍了System.ObjectDisposedException:无法访问已处置的对象,ASP.NET Core 3.1的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在ASP.NET Core 3.1中使用SignalR编写API.我对.NET Core完全陌生,对SignalR也很陌生.我在执行在事务中运行的MongoDB(Atlas)查询时遇到问题.似乎在执行查询之前,事务会话即将到期.我很确定这是一个 async / await 问题,但似乎无法解决.

I am writing an API using SignalR in ASP.NET Core 3.1. I am totally new to .NET Core, and am pretty new to SignalR too. I am having an issue executing MongoDB (Atlas) queries which are running in transactions. It appears that the transaction sessions are expiring before the queries are executing. I'm quite sure it's a async/await issue, but don't seem to be able to fix it.

我的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()方法是对事务/会话过程进行概括的一种尝试,如下所示:

My ExecuteInTransaction() method is an attempt to generalise the transaction/session process, and looks like this:

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()中指出发生错误的那一行.完整的错误如下所示:

I have indicated the line in the UpdateProfile() where the error occurs. The full error looks like this:

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会话,IEnumerable 1请求,BulkWriteOptions选项)在MongoDB.Driver.MongoCollectionImpl 1.BulkWriteAsync(IClientSessionHandle会话,IEnumerable 1请求,BulkWriteOptions选项,CancellationToken cancelleToken)中在IndistinctChatter.API.Services.Business.Profile.<> c__DisplayClass5_0上的MongoDB.Driver.MongoCollectionBase 1.ReplaceOneAsync(FilterDefinition 1过滤器,TDocument替换,ReplaceOptions选项,Func 3 bulkWriteAsync)处./Volumes/Data/Users/marknorgate/Documents/Development/source/indistinct-chatter/api-dotnet/Services/Business/Profile.cs:第48行中的<< UpdateProfile> b__0> d.MoveNext()-从前一个引发异常的位置开始的堆栈跟踪---在System.Threading.Tasks.Task.c.< ThrowAsync> b__139_1(对象状态)在System.Threading.QueueUserWorkItemCallback处.c.< .cctor> b__6_0(QueueUserWorkItemCallback quwi)at System.Threading.ExecutionContext.RunForThreadPoolUnsafe [TState](ExecutionContext executeContext,Action 1回调,TState&状态)在System.Threading.QueueUserWorkItemCallback.Execute()在System.Threading.ThreadPoolWorkQueue.Dispatch()在System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

System.ObjectDisposedException: Cannot access a disposed object. Object name: 'MongoDB.Driver.Core.Bindings.CoreSessionHandle'. at MongoDB.Driver.Core.Bindings.WrappingCoreSession.ThrowIfDisposed() at MongoDB.Driver.Core.Bindings.WrappingCoreSession.get_IsInTransaction() at MongoDB.Driver.ClientSessionHandle.get_IsInTransaction() at MongoDB.Driver.MongoCollectionImpl1.CreateBulkWriteOperation(IClientSessionHandle session, IEnumerable1 requests, BulkWriteOptions options) at MongoDB.Driver.MongoCollectionImpl1.BulkWriteAsync(IClientSessionHandle session, IEnumerable1 requests, BulkWriteOptions options, CancellationToken cancellationToken) at MongoDB.Driver.MongoCollectionBase1.ReplaceOneAsync(FilterDefinition1 filter, TDocument replacement, ReplaceOptions options, 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, Action1 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 session.CommitTransactionAsync(); 行首先执行.

So it seems the session object is expiring before the await _userCollectionService.UpdateAsync(session, existingUser.Id, existingUser); line is executing. The line await session.CommitTransactionAsync(); is executing first.

我要去哪里错了?我感觉到 Task 即将出现...

Where am I going wrong? I feel a Task coming on...

推荐答案

您的 DatabaseAction 不是异步委托.它似乎是 Action< T> ,但是您真正需要的是 Func< T,Task> .因此,当您执行 async session => 时,该代码将变成一个 async void 签名.由于它是 void ,因此您无法等待.这导致您的 async 代码被调度到线程池线程中,并且该调用立即返回.这具有 await session.CommitTransactionAsync()提交的连锁效果,而您的委托甚至还没有开始运行.现在,您在 ExecuteInTransaction 中的代码被视为完成";然后退出,并由于 using 块的原因而处理 session .

Your DatabaseAction is not an asynchronous delegate. It seems to be Action<T> but what you really need is Func<T, Task>. So when you do async session => that code gets turned into an async void signature. Due to it being void, you cannot await it. This leads to your async code being scheduled to a threadpool thread and the call immediately returns. This has the knock on effect that await session.CommitTransactionAsync() commits while potentially, your delegate hasn't even started running yet. Your code inside ExecuteInTransaction is now considered "done" and exits, disposing your session along the way due to using block.

要解决此问题,您需要更改您的 DatabaseAction 签名,然后 await databaseAction(session);

To fix this, you need to change your DatabaseAction's signature and then await databaseAction(session);

这篇关于System.ObjectDisposedException:无法访问已处置的对象,ASP.NET Core 3.1的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆