【问题标题】:SignalR and ASP.NET Identity's UserManager class lifetimeSignalR 和 ASP.NET Identity 的 UserManager 类生命周期
【发布时间】:2015-01-02 23:52:49
【问题描述】:

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

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

    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() 等,具体取决于设计),因此将我当前使用的上下文用于@ 的UserStore 非常重要987654332@ 的当前实例。每个请求创建一次存储库,这似乎按预期适用于每个 SignalR 调用。虽然到目前为止我在这个设计上没有遇到任何问题,但在阅读了这个问题(在this 文章中)之后,尤其是这一行:

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

问题:什么是使用 ASP.NET Identity 的 UserManager 类和 SignalR 的首选方式,如果我使用相同的 DbContext 实例是必要的 UserManagerUserStore 使用的我的存储库的方法?

【问题讨论】:

    标签: c# asp.net-mvc entity-framework signalr asp.net-identity


    【解决方案1】:

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

    Using Simple Injector with SignalR

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

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

    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 容器可以帮助您解决的另一个问题。

    【讨论】:

    • 谢谢,我做到了。然而,Niject 的行为有些出人意料。使用InRequestScope(),在调用集线器上的第一个方法后,它会按预期创建存储库、DbContext 和 UserManager 的新实例。但是,调用更多的方法不会产生新的实例;使用了相同的上下文,这当然是非常不幸的。但既然这是一个不相关的问题,而且你的回答很完美,我接受它。
    • @Shinzon 只要在同一个 Web 请求期间使用同一个 DbContext 实例调用所有其他方法,我不同意这是不幸的。这是国际海事组织有意和期望的。我相信 InRequestScope 将 DbContext 存储在 HttpContext.Current.Items 中,并在 Application_EndRequest 期间处理它。不是这样吗?使用这种范围,您的 DbContext 不会存在太久,但会存在足够长的时间来完成单个 Web 请求所需的一切。
    • 问题是,在客户端调用SignalR方法的时候,Application_EndRequest没有被触发,所以DbContext没有被释放。
    • @Shinzon 不确定 Ninject,但 SimpleInjector 具有其他范围,例如 LifetimeScope 和 ExecutionScope,您可以配置它们以在未调用 Application_EndRequest 时管理 SignalR 中的 DbContext 生命周期。可能值得研究。
    • Ninject 的 NamedScope 扩展的 InCallScope() 似乎已经解决了这个问题。感谢您的帮助!
    猜你喜欢
    • 2018-12-20
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2016-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多