在多用户Web应用程序中使用Unity依赖注入:第二位用户登录导致第一位用户看到第二位用户的数据 [英] Using Unity Dependency Injection in Multi-User Web Application: Second User to Log In Causes First User To See Second User's Data

查看:58
本文介绍了在多用户Web应用程序中使用Unity依赖注入:第二位用户登录导致第一位用户看到第二位用户的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用ASP.NET MVC和Microsoft Unity DI框架来实现Web应用程序.该应用程序需要同时支持多个用户会话,每个用户会话都有自己的连接到单独的数据库(但是所有用户都使用相同的DbContext;数据库模式相同,只是数据不同).

在用户登录后,我将必要的类型映射注册到应用程序的Unity容器没有其他内容.无论是通过登录启动,还是通过我的Web应用程序中的数据库访问选择(调用相同的方法将用户的连接重新路由到他们有权访问的另一个数据库)启动,最后绘制的始终会施加他们的数据Web应用程序的 all 个其他用户上.如果我理解正确,那么这就是SessionLifetimeManager应该解决的问题-不幸的是,我似乎真的无法正常工作.

我真诚地怀疑,像这样一个简单而通用的用例-登录MVC应用程序的多个用户,每个用户都应该访问自己的独立数据-超出了Unity的能力,所以很明显,我必须这样做这里很不对劲.我一整天都在搜寻互联网的深处,甚至不确定自己是否真的存在过,不幸的是,我现在必须意识到,我在这里完全处于茫然之中.

以前有没有人处理过这个问题?以前有没有人处理过这个用例,如果是的话,有人可以告诉我如何改变我的方法以减少这种头痛的情况吗?在这一点上,我非常绝望,并且正在考虑重写我的整个数据访问方法,只是为了使它起作用-而不是清洁和可维护代码的最健康的心态.

非常感谢.

谢谢,穆罕默德.您的回答使我走上了正确的道路-我最终使用了RepositoryFactory最终解决了这个问题,该过程在注册期间在InjectionFactory中实例化,并返回一个总是围绕ClientContext并指向当前登录状态的存储库用户当前选择的数据库.

 //DataAccessDependencies.cs受保护的重写void Initialize(){IConfigurationBuilder configurationBuilder = Container.Resolve< IConfigurationBuilder>();Container.RegisterType< IRepository< ClientContext>>(新的InjectionFactory(c => {ClientRepositoryFactory repositoryFactory =新的ClientRepositoryFactory(configurationBuilder);返回repositoryFactory.GetRepository();}));}//ClientRepositoryFactory.cs公共类ClientRepositoryFactory:IRepositoryFactory< RepositoryService< ClientContext>>{私有只读IConfigurationBuilder _configurationBuilder;公共ClientRepositoryFactory(IConfigurationBuilder configurationBuilder){_configurationBuilder = configurationBuilder;}公共RepositoryService< ClientContext>GetRepository(){var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);ClientContext ctx = new ClientContext(connectionString);RepositoryService< ClientContext>仓库=新的RepositoryService< ClientContext>(ctx);退货仓库;}}//UserData.cs(multiton-singleton-hybrid)公共静态UserData当前{得到{var currentAADUID =(string)(HttpContext.Current.Session ["currentAADUID"]);返回Get(currentAADUID);}}公共静态UserData Get(字符串AADUID){UserData实例;锁(_instances){if(!_ instances.TryGetValue(AADUID,输出实例)){抛出新的UserDataNotInitializedException();}}返回实例;}公共静态UserData当前{得到{var currentAADUID =(string)(HttpContext.Current.Session ["currentAADUID"]);返回Get(currentAADUID);}}公共静态UserData Get(字符串AADUID){UserData实例;锁(_instances){if(!_ instances.TryGetValue(AADUID,输出实例)){抛出新的UserDataNotInitializedException();}}返回实例;} 

I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).

Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.

My container is initialized like this:

// Global.asax.cs

public static UnityContainer CurrentUnityContainer { get; set; }

protected void Application_Start()
{
    // ...other code...
    CurrentUnityContainer = UnityConfig.Initialize();
    // misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
    UnityConfig.RegisterComponents(CurrentUnityContainer);
}

// UnityConfig.cs

public static UnityContainer Initialize()
{
    UnityContainer container = new UnityContainer();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);

    return container;
}

This is the code that's called upon logging in:

// UserController.cs

UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);

// UnityConfig.cs

public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
    container.AddExtension(new DataAccessDependencies(databaseName));
}

// DataAccessDependencies.cs

public class DataAccessDependencies : UnityContainerExtension
{
    private readonly string _databaseName;

    public DataAccessDependencies(string databaseName)
    {
        _databaseName = databaseName;
    }

    protected override void Initialize()
    {
        IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();

        Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
        Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
    }
}

// SessionLifetimeManager.cs

public class SessionLifetimeManager : LifetimeManager
{
    private readonly string _key = Guid.NewGuid().ToString();

    public override void RemoveValue(ILifetimeContainer container = null)
    {
        HttpContext.Current.Session.Remove(_key);
    }

    public override void SetValue(object newValue, ILifetimeContainer container = null)
    {
        HttpContext.Current.Session[_key] = newValue;
    }

    public override object GetValue(ILifetimeContainer container = null)
    {
        return HttpContext.Current.Session[_key];
    }

    protected override LifetimeManager OnCreateLifetimeManager()
    {
        return new SessionLifetimeManager();
    }
}

This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.

Then, as soon as a second user logs in, disaster strikes.

The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.

I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.

Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.

Many thanks.

解决方案

Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.

// DataAccessDependencies.cs

protected override void Initialize()
{
    IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();

    Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
        ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
        return repositoryFactory.GetRepository();
    }));
}

// ClientRepositoryFactory.cs

public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
    private readonly IConfigurationBuilder _configurationBuilder;

    public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
    {
        _configurationBuilder = configurationBuilder;
    }

    public RepositoryService<ClientContext> GetRepository()
    {
        var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
        ClientContext ctx = new ClientContext(connectionString);
        RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);

        return repository;
    }
}

// UserData.cs (multiton-singleton-hybrid)

public static UserData Current
{
    get
    {
        var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
        return Get(currentAADUID);
    }
}

public static UserData Get(string AADUID)
{
    UserData instance;

    lock(_instances)
    {
        if(!_instances.TryGetValue(AADUID, out instance))
        {
            throw new UserDataNotInitializedException();
        }
    }
    return instance;
}

public static UserData Current
{
    get
    {
        var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
        return Get(currentAADUID);
    }
}

public static UserData Get(string AADUID)
{
    UserData instance;

    lock(_instances)
    {
        if(!_instances.TryGetValue(AADUID, out instance))
        {
            throw new UserDataNotInitializedException();
        }
    }
    return instance;
}

这篇关于在多用户Web应用程序中使用Unity依赖注入:第二位用户登录导致第一位用户看到第二位用户的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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