【问题标题】:Injecting Dependencies into Domain Model classes with Nhibernate (ASP.NET MVC + IOC)使用 Nhibernate (ASP.NET MVC + IOC) 将依赖项注入域模型类
【发布时间】:2011-01-04 10:39:09
【问题描述】:

我正在构建一个 ASP.NET MVC 应用程序,该应用程序使用 DDD(域驱动设计)方法和由 NHibernate 处理的数据库访问。我有域模型类(管理员),我想通过 IOC 容器(例如 Castle Windsor)将依赖项注入其中,如下所示:

public class Administrator
{
    public virtual int Id { get; set; }

    //.. snip ..//

    public virtual string HashedPassword { get; protected set; }

    public void SetPassword(string plainTextPassword)
    {
        IHashingService hasher = IocContainer.Resolve<IHashingService>();

        this.HashedPassword = hasher.Hash(plainTextPassword);
    }
}

我基本上想为 SetPassword 方法注入 IHashingService 而不直接调用 IOC 容器(因为这被认为是 IOC 反模式)。但我不知道该怎么做。我的 Administrator 对象要么通过 new Administrator(); 实例化,要么通过 NHibernate 加载,那么如何将 IHashingService 注入 Administrator 类?

再想一想,我这样做的方式是否正确?我希望避免让我的代码库乱七八糟......

currentAdmin.Password = HashUtils.Hash(password, Algorithm.Sha512);

...而是让域模型本身来处理散列并将其巧妙地封装起来。我可以设想另一个开发人员不小心选择了错误的算法,并有一些密码为 Sha512,一些密码为 MD5,一些使用一种盐,一些使用不同的盐等等。相反,如果开发人员正在编写......

currentAdmin.SetPassword(password);

...那将隐藏这些细节并解决上面列出的问题,不是吗?

【问题讨论】:

    标签: asp.net-mvc nhibernate inversion-of-control castle-windsor ioc-container


    【解决方案1】:

    【讨论】:

    • 拦截器看起来很有趣,我试试看,谢谢!
    【解决方案2】:

    您需要记住如何它是如何被散列的。这样您就可以在将来对一个字符串进行哈希处理,以检查它是否是他们的密码,并将其与哈希值进行比较。这意味着您需要在对象中存储一个枚举或其他字段,以指示数据库中使用的散列机制。

    否则,如果您更改默认的散列实现,所有旧的散列密码都不再有效,您的用户将会对为什么他们的密码不再有效而摸不着头脑——您最终会得到一个IHashingService 没有提供灵活性的接口(因为如果不添加奇怪的规则,例如“为 2010 年 1 月 12 日之前创建的管理员使用此哈希”,就无法更改哈希实现),没有真正好的理由存在。

    为此,我将添加适当的字段(枚举、IHashingService 接口返回的字符串等),然后让 NHibernate 通过 IUserType 实现为我实例化哈希服务,或者我会使用工厂模式,具体实例由 IoC 容器提供给工厂。这将把 Jarrett 的方法级注入与允许重新水合的对象在不依赖 IoC 容器的情况下找到其哈希实现的解决方案相结合。

    祝你好运!

    【讨论】:

      【解决方案3】:

      有什么原因不能在Administrator 类的构造函数中传递IHashingService?这就是我解决依赖关系的方法。

      public class Administrator
      {
          private readonly IHashingService _hashingService;
      
          public Administrator(IHashingService hashingService)
          {
              _hashingService = hashingService;
          }
      
          // <snip>
      
          public void SetPassword(string plainTextPassword)
          {
              this.HashedPassword = _hashingService.Hash(plainTextPassword);
          }
      }
      

      编辑#1

      如果从模型中提取,请尝试使用方法级注入。

      public void SetPassword(string plainText, IHashingService hasher)
      {
          if (hasher == null) throw new ArgumentNullException("hasher");
          this.HashedPassword = hasher.Hash(plainText);
      }
      

      编辑#2

      另外,为什么不让自己轻松一点,只在字符串上做一个扩展?

      public static class ExtensionsOfString
      {
          public static string Hash(this string s)
          {
              // hash with SHA256
              return hashedString;
          }
      }
      

      虽然我确实意识到使用依赖注入有一个“可替换”的代码方面,但这对于这个例子来说并不是什么大问题。您实际上并不需要 IPasswordEncryptionService,就像您需要 ICreditCardAuthorizationService 一样。如果有一天,您将散列算法从 SHA256 更改为 SHA512,您现在将使数据库中的每个密码都无效。

      【讨论】:

      • 因为管理员可以被 Nhibernate 实例化(即,如果它从数据库加载管理员对象)。那么我如何让 NHibernate 注入服务呢?这就是我坚持的一点。
      • 我同意 Jarretts 的第二次编辑。在这种情况下,您注入了一个不应该存在的依赖项。
      • 我同意这种情况,也许依赖注入不是最好的方法。但我可能还有其他需要它的情况,例如 ITaxCalculatorService,因此了解如何执行此操作以供将来参考会很有用。
      【解决方案4】:

      要么在应用程序外观中散列密码(如果您正在使用),要么在每次调用 Administrator.SetPassword(..) 时提供 IHashingService 实现。我认为它被称为双重调度?!

      如果您坚持 DI-in-entity 解决方案,我通过在实体上声明 [Configurable] 属性对 PostSharp AOP 和 PostSharp4Spring 做了类似的事情,但解决方案适用于 Spring.Net .您可以查看here 了解更多信息。此外,如果您从 DI 容器配置 NHibernate,您可能会在容器完成配置之前尝试 DI 实体的递归。您需要一个简单的静态类,该类具有在容器初始化期间抑制实体构造的 DI。虽然不能提供一个例子:(

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-16
        • 2013-06-28
        • 1970-01-01
        相关资源
        最近更新 更多