SignalR 和 ASP.NET Identity 的 UserManager class 生命周期

SignalR and ASP.NET Identity's UserManager class lifetime

在我的 MVC 应用程序中,我使用 SignalR 在用户之间进行通信。基本上,客户端调用集线器上的方法,集线器调用存储库上的方法,然后将消息保存到数据库,集线器将新消息通知其他客户端。

我在客户端的这些调用中使用了 GetOwinContext() 方法来获取 UserManagerApplicationDbContext 的当前实例,方法是使用 GetUserManager<UserManager>()Get<ApplicationDbcontex>() 扩展方法,分别。但是,我注意到来自同一连接的调用使用相同的上下文,这显然不是一件好事。我继续更改我的存储库,所以现在是这样的:

    public XyzRepository()  //constructor
    {
        db = ApplicationDbContext.Create(); //static method that generates a new instance
    }
    private ApplicatonDbContext db { get; set; }     
    private UserManager UserManager
    {
        get
        {
            return new UserManager(new UserStore<ApplicationUser>(db)); //returns a new UserManager using the context that is used by this instance of the repository
        }
    }

由于我使用 UserManager 引用 ApplicationUser 对象(使用 FindByIdAsync() 等,具体取决于设计),因此使用我当前使用的上下文非常重要对于 UserManager 的当前实例的 UserStore。每个请求创建一次存储库,这似乎适用于预期的每个 SignalR 调用。虽然到目前为止我在这个设计上没有遇到任何问题,但在阅读了这个问题之后(在 this 文章中),特别是这一行:

"在当前方法中,如果请求中有两个 UserManager 实例对同一用户起作用,它们将使用用户对象的两个不同实例。",我决定问社区:

问题:将ASP.NET身份的UserManagerclass与SignalR一起使用的首选方式是什么,如果是 必须 我对存储库的方法使用与 UserManagerUserStore 相同的 DbContext 实例?

我认为首选方法是使用控制反转容器和构造函数注入具有某种生命周期范围的依赖项。这是您可能想要研究的另一个问题:

Using Simple Injector with SignalR

您的 DbContext 实例最好与当前网络请求一样长。 IoC 容器可以让您注册 DbContext 个具有每个 Web 请求生命周期的实例,但是您需要设置 IoC 容器,以便它可以管理 Hub 类 的构造以实现这个。某些 IoC 容器(如 SimpleInjector)也会在网络请求结束时自动为您处理 DbContext,因此您无需将任何内容包装在 using 块中。

至于 UserManagerXyzRepository 等,我认为它们也可以具有每个 Web 请求的生命周期,甚至是短暂的生命周期。最后,我不明白为什么你不能实现这样的目标:

public class MyXyzHub : Hub
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly MessageRepository _messageRepository;

    public MyXyzHub(UserManager<ApplicationUser> userManager,
        MessageRepository messageRepository)
    {
        _userManager = userManager;
        _messageRepository= messageRepository;
    }

    public void sendMessage(string message)
    {
        var user = _userManager.FindByIdAsync(...
        _messageRepository.CreateAndSave(new Message
        {
            Content = message, UserId = user.Id 
        });
        Clients.All.receiveMessage(message, user.Name);
    }
}

如果您以正确的方式连接 IoC 容器,那么每次构建 Hub 时,它都应该为当前的 Web 请求重用相同的 ApplicationDbContext 实例。同样对于您当前的代码,看起来 XyzRepository 永远不会处理您的 ApplicationDbContext,这是 IoC 容器可以帮助您解决的另一个问题。