在WPF w / Unity和MVVM中保留用户凭据

尽管在Windows应用程序中做了类似的事情,但我在这方面做得很糟糕。 我正在研究WPF应用程序(Prism,Unity,MVVM),我刚刚完成了登录视图。 一旦根据SQL Server中的表validation了用户的凭据,我就这样做:

Thread.CurrentPrincipal = user.GenericPrincipal(); 

用户类定义如下:

 public class ApplicationIdentity : GenericIdentity, IApplicationIdentity { public string UserName { get; set; } public bool Authenticated { get; set; } public ICollection Modules { get; set; } public ICollection 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); } } 

我遇到的问题是,在登录屏幕被解雇后几乎立即我打这个电话:

  var currentUser = (IApplicationIdentity)Thread.CurrentPrincipal.Identity; 

这引发了一个exception,即Identity无法转换为类型IApplicationIdentity,我不确定我缺少什么。 到目前为止,我所阅读的所有SO / Google文章都通过针对ADvalidation用户来解决了这个问题,但这在我的场景中不起作用。

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

编辑(解决方案):

我只想关闭解决方案的循环。 接下来是一些教程,所以它有点冗长,但应该对任何绊倒它的人有所帮助。 接受的答案建议我注入Unity容器,注册我的对象的实例,并从那里使用它。 我同意这可能是正确的方法,但它要求我在我的Bootstrapper上“破解”一下。 在我开始使用Bootstrapper逻辑之前,我应该考虑一下准备工作。

在我原来的方法中,我的登录视图没有在我的容器中注册,因为登录视图在我的Bootstrapper运行之前被实例化(App.xaml打开了Login视图。)

我做的第一件事是使我的View和ViewModel注入ala:

  public Login(ILoginViewModel viewModel) { InitializeComponent(); DataContext = viewModel; } public LoginViewModel(IUnityContainer container) { Container = container; } 

再说一次:对于使用Unity(或一般IoC)的人来说,这应该是熟悉的。 然后,我需要在用户通过身份validation后向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; } 

这种方法很简单,但在实际场景中,用户可以访问的模块可能是动态的而不是静态的(或两者的组合。)在InitializeShell()之前的Bootstrapper Run()方法中调用CreateModuleCatalog() InitializeShell() 。 在我的情况下,我仍然有一个所有用户都可以访问的静态模块(无论授权级别如何),但是(在我看来)从这种方法实例化Login视图会觉得“反模式”(更不用说注册)使用Unity的类型。)因此我的CreateModuleCatalog()成为:

  protected override IModuleCatalog CreateModuleCatalog() { var catalog = new ModuleCatalog(); catalog.AddModule(typeof(CoreModule)); return catalog; } 

我选择使用我的覆盖InitializeShell()来注册我的登录类型,显示Login视图等。我最终结束了这个实现:

  protected override void InitializeShell() { base.InitializeShell(); Container.RegisterType(typeof (Login), "LoginView"); Container.RegisterType(); Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; ShowLogOn(); Application.Current.MainWindow = (Window)Shell; Application.Current.MainWindow.Show(); } 

如果用户最终点击Login视图上的Cancel按钮, ShowLogOn()将关闭应用程序,所以我可能在调用base.InitializeShell()之前放置了ShowLogOn()所有代码,以便不需要的代码跑了。 ShowLogOn()看起来与您期望的完全相同:

  private void ShowLogOn() { var login = Container.Resolve(); var dialogResult = login.ShowDialog(); if (!dialogResult.Value) { Application.Current.Shutdown(1); } else { LoadAuthorizedModules(); Application.Current.MainWindow = null; Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; } } 

如果用户取消我的登录视图,则对话框结果将为false,并且应用程序将关闭。 如果他们已成功通过身份validation,我们现在需要加载允许他们查看的模块。 这也非常简单:

  private void LoadAuthorizedModules() { var currentUser = Container.Resolve("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 }); } } 

这种方法需要更多解释。 首先看看这一行,因为它可能令人困惑: var currentUser = Container.Resolve("CurrentUser"); 注意我没有在我的代码中的任何地方显式注册IApplicationIdentity类型! 但是,当我这样做时,它是隐式注册的: Container.RegisterInstance("CurrentUser", user); 从技术上讲,我可以将前面的语句写成: Container.RegisterInstance("CurrentUser", user); 但这对我来说是多余的,所以做你觉得最舒服的事情。

IApplicationIdentity的Modules属性IApplicationIdentity对象,这些对象包含有关当前用户有权访问的模块的信息。 module.AssemblyName是我的自定义模块类型所在的物理程序集的名称, module.AssemblyNamemodule.Type的Type。

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

这非常冗长,但我希望有人发现它很有用。 此外,我对如何使这个代码“更好”的任何意见感兴趣。 谢谢!

如果要将其存储在线程中,可以查看此问题 – https://stackoverflow.com/a/6699699/1798889 。 但是,如果您只想访问IApplicationIdentity。 我建议你只注册那个实例。

 // 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,并且每次你需要时它将统一实现它。 你不需要担心线程。