代码之家  ›  专栏  ›  技术社区  ›  Dmytrii Nagirniak

没有ServiceLocator的验证

  •  0
  • Dmytrii Nagirniak  · 技术社区  · 16 年前

    我一次又一次地回到它身边,思考着最佳的表演方式 验证需要访问某些上下文的POCO对象 (例如,iSession在NH,iRepository中)。

    我能看到的唯一选择是 服务定位器 ,因此我的验证如下:

    public User : ICanValidate {
        public User() {} // We need this constructor (so no context known)
    
        public virtual string Username { get; set; }
    
        public IEnumerable<ValidationError> Validate() {
            if (ServiceLocator.GetService<IUserRepository>().FindUserByUsername(Username) != null)
                yield return new ValidationError("Username", "User already exists.")
        }
    }
    

    我已经用过了 控制反转和依赖注入 由于事实的原因,确实不喜欢ServiceLocator:

    • 难以维护隐式依赖关系。
    • 更难测试代码。
    • 潜在的线程问题。
    • 仅显式依赖于ServiceLocator。
    • 代码变得更难理解。
    • 需要在测试期间注册ServiceLocator接口。

    但是在另一方面,对于普通的POCO对象,我看不到像上面那样没有ServiceLocator和只使用IOC/DI执行验证的任何其他方式。

    目前我在 服务层 . 因此,每当参与者试图更改用户名(当然可能有所不同)时,服务就会执行这个验证。一个明显的缺点是,每个使用用户的服务都必须执行这个检查(即使它是一个调用)。

    所以问题是: 对于上述情况,有没有办法使用DI/IOC? ?

    谢谢,
    Dmitriy。

    2 回复  |  直到 16 年前
        1
  •  1
  •   Aaronaught    16 年前

    存储库通常比它们获取/存储的域对象处于更高的抽象级别。如果您发现您的域对象依赖于存储库,那么它是上游设计问题的指示。

    实际上,您拥有的是循环依赖关系。这个 IUserRepository 取决于 User 以及 用户 取决于 iieleRealStk . 这个 技术上 如果两个对象都在同一个程序集中,就可以编译,但是作为一个一般设计,它会给您带来麻烦。可能有各种各样的对象想要处理 用户 但不知道 iieleRealStk 它来自。

    我的建议是,不要将此作为 用户 . 验证应该由存储库本身执行,或者更好的是,如果用户名在尝试保存时已经存在,让存储库引发异常。

    这个建议有第二个原因。这是并发性的原因。即使您验证用户名并发现它 已经存在,当你试图 节约 那个用户。因此,您需要处理异常情况(尝试插入已经存在的用户名) 无论如何 . 考虑到这一点,你最好把这件事推迟到最后一刻,因为你事先没有办法保证。

    域对象应该 依赖关系;如果它们自验证,那么验证应该依赖于 只有 在被验证的实际对象上,而不是数据库中的其他数据上。重复的用户名约束实际上是一个数据约束,而不是域约束。

    总结: 将此特定验证移出 用户 班级。它不属于那里;这就是为什么你发现自己在使用这种特殊的反模式。

        2
  •  1
  •   Igor Zevaka    16 年前

    只是补充一下奥罗诺特所说的。这种设计存在一个更大的问题,因为域模型验证应该只验证模型固有的属性,而不是在更大的系统的上下文中。这种内在属性的一些例子是对用户名长度、可接受字符、名字和姓氏都被归档等的要求。

    您正在执行的验证是系统范围的验证,属于服务/存储库。如果使用域驱动设计进行设计,则此系统的外观如下:

    public class User : ICanValidate {
        public User() {} 
    
        public virtual string Username { get; set; }
    
        public IEnumerable<ValidationError> Validate() {
            if (!string.IsNullOrEmpty(this.UserName))
              yield return new ValidationError("Username must not be empty");
        }
    }
    
    public class UserRepository : IUserRepository {
    }
    
    public static class UserService { 
      readonly IUserRepository Repository;
    
      static UserService() {
        this.Repository = ServiceLocator.GetService<IUserRepository>();
      }
    
      public static IEnumerable<ValidationError> Validate(User user) {
          if (Repository.FindUserByUsername(user.Username) != null)
              yield return new ValidationError("Username", "User already exists.")
      }
    }
    
    推荐文章