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

2024-03-31

尽管我在 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);
    }
}

我遇到的问题是,几乎在登录屏幕关闭后我立即拨打此电话:

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

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

我在这里试图解决的问题只是保留当前登录的用户以及他们应该有权访问的模块和视图。如果除了设置 CurrentPrincipal 之外还有更好的方法来实现此目的,我完全愿意接受其他解决方案。感谢您能够提供的任何帮助!

编辑(解决方案):

我只是想结束解决方案的循环。接下来是一些教程,所以它有点长,但应该对任何偶然发现它的人都有帮助。接受的答案建议我注入 Unity 容器,注册对象的实例,然后从那里使用它。我同意这可能是正确的方法,但它需要我对我的 Bootstrapper 进行一些“破解”。不过,在了解引导程序逻辑之前,我可能应该回顾一下所涉及的一些准备工作。

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

我做的第一件事是让我的 View 和 ViewModel 可注入:

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

    public LoginViewModel(IUnityContainer container)
    {
        Container = container;
    }

再次强调:使用过 Unity(或一般 IoC)的任何人都应该熟悉这一点。然后,一旦用户经过身份验证,我需要向 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()在 Bootstrapper 中调用Run()方法提前于InitializeShell()。就我而言,我仍然有一个所有用户都可以访问的静态模块(无论授权级别如何),但是(在我看来)从这个方法实例化登录视图会感觉“反模式”(更不用说注册Unity 的类型。)因此我的CreateModuleCatalog() became:

    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();
    }

ShowLogOn()如果用户最终点击登录视图上的取消按钮,将关闭应用程序,所以我可能已经将所有代码放在ShowLogOn()在致电之前base.InitializeShell()这样就不会运行不必要的代码。ShowLogOn()看起来和你想象的一模一样:

    private void ShowLogOn()
    {
        var login = Container.Resolve<Login>();
        var dialogResult = login.ShowDialog();

        if (!dialogResult.Value)
        {
            Application.Current.Shutdown(1);
        }
        else
        {
            LoadAuthorizedModules();
            Application.Current.MainWindow = null;
            Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
        }
    }

如果用户取消登录视图,对话框结果将为 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
                });
        }
    }

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

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

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

这是相当长的,但我希望有人觉得它有用。另外,我对有关如何使这段代码“更好”的任何意见感兴趣。谢谢!


