在异步等待中使用 HttpContext.Current.User 的正确方法 [英] Correct way to use HttpContext.Current.User with async await

查看:44
本文介绍了在异步等待中使用 HttpContext.Current.User 的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理异步操作并像这样使用 HttpContext.Current.User

I am working with async actions and use the HttpContext.Current.User like this

public class UserService : IUserService
{
   public ILocPrincipal Current
   {
       get { return HttpContext.Current.User as ILocPrincipal; }
   }
}
    
public class ChannelService : IDisposable
{
    // In the service layer 
    public ChannelService()
          : this(new Entities.LocDbContext(), new UserService())
      {
      }

    public ChannelService(Entities.LocDbContext locDbContext, IUserService userService)
    {
      this.LocDbContext = locDbContext;
      this.UserService = userService;
    }

    public async Task<ViewModels.DisplayChannel> FindOrDefaultAsync(long id)
    {
     var currentMemberId = this.UserService.Current.Id;
     // do some async EF request …
    }
}

// In the controller
[Authorize]
[RoutePrefix("channel")]
public class ChannelController : BaseController
{
    public ChannelController()
        : this(new ChannelService()
    {
    }

    public ChannelController(ChannelService channelService)
    {
        this.ChannelService = channelService;
    }
    
    // …

    [HttpGet, Route("~/api/channels/{id}/messages")]
    public async Task<ActionResult> GetMessages(long id)
    {
        var channel = await this.ChannelService
            .FindOrDefaultAsync(id);
 
        return PartialView("_Messages", channel);
    }

    // …
}

我最近重构了代码,以前我必须在每次调用服务时给用户.现在我读了这篇文章 https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/ 我不确定如果我的代码仍然有效.有没有人有更好的方法来处理这个问题?我不想向用户提供对服务的每个请求.

I have the code recently refactored, previously I had to give the user on each call to the service. Now I read this article https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/ and I’m not sure if my code still works. Has anyone a better approach to handle this? I don’t want to give the user on every request to the service.

推荐答案

只要您的 web.config 设置正确async/awaitHttpContext.Current 完美配合.我建议将 httpRuntime targetFramework 设置为 4.5 以删除所有怪癖模式"行为.

As long as your web.config settings are correct, async/await works perfectly well with HttpContext.Current. I recommend setting httpRuntime targetFramework to 4.5 to remove all "quirks mode" behavior.

一旦完成,普通的 async/await 将工作得很好.如果您在另一个线程上工作或者您的 await 代码不正确,您只会遇到问题.

Once that is done, plain async/await will work perfectly well. You'll only run into problems if you're doing work on another thread or if your await code is incorrect.

一、其他线程"问题;这是您链接到的博客文章中的第二个问题.像这样的代码当然不能正常工作:

First, the "other thread" problem; this is the second problem in the blog post you linked to. Code like this will of course not work correctly:

async Task FakeAsyncMethod()
{
  await Task.Run(() =>
  {
    var user = _userService.Current;
    ...
  });
}

这个问题其实与异步代码无关;它与从(非请求)线程池线程中检索上下文变量有关.如果您尝试同步执行,则会出现完全相同的问题.

This problem actually has nothing to do with asynchronous code; it has to do with retrieving a context variable from a (non-request) thread pool thread. The exact same problem would occur if you try to do it synchronously.

核心问题是异步版本使用了fake异步.这不合适,尤其是在 ASP.NET 上.解决方案是简单地删除假异步代码并使其同步(或真正异步,如果它确实有真正的异步工作要做):

The core problem is that the asynchronous version is using fake asynchrony. This inappropriate, especially on ASP.NET. The solution is to simply remove the fake-asynchronous code and make it synchronous (or truly asynchronous, if it actually has real asynchronous work to do):

void Method()
{
  var user = _userService.Current;
  ...
}

链接博客中推荐的技术(包装 HttpContext 并将其提供给工作线程)非常危险.HttpContext 被设计为一次只能从一个线程访问,AFAIK 根本不是线程安全的.所以在不同的线程之间分享它是在寻求一个伤害的世界.

The technique recommended in the linked blog (wrapping the HttpContext and providing it to the worker thread) is extremely dangerous. HttpContext is designed to be accessed only from one thread at a time and AFAIK is not threadsafe at all. So sharing it among different threads is asking for a world of hurt.

如果await 代码不正确,则会导致类似的问题.ConfigureAwait(false) 是库代码中常用的一种技术,用于通知运行时它不需要返回到特定上下文.考虑这个代码:

If the await code is incorrect, then it causes a similar problem. ConfigureAwait(false) is a technique commonly used in library code to notify the runtime that it doesn't need to return to a specific context. Consider this code:

async Task MyMethodAsync()
{
  await Task.Delay(1000).ConfigureAwait(false);
  var context = HttpContext.Current;
  // Note: "context" is not correct here.
  // It could be null; it could be the correct context;
  //  it could be a context for a different request.
}

在这种情况下,问题很明显.ConfigureAwait(false) 告诉 ASP.NET 当前方法的其余部分不需要上下文,然后它立即访问该上下文.但是,当您开始在接口实现中使用上下文值时,问题就不那么明显了:

In this case, the problem is obvious. ConfigureAwait(false) is telling ASP.NET that the rest of the current method does not need the context, and then it immediately accesses that context. When you start using context values in your interface implementations, though, the problem is not as obvious:

async Task MyMethodAsync()
{
  await Task.Delay(1000).ConfigureAwait(false);
  var user = _userService.Current;
}

这段代码同样是错误的,但并没有那么明显错误,因为上下文隐藏在界面后面.

This code is just as wrong but not as obviously wrong, since the context is hidden behind an interface.

因此,一般准则是:使用 ConfigureAwait(false) 如果您知道该方法不依赖于其上下文(直接或间接);否则,不要使用 ConfigureAwait.如果在您的设计中让接口实现在其实现中使用上下文是可以接受的,那么任何调用接口方法的方法都应该使用 ConfigureAwait(false):

So, the general guideline is: use ConfigureAwait(false) if you know that the method does not depend on its context (directly or indirectly); otherwise, do not use ConfigureAwait. If it's acceptable in your design to have interface implementations use the context in their implementation, then any method that calls an interface method should not use ConfigureAwait(false):

async Task MyMethodAsync()
{
  await Task.Delay(1000);
  var user = _userService.Current; // works fine
}

只要您遵循该准则,async/await 将与 HttpContext.Current 完美配合.

As long as you follow that guideline, async/await will work perfectly with HttpContext.Current.

这篇关于在异步等待中使用 HttpContext.Current.User 的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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