使用 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 中保留用户凭据 的相关文章

  • 检查列表是否包含另一个列表。 C#

    编辑 只是说 ContainsAllItem 中的注释解释得最好 很抱歉问这个问题 我知道以前有人问过这个问题 但我只是不明白 好的 所以我想检查一个列表是否包含另一个列表中的所有项目WITHOUT重叠 以及根据类字符串 名称变量 称为项目
  • WebClient读取错误页面的内容

    我有一个加载页面内容的应用程序 我使用 WebClient 类 即使服务器返回 404 500 等错误 我也需要检索内容 我需要这样的东西 WebClient wc new WebClient string pageContent try
  • 如何将pdf页面设置设置为打印属性对话框?

    大家好 我想知道如何设置 pdf 页面设置到打印属性对话框 例如 如果我的 PDF 页面设置为横向 则布局会自动显示横向而不是纵向 如果我的 PDF 页面设置为纵向 则布局会自动显示纵向 我在这个主题上做了很多研发 但没有找到任何满意的链接
  • F10键没被抓住

    I have a Windows Form and there overriden ProcessCmdKey However this works with all of the F Keys except for F10 I am tr
  • 将成员函数作为参数传递/c++

    我想用 C 实现一个类b可以通过封装该迭代器类型的成员集进行某种迭代 喜欢 b object for each x do function f so 函数 f会得到每个人的x成员并做任何事情 比方说 void function f x me
  • .net Framework (.net 4.0) 中定义 Base 3 数字的类

    我正在寻找一些可以用来定义 3 基数 三进制数 的类 有什么我可以在 net 框架中使用的东西或者我需要写一些东西吗 谢谢你的帮助 您可以使用解析Convert ToInt32 s base http msdn microsoft com
  • 使用 openssl 检查服务器安全协议

    我有一个框架应用程序 它根据使用方式连接到不同的服务器 对于 https 连接 使用 openssl 我的问题是 我需要知道我连接的服务器是否使用 SSL 还是 TLS 以便我可以创建正确的 SSL 上下文 目前 如果我使用错误的上下文尝试
  • 如何使用泛型类型的 DataContractSerializer 编写自定义序列化器?

    我想编写一个自定义序列化器 用于将会话状态存储到Azure 缓存 预览版 这意味着这个自定义序列化器必须实现IDataCacheObjectSerializer 如果我错了 请告诉我 我需要编写这个自定义序列化程序的原因是我需要序列化一些包
  • Microsoft.Graph - 如何从具有不同用户名的共享邮箱发送?

    我目前正在将使用 SMTP 的服务代码移植到 Office 365 通过 SMTP 我可以使用 发件人 字段在来自共享收件箱的邮件上设置不同的用户名 同时保留共享电子邮箱地址 这似乎无法通过 Office 365 运行 其工艺流程为 客户填
  • 数据损坏 C++ 和 Python 之间的管道

    我正在编写一些代码 从 Python 获取二进制数据 将其通过管道传输到 C 对数据进行一些处理 在本例中计算互信息度量 然后将结果通过管道传输回 Python 在测试时 我发现如果我发送的数据是一组尺寸小于 1500 X 1500 的 2
  • 更改其他页面的主窗口内容

    在 WPF 应用程序的主窗口中 我有一个 Badged 元素 来自材料设计 这是我的代码
  • 如何将字符串转换为 Indian Money 格式?

    我正在尝试将字符串转换为印度货币格式 例如如果输入为 1234567 则输出应为 12 34 567 我编写了以下代码 但它没有给出预期的输出 CultureInfo hindi new CultureInfo hi IN string t
  • `cosf`、`sinf` 等不在 `std` 中 [重复]

    这个问题在这里已经有答案了 根据这里的讨论 我有报告了一个错误 https bugs launchpad net ubuntu source gcc 8 bug 1831385给 Ubuntu 开发者 编译以下示例 C 程序时 includ
  • asp.net c# 防止在从服务器端代码更改索引时触发 selectedindexchanged 事件

    我在同一个 aspx 页面上有两个下拉列表控件
  • 无法通过 LINQ to Entities 使用某些功能?

    我正在尝试使用 LINQ 查询在项目上实现搜索功能 由于数据有时包含带有重音符号和其他符号的字符 因此我创建了一种方法来删除这些字符以进行搜索 这是我的代码 var addresses from a in db Addresses join
  • 展开路径中具有环境变量的文件名

    最好的扩张方式是什么 MyPath filename txt to home user filename txt or MyPath filename txt to c Documents and settings user filenam
  • 将一个 long 转换为两个 int 以进行重构

    我需要将一个参数作为两个 int 参数传递给 Telerik Report 因为它不能接受长参数 将 long 拆分为两个 int 并在不丢失数据的情况下重建它的最简单方法是什么 使用掩蔽和移位是最好的选择 根据文档 long 保证为 64
  • 如何强制执行特定的 UserControl 设计

    我正在编写一个基本用户控件 它将由一堆其他用户控件继承 我需要对所有这些后代控件强制执行某种设计 例如 顶部必须有几个按钮以及一个或两个标签 后代用户控件区域的其余部分可以自由放置任何内容 最初 我认为我可以将一个面板放到 Base Use
  • 通过 cmake 链接作为外部项目包含的 opencv 库[重复]

    这个问题在这里已经有答案了 我对 cmake 比较陌生 经过几天的努力无法弄清楚以下事情 我有一个依赖于 opencv 的项目 它本身就是一个 cmake 项目 我想静态链接 opencv 库 我正在做的是我的项目中有一份 opencv 源
  • 创建带有部分的选项卡式侧边栏 WPF

    我正在尝试创建一个带有部分的选项卡式侧边栏 如 WPF 中的以下内容 我考虑过几种方法 但是有没有更简单 更优雅的方法呢 方法一 列表框 Using a ListBox并将 SelectedItem 绑定到右侧内容控件所绑定的值 为了区分标

随机推荐