C# 异步,执行停止,发生了什么?
有些时候我真的知道自己在做什么;这不是其中之一 我试图理解为什么我的代码在执行特定异步操作时停止执行。它在同步执行时工作,在异步执行时不工作 这一切都发生在MVC应用程序中C# 异步,执行停止,发生了什么?,c#,asp.net,asynchronous,C#,Asp.net,Asynchronous,有些时候我真的知道自己在做什么;这不是其中之一 我试图理解为什么我的代码在执行特定异步操作时停止执行。它在同步执行时工作,在异步执行时不工作 这一切都发生在MVC应用程序中 AccountController通过AccountService创建一个新帐户,使我的控制器或多或少保持干净 在AccountService中,我们做了一些类似于用户帐户(ASP.NET标识)的事情,效果很好。没什么特别的 然后,我试图发送一封电子邮件“欢迎使用您的新帐户”,但一切都出了问题 我并不是特别在寻找解决办法
通过AccountController
创建一个新帐户,使我的控制器或多或少保持干净AccountService
- 在
中,我们做了一些类似于用户帐户(ASP.NET标识)的事情,效果很好。没什么特别的AccountService
- 然后,我试图发送一封电子邮件“欢迎使用您的新帐户”,但一切都出了问题
EmailSender.Send(message)命令的那一刻
在CreatePrimaryUser
方法中,account.AccountOwnerId=userId代码>行不再执行。也不例外。看起来一切都很顺利,但该方法只是停止执行
我哪里出错了
AccountController
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
public ActionResult Register(AccountViewModel model)
{
if (ModelState.IsValid)
{
try
{
BusinessServiceFacade.GetAccountService(RavenMasterSession).Create(model.ToModel(), model.SubscriptionPlanId);
return Redirect(string.Format("http://{0}.localhost:6257/Admin/GettingStarted",model.Name));
}
catch(AccountTakenException)
{
ModelState.AddModelError("", "Account name already taken");
}
catch (CustomException e)
{
ModelState.AddModelError("", "Error:" + e);
}
}
return View(model);
}
public static async Task SendSES(MailMessage message)
{
const string FROM = "from@domain.com";
message.From = new MailAddress(FROM);
const string SMTP_USERNAME = "user";
const string SMTP_PASSWORD = "pass";
const string HOST = "email-smtp.eu-west-1.amazonaws.com";
const int PORT = 587;
using (SmtpClient client = new SmtpClient(HOST, PORT))
{
client.Credentials = new System.Net.NetworkCredential(SMTP_USERNAME, SMTP_PASSWORD);
client.EnableSsl = true;
try
{
//client.Send(message);
await client.SendMailAsync(message);
}
catch (Exception ex)
{
throw ex;
}
}
}
GetAccountService创建
public async Task<Account> Create(Account account, string subscriptionPlanId)
{
if (IsAccountNameTaken(account.Name))
throw new AccountTakenException();
RavenMasterSession.Store(account); // register account in MasterDB
CreateTenantDatabase(account); // create tenantDB
var userId = await CreatePrimaryUser(account); // create account owner (first user)
account.AccountOwnerId = userId;
var stripeAccountResult = BusinessServiceFacade.GetStripeService(RavenMasterSession).CreateCustomer(account); // register account in Stripe
account.CustomerId = stripeAccountResult.CustomerId;
AddSubscription(account, subscriptionPlanId); // add subscription to account
return account;
}
这是在某个地方混合异步同步的一种症状,它会导致死锁,正如您所描述的那样。像下面这样更改您的调用代码,使其始终是异步的,否则使用异步并没有真正的好处。像这样更改它是没有问题的,因为您使用的是支持此操作签名的MVC
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
// change to async
public async Task<ActionResult> Register(AccountViewModel model)
{
if (ModelState.IsValid)
{
try
{
var service = BusinessServiceFacade.GetAccountService(RavenMasterSession);
// broken apart so you can see the await call
await service.Create(model.ToModel(), model.SubscriptionPlanId);
return Redirect(string.Format("http://{0}.localhost:6257/Admin/GettingStarted",model.Name));
}
catch(AccountTakenException)
{
ModelState.AddModelError("", "Account name already taken");
}
catch (CustomException e)
{
ModelState.AddModelError("", "Error:" + e);
}
}
return View(model);
}
旁注2
对于非操作方法(那些未在Mvc或Web API控制器上定义的方法),如果该方法是异步方法,则应使用Async作为命名约定的后缀。这样可以很容易地看到您可以在代码中的何处使用wait
或其他一些任务机制来执行代码。您可以看到,Microsoft通过FCL中内置的异步方法也做到了这一点。它不是必需的(没有什么可以强制执行),但它是一种良好的实践,因为它使代码更具可读性
变为Create
CreateAsync
变为CreatePrimaryUser
CreatePrimaryUserAsync
变为SendSES
SendSESAsync
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
// change to async
public async Task<ActionResult> Register(AccountViewModel model)
{
if (ModelState.IsValid)
{
try
{
var service = BusinessServiceFacade.GetAccountService(RavenMasterSession);
// broken apart so you can see the await call
await service.Create(model.ToModel(), model.SubscriptionPlanId);
return Redirect(string.Format("http://{0}.localhost:6257/Admin/GettingStarted",model.Name));
}
catch(AccountTakenException)
{
ModelState.AddModelError("", "Account name already taken");
}
catch (CustomException e)
{
ModelState.AddModelError("", "Error:" + e);
}
}
return View(model);
}
try {
// something that could throw exception
} catch(Exception ex) {
// do something with the exception like logging or retry or whatever
// if the code does nothing but a rethrow then remove the whole try/catch as it has no added value
throw;
}