C# 在WPF中使用Unity和MVVM持久化用户凭据

C# 在WPF中使用Unity和MVVM持久化用户凭据,c#,wpf,mvvm,C#,Wpf,Mvvm,尽管我在Windows应用程序中做了类似的事情,但我在这方面做得很糟糕。我正在开发一个WPF应用程序(Prism、Unity、MVVM),我刚刚完成了登录视图。根据SQL Server中的表验证用户凭据后,我将执行以下操作: Thread.CurrentPrincipal = user.GenericPrincipal(); 用户类的定义如下: public class ApplicationIdentity : GenericIdentity, IApplicationIdentity {

尽管我在Windows应用程序中做了类似的事情,但我在这方面做得很糟糕。我正在开发一个WPF应用程序(Prism、Unity、MVVM),我刚刚完成了登录视图。根据SQL Server中的表验证用户凭据后,我将执行以下操作:

 Thread.CurrentPrincipal = user.GenericPrincipal();
用户类的定义如下:

public class ApplicationIdentity : GenericIdentity, IApplicationIdentity
{
    public string UserName { get; set; }
    public bool Authenticated { get; set; }

    public ICollection<IModuleProperties> Modules { get; set; }
    public ICollection<IViewProperties> Views { get; set; }

    public ApplicationIdentity(string userName, IAuthenticatedUser authenticatedUser)
        : base(userName)
    {
        UserName = userName;
        Authenticated = authenticatedUser.Authenticated;
        Modules = authenticatedUser.Modules;
        Views = authenticatedUser.Views;
    }

    public IPrincipal GenericPrincipal()
    {
        return new GenericPrincipal(this, null);
    }
}
这引发了一个异常,标识不能转换为IApplicationIdentity类型,我不确定我缺少了什么。到目前为止,我读到的所有SO/Google文章都通过根据广告对用户进行身份验证解决了这个问题,但这在我的场景中不起作用

我在这里试图解决的问题只是持久化当前登录的用户以及他们应该访问哪些模块和视图。如果在设置CurrentPrincipal之外有更好的方法来实现这一点,我完全愿意接受其他解决方案。感谢您可能提供的任何帮助

编辑(解决方案):

我只想结束关于解决方案的循环。下面是一个教程,所以它有点长,但应该有助于任何人绊倒在它。公认的答案建议我注入Unity容器,注册我的对象的一个实例,然后从那里开始使用它。我同意这可能是正确的方法,但它需要我对我的引导程序进行一些“黑客”操作。不过,在我开始讨论引导程序逻辑之前,我应该先回顾一下相关的准备工作

在我最初的方法中,我的登录视图没有注册到我的容器中,因为登录视图是在我的引导程序运行之前实例化的(App.xaml打开了登录视图)

我做的第一件事是使我的视图和ViewModel可注射:

    public Login(ILoginViewModel viewModel)
    {
        InitializeComponent();
        DataContext = viewModel;
    }

    public LoginViewModel(IUnityContainer container)
    {
        Container = container;
    }
再次强调:任何与Unity(或国际奥委会)合作过的人都应该熟悉这一点。然后,我需要在用户通过身份验证后向Unity注册当前用户对象:

    private void Login(object obj)
    {
        ...
        if (user.Authenticated)
        {
            Container.RegisterInstance("CurrentUser", user); 
        }
        ...
    }
任何关注过互联网上大量的Prism/Unity/MVVM文章的人都可能熟悉以下方法:

    protected override IModuleCatalog CreateModuleCatalog()
    {
        var catalog = new ModuleCatalog();
        catalog.AddModule(typeof (CoreModule));
        catalog.AddModule(typeof (CoreModule2));
        catalog.AddModule(typeof (CoreModule3));
        return catalog;
    }
此方法非常简单,但在现实场景中,用户有权访问的模块可能是动态的,而不是静态的(或两者的组合)。
CreateModuleCatalog()
InitializeShell()
之前的Bootstrapper
Run()
方法中调用。在我的例子中,我仍然有一个所有用户都可以访问的静态模块(无论授权级别如何),但是(在我看来)从这个方法实例化登录视图(更不用说用Unity注册类型)会让我觉得“反模式化”。因此我的
CreateModuleCatalog()
变成:

    protected override IModuleCatalog CreateModuleCatalog()
    {
        var catalog = new ModuleCatalog();
        catalog.AddModule(typeof(CoreModule));
        return catalog;
    }
