Task.Wait在MVC5中锁定控制器的流程

Ric*_*lva 0 c# async-await asp.net-web-api asp.net-mvc-5

我们正在从MVC站点使用WebAPI.我们从这样的控制台应用程序中做了一个概念验证,使用了AddUser Method,它在我们的数据库中调节了一个新用户.

static void Main()
    {
        M_Users user = new M_Users();

        var newUser = new M_Users()
        {
            Password = "123",
            FirstName = "Andrew",
            Email = "someAndrew@someplace.com",
            AdminCode = 1,
            IsDisabled = false,
            CountryId = 48,
            CityId = 149,
            DepartmentId = 3,
            CreationDate = DateTime.Now,
            CreatorUserId = 1
        };
        var star = RunAsync(newUser);
        star.Wait();

        //THIS LINE IS REACHED ALWAYS
        Console.WriteLine(star.Result.UserID);
        Console.ReadLine();
    }

    static async Task<M_Users> AddUserAsync(M_Users newUser)
    {
        M_Users user = new M_Users();
        string requestUri = "api/User/AddUser";

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:44873/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // HTTP POST
            HttpResponseMessage response = await client.PostAsJsonAsync<M_Users>(requestUri, newUser);
            if (response.IsSuccessStatusCode)
            {
                Uri newUserUrl = response.Headers.Location;
                user = await response.Content.ReadAsAsync<M_Users>();
            }

            return user;
        }
    }
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,控制台应用程序和MVC应用程序中的AddUserAsync方法都被完全相同,并且biz层中的方法也是相同的,此处未描述的似乎并不相关.

在控制台它只是工作.用户注册并控制打印用户在BD中保存的id.这几乎是我们需要证明的.从同步方法我们可以调用异步方法.现在我们从我们网站的控制器尝试相同的,就像这样.

    public ActionResult Index()
    {
        User spUser = null;

        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        var model = new LoginViewModel();

        // here we invoke sharepoint context for getting user info to bild the object

        M_Users new_user = new M_Users()
                    {
                        Password = "123",
                        FirstName = spUser.Title,
                        Email = spUser.LoginName.Split('|')[2].ToString(),
                        AdminCode = 1,
                        IsDisabled = false,
                        CountryId = 48,
                        CityId = 149,
                        DepartmentId = 3, 
                        CreationDate = DateTime.Now,
                        CreatorUserId = 1
                    };


                    var returnedUser = AddUserAsync(new_user);
                    returnedUser.Wait(); //HERE IT STOPS RUNNING

                    //THIS LINE IS NEVER REACHED
                    ViewBag.UserId = returnedUser.Result.UserID;

                    return View(model);
    }
Run Code Online (Sandbox Code Playgroud)

需要做什么才能继续执行.

我们还尝试使用ContinueWith()并运行,但是不执行异步方法,并且数据库中没有记录任何内容.更具体地说,该方法被调用,但它似乎已经过去并且没有任何重新调整.:(

Ste*_*ary 6

这几乎是我们需要证明的.从同步方法我们可以调用异步方法.

这是错误的教训.控制台应用程序使用与ASP.NET不同的线程框架,这就是让您失望的原因.

我在博客上完整地解释了它; 它的要点是await捕获"上下文"并使用它来恢复该async方法.在Console应用程序中,此"上下文"是线程池上下文,因此该async方法继续在某个任意线程池线程上运行,并不关心您是否已阻止线程调用Wait.但是,ASP.NET有一个只允许一次在一个线程中的请求上下文,因此该async方法试图在该请求上下文中恢复,但它不能,因为在该上下文中已经有一个线程被阻塞.

最好的解决方案是允许async通过代码库增长:

public Task<ActionResult> Index()
{
  User spUser = null;
  var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
  var model = new LoginViewModel();
  M_Users new_user = new M_Users()
  {
    Password = "123",
    FirstName = spUser.Title,
    Email = spUser.LoginName.Split('|')[2].ToString(),
    AdminCode = 1,
    IsDisabled = false,
    CountryId = 48,
    CityId = 149,
    DepartmentId = 3, 
    CreationDate = DateTime.Now,
    CreatorUserId = 1
  };

  var returnedUser = await AddUserAsync(new_user);
  ViewBag.UserId = returnedUser.UserID;
  return View(model);
}
Run Code Online (Sandbox Code Playgroud)