Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/35.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步,执行停止,发生了什么?_C#_Asp.net_Asynchronous - Fatal编程技术网

C# 异步,执行停止,发生了什么?

C# 异步,执行停止,发生了什么?,c#,asp.net,asynchronous,C#,Asp.net,Asynchronous,有些时候我真的知道自己在做什么;这不是其中之一 我试图理解为什么我的代码在执行特定异步操作时停止执行。它在同步执行时工作,在异步执行时不工作 这一切都发生在MVC应用程序中 AccountController通过AccountService创建一个新帐户,使我的控制器或多或少保持干净 在AccountService中,我们做了一些类似于用户帐户(ASP.NET标识)的事情,效果很好。没什么特别的 然后,我试图发送一封电子邮件“欢迎使用您的新帐户”,但一切都出了问题 我并不是特别在寻找解决办法

有些时候我真的知道自己在做什么;这不是其中之一

我试图理解为什么我的代码在执行特定异步操作时停止执行。它在同步执行时工作,在异步执行时不工作

这一切都发生在MVC应用程序中

  • AccountController
    通过
    AccountService
    创建一个新帐户,使我的控制器或多或少保持干净
  • AccountService
    中,我们做了一些类似于用户帐户(ASP.NET标识)的事情,效果很好。没什么特别的
  • 然后,我试图发送一封电子邮件“欢迎使用您的新帐户”,但一切都出了问题
我并不是特别在寻找解决办法,我更想知道发生了什么

执行wait
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

一些提示:正确设置代码格式。如果有缩进问题,只需选择缩进不正确的代码并按ctrl-k。单击此链接并立即尝试:。通常最好先在IDE中获得缩进,然后复制缩进。说“我不是特别想找到解决办法”,有点招来了反对票。你不必限定你为什么这么问。你有一个死锁问题,你不知道在哪里,你正在寻求帮助。那很好。暗示你真的没有问题=>浪费别人的时间。
[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;
}