我选择使用我对
InitializeShell()
的覆盖来注册我的登录类型、显示登录视图等。我最终用这个来实现:

    protected override void InitializeShell()
    {
        base.InitializeShell();
        Container.RegisterType(typeof (Login), "LoginView");
        Container.RegisterType<ILoginViewModel, LoginViewModel>();

        Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
        ShowLogOn();

        Application.Current.MainWindow = (Window)Shell;
        Application.Current.MainWindow.Show();
    }
如果用户取消我的登录视图,对话框结果将为false,应用程序将关闭。但是,如果他们已经成功地进行了身份验证,我们现在需要加载允许他们查看的模块。这也非常简单:

    private void LoadAuthorizedModules()
    {
        var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser");
        foreach (var module in currentUser.Modules)
        {
            var moduleAssembly = Assembly.Load(module.AssemblyName);
            var loadingModule = moduleAssembly.GetType(module.Type);

            ModuleCatalog.AddModule(new ModuleInfo
                {
                    ModuleName = loadingModule.Name,
                    ModuleType = loadingModule.AssemblyQualifiedName
                });
        }
    }
private void LoadAuthorizedModules()
{
var currentUser=Container.Resolve(“currentUser”);
foreach(currentUser.Modules中的var模块)
{
var moduleAssembly=Assembly.Load(module.AssemblyName);
var loadingModule=moduleAssembly.GetType(module.Type);
ModuleCatalog.AddModule(新模块信息
{
ModuleName=loadingModule.Name,
ModuleType=loadingModule.AssemblyQualifiedName
});
}
}
这个方法需要更多的解释。首先看一下这一行,因为它可能会让人困惑:
var currentUser=Container.Resolve(“currentUser”)注意,我没有在代码中的任何地方显式注册类型
iaapplicationidentity
!然而,当我这样做时,它是隐式注册的:
Container.RegisterInstance(“CurrentUser”,user)Container.RegisterInstance(“CurrentUser”,user)
iaapplicationidentity
的Modules属性包含一组对象,其中包含有关当前用户有权访问的模块的信息
module.AssemblyName
是自定义模块类型所在的物理程序集的名称,
module.type
是模块的类型

正如您所看到的:一旦通过反射的力量加载了类型,我们需要将其添加到Unity Bootstrappers
ModuleCatalog
属性中,这是直接的。假设一切正常,执行应该返回到InitializeShell方法,Shell现在应该启动了


这相当长,但我希望有人觉得它有用。此外,我还想了解有关如何使此代码“更好”的任何意见。谢谢

如果要将此问题存储在线程-,则可以检查它。但如果您只是想访问IApplicationIdentity。我建议你向unity注册这个实例

// IUnityContainer can be injected by unity 
IUnityContainer container;

// Register this instance each time now you call for IApplicationIdentity this object will be returned
container.RegisterInstance(typeof (IApplicationIdentity), user.GenericPrincipal());

现在,您可以注入IApplicationIdentity,unity将在每次需要时实现它。您不必担心线程。

您将无法将框架标识对象强制转换为自定义类型。您可能应该更改ApplicationIdentity类型,使其具有以标识对象作为参数的构造函数。您好,Charles。我查看了该链接,但Windows标识不是我的用户对象所表示的,因此它无法工作。然而,您确实很好地说明了注入容器的重要性,这最终导致我找到了我的目标
    private void LoadAuthorizedModules()
    {
        var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser");
        foreach (var module in currentUser.Modules)
        {
            var moduleAssembly = Assembly.Load(module.AssemblyName);
            var loadingModule = moduleAssembly.GetType(module.Type);

            ModuleCatalog.AddModule(new ModuleInfo
                {
                    ModuleName = loadingModule.Name,
                    ModuleType = loadingModule.AssemblyQualifiedName
                });
        }
    }
// IUnityContainer can be injected by unity 
IUnityContainer container;

// Register this instance each time now you call for IApplicationIdentity this object will be returned
container.RegisterInstance(typeof (IApplicationIdentity), user.GenericPrincipal());