如果你想将它存储在线程中,你可以查看这个问题 -https://stackoverflow.com/a/6699699/1798889 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,并且 Unity 将在您每次需要时实现它。您无需担心线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Unity 和 MVVM 在 WPF 中保留用户凭据 的相关文章

  • 测试 hdf5/c++ 中的组是否存在

    我正在打开一个现有的 HDF5 文件来附加数据 我想向那个叫做的小组保证 A存在以供后续访问 我正在寻找一种简单的方法来创建 A有条件地 如果不存在则创建并返回新组 或者返回现有组 一种方法是测试 A存在 我怎样才能高效地做到这一点 根据
  • 如何在 C# 事件中区分更改是由代码还是由用户进行?

    我有一个简单的TextBox一开始是空的 我有一个简单的事件 TextChanged 可以知道用户何时更改了其中的任何内容TextBox 但是 如果我自己在代码中对其执行任何操作 该事件就会触发 喜欢设置textbox Text Test
  • 处理 LINQ sum 表达式中的 null

    我正在使用 LINQ 查询来查找列的总和 并且在少数情况下该值有可能为空 我现在使用的查询是 int score dbContext domainmaps Where p gt p SchoolId schoolid Sum v gt v
  • 有没有比这更快的方法来查找目录和所有子目录中的所有文件?

    我正在编写一个程序 需要在目录及其所有子目录中搜索具有特定扩展名的文件 这将在本地驱动器和网络驱动器上使用 因此性能是一个问题 这是我现在使用的递归方法 private void GetFileList string fileSearchP
  • 当我单击 GridView 项时返回 ImageView 实例

    当我点击GridView项时如何返回ImageView实例 我为 ItemClick 创建自定义绑定事件 public class ItemClickSquareBinding MvxBaseAndroidTargetBinding pri
  • __FUNCTION__ 宏的 C# 版本

    有人对 C FUNCTION 宏的 C 版本有好的解决方案吗 编译器似乎不喜欢它 尝试使用这个代替 System Reflection MethodBase GetCurrentMethod Name C 没有 LINE or FUNCTI
  • C++ 在 Vector 中使用不可分配的对象

    我想将对象列表存储在std vector 但对象包含引用且无法分配给 但是 我可以复制构造该对象 我能想到的唯一选择是使用指针来包装对象并在需要分配指针时重新设置指针 但这样做的语法会显着降低可读性 特别是在使用迭代器时 我更喜欢另一种选择
  • 根据拦截和返回值自动重试客户端WCF调用

    是否可以拦截 WCF 调用的结果并重试该操作 例如 操作的返回值可能包含状态代码 指示我传递到原始调用的会话令牌已过期 在这种情况下 我可以检索新的会话令牌并使用新的会话令牌重试调用 是否可以通过使用 WCF 拦截返回值 检查它 然后以对操
  • 推送 Lua 表

    我已经创建了一个Lua表C 但我不知道如何将该表推入堆栈顶部 以便我可以将其传递给 Lua 函数 有谁知道如何做到这一点 这是我当前的代码 lua createtable state libraries size 0 int table i
  • 为什么以下代码不允许我使用 fgets 获取用户输入但可以使用 scanf?

    这是一个更大程序的简短摘录 但该程序的其余部分无关紧要 因为我认为我能够隔离该问题 我怀疑这与我使用 fgets 的方式有关 我读过 最好使用 fgets 而不是 scanf 但我似乎无法让它在这里正常工作 当我使用以下代码时 程序不会给我
  • 抽象类和接口之间的区别[重复]

    这个问题在这里已经有答案了 可能的重复 接口与基类 https stackoverflow com questions 56867 interface vs base class 我不明白抽象类和接口之间的区别 我什么时候需要使用哪种字体
  • C#:如何使用 SHOpenFolderAndSelectItems [重复]

    这个问题在这里已经有答案了 有人可以举例说明如何使用 shell 函数吗SH打开文件夹并选择项目 http msdn microsoft com en us library bb762232 VS 85 aspx来自 C 我不太明白如何使用
  • C++ Primer 5th Edition 错误 bool 值没有指定最小大小?

    bool 的最小大小不应该是 1 个字节吗 这有点学术性的东西 尽管它们会转换为数字 并且 与其他所有事物一样 它们最终将基本上由计算机内存中的数字表示 但布尔值不是数字 你的bool可以取值true 或值false 即使您确实需要至少 1
  • 在可观察项目生成时对其进行处理

    我有一个IObservable它会生成一次性物品 并且在其生命周期内可能会生成无限数量的物品 因此 我想在每次生成新项目时处理最后一个项目 因此Using http reactivex io documentation operators
  • 标准 C 中的 sizeof 与 sizeof()? [复制]

    这个问题在这里已经有答案了 我看到一些直接使用 sizeof 的代码 想知道它是否是标准 C 令我惊讶的是 它运行得很好 这是一个例子 include
  • 为什么 C 函数不能返回数组类型?

    我是 C 语言新手 想知道 为什么 C 函数不能返回数组类型 我知道数组名是数组第一个值的地址 而数组是 C 中的二等公民 您自己已经回答了这个问题 数组是二等公民 C 按值返回 数组不能按值传递 因此不能返回它们 至于为什么数组不能按值传
  • 用 C# 编写的带有点击移动的 WPF 游戏

    我试图将标签网格移动到鼠标的位置 就像冒险游戏中的移动一样 理想情况下 我会在途中删除并重新绘制它们 但是 现在我只想弄清楚如何将 int 转换为厚度或 pointtoscreen 到目前为止我有 player XMove int Mous
  • 为什么我不能在扩展 List 的类中调用 OrderBy?

    我有一堂课 Deck 其中包含一个名为的方法Shuffle 我正在致力于重构Deck延长List
  • 改进C++逐行读取文件的能力?

    我正在解析大约 500GB 的日志文件 我的 C 版本需要 3 5 分钟 我的 Go 版本需要 1 2 分钟 我正在使用 C 的流来流式传输文件的每一行以进行解析 include
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn

随机推荐