C# 中的异常未按预期运行
Posted
技术标签:
【中文标题】C# 中的异常未按预期运行【英文标题】:Exceptions in C# not behaving as expected 【发布时间】:2021-02-04 21:46:28 【问题描述】:我正在为我的 React 前端应用程序开发一个 ASP.NET Core 3.1 API。
我的问题是我的异常并没有像我预期的那样通过我的对象层次结构传播。我认为这可能是由于一些多线程问题,但我对 C# 的了解不够肯定!我正在学习 Pluralsight,但我目前正在学习网络,这对我没有帮助!
调用代码是一个 SignalR Hub 方法,如下所示:
public async Task<bool> UpdateProfile(object profileDto)
try
ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
_profile.UpdateProfile(profile);
return true;
catch (Exception e)
return false;
我希望_profile.UpdateProfile(profile);
中抛出或未处理的任何异常都会导致此处的异常块返回 false。我的UpdateProfile()
看起来像这样:
public void UpdateProfile(ProfileDTO profileDto)
_databaseService.ExecuteInTransaction(async session =>
// simulate an error
throw new Exception("Some exception");
);
...我的ExecuteInTransaction()
看起来像这样:
public async void ExecuteInTransaction(Func<IClientSessionHandle, Task> databaseAction)
using var session = await Client.StartSessionAsync();
try
session.StartTransaction();
await databaseAction(session);
await session.CommitTransactionAsync();
catch(Exception e)
await session.AbortTransactionAsync();
throw e;
我希望UpdateProfile()
中引发的异常会逐渐传播到ExecuteInTransaction()
中的catch 块——它确实如此——但更进一步,我希望这个异常会传播到集线器UpdateProfile()
方法。相反,它最终出现在 System.Runtime.ExceptionServices
命名空间中的 ExceptionDispatchInfo
类的 Throw()
方法中。
阅读此文件中的 cmets 让我觉得这是一个线程问题,但我对 C# 中的线程如何工作还不够了解。 UpdateProfile()
中抛出的异常是否有可能达到我的 Hub UpdateProfile()
的顶层? (刚刚注意到它们的名字相同令人困惑)。
【问题讨论】:
附带说明,请使用throw;
,而不是throw e;
,这样您就不会在异常中丢失原始调用堆栈。
Gotchya,好的,谢谢。
【参考方案1】:
您的问题是ExecuteInTransaction
的async void
签名。
Async void 方法具有不同的错误处理语义。当异步任务或异步任务方法抛出异常时,会捕获该异常并将其放置在任务对象上。对于 async void 方法,没有 Task 对象,因此 任何从 async void 方法抛出的异常都将直接在 async void 方法启动时处于活动状态的 SynchronizationContext 上引发 Source
这意味着,如果您使用的是没有 SynchronizationContext
的 ASP.NET Core,如果您不乱用,大多数很可能会在线程池线程上抛出异常任务调度器。如果您使用的是较旧的 .NET 框架代码,它将位于捕获的上下文中,但无论哪种方式,您对异常处理的了解都不适用于此处。您可以通过订阅AppDomain.UnhandledException
来捕获这些异常,但没有人愿意在可维护的代码中这样做。
要解决此问题,请将 public async void ExecuteInTransaction
更改为 public async Task ExecuteInTransaction
,将 public void UpdateProfile
更改为 public async Task UpdateProfile
并像这样调用它:
public async Task<bool> UpdateProfile(object profileDto)
try
ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
await _profile.UpdateProfile(profile);
return true;
catch (Exception e)
return false;
public async Task UpdateProfile(ProfileDTO profileDto)
await _databaseService.ExecuteInTransaction(async session =>
// simulate an error
throw new Exception("Some exception");
);
【讨论】:
哦,解决了。了不起的知识,谢谢!以上是关于C# 中的异常未按预期运行的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot中的Spring安全配置未按预期工作[重复]