【问题标题】:Validation in Business Layer: How to call service methods?业务层中的验证:如何调用服务方法?
【发布时间】:2018-05-21 12:43:58
【问题描述】:

我在业务层上创建了一个基于Steven's answer 验证模型的结构。

它运行良好,但有些事情让我感到困惑。我在CreateUserValidator 中注入UserService 以便能够使用GetUser 方法。这意味着我在 UserService 中调用验证器并创建一个新的 UserService 实例来检查用户是否存在。

UserService -> [ValidateUser -> new UserService().GetUser()]

它有效,但似乎是一个非常糟糕的设计。但我必须使用那个方法。

请告诉我如何解决这个问题,或者我不应该担心吗?

public class CreateUser
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public sealed class CreateUserValidator : Validator<CreateUser>
{
    private IUserService _userService;
    public CreateUserValidator(IUserService userService)
    {
        _userService = userService;
    }
    protected override IEnumerable<ValidationResult> Validate(
        CreateUser entity)
    {

        var user = _userService.GetUserByEmail(entity.Email);
        if (user != null)
        {
            yield return new ValidationResult("Email", "Email address is already exist!");
        }
    }
}

UserService.cs

public partial class UserService : IUserService
{
    IGenericUnitofWork _uow = null;
    private readonly IValidationProvider _validationProvider;
    public UserService(IGenericUnitofWork uow, IValidationProvider validationProvider)
    {
        _uow = uow;
        _validationProvider = validationProvider;
    }

    public User CreateUser(CreateUser createUser)
    {
        this._validationProvider.Validate(createUser);

        var user = new User()
        {
            Email = createUser.Email,
            Name = createUser.Name,
        };
        _uow.Repository<User>().Insert(User);
        _uow.SaveChanges();
        return user;
    }

    public User GetUser(string email)
    {
        var user = _uow.Repository<User>().Where(m => m.Email == email).FirstOrDefault();
        return user;
    }
}

【问题讨论】:

  • 我想我最初的阅读似乎很愚蠢,因为您正在结合您的业务逻辑和数据层逻辑。 GetUser 方法可能应该在您的 _uow 或等效项中,您也可以将其传递给您的验证器,而无需重新注入。此外,由于您只是检查是否正在使用电子邮件,因此就可读性而言,获取整个 User 可能不是最好的方法。也许考虑使用if(db.Users.Any(x=&gt;x.Email == email)){return new Validation("Email In Use")
  • 我有基于 IGenericRepository 的 DAL 并处理 GetById 等。但我认为我必须保持简单,像 GetUserByEmail 这样的特殊功能应该在业务层中。
  • 注入并不总是必须在构造函数级别。也许validate(CreateUser entity, IUserService userService) 对您的情况更明智?服务将使用this 作为第二个参数调用它。
  • @Andrei,好点子,由于 IValidationProvider 实现,我找不到用参数注入它的方法。我在做这个工作。 stackoverflow.com/questions/4776396/…
  • @ocanal,我认为在这种情况下不需要通过容器注入任何东西,只需直接将服务实例作为参数传递:this._validationProvider.Validate(createUser, this);

标签: c# asp.net asp.net-mvc ninject ioc-container


【解决方案1】:

你的依赖图是循环的。正如section 6.3 of Dependency Injection in .NET second edition 中所述,依赖循环通常是由Single Responsibility Principle 违规引起的,就像您的设计中的情况一样。

问题在于UserService 的职责太多:创建用户与获取用户的职责不同。正如验证逻辑所暗示的那样,创建用户可能会成为一个非常复杂的用例,而获取用户通常非常简单。因此,将UserService 拆分为多个较小的类将是有益的。这将允许验证器依赖于允许通过其邮件地址检索用户的服务,而“创建用户”服务可以依赖于验证器。

更进一步,您可能希望完全从“创建用户”服务中删除验证。验证是一个横切关注点,将其与包含业务逻辑的类混合在一起,会使此类类更难维护。

here 所述,一种可能使您受益的设计是将所有状态变化的业务操作置于一个通用抽象之后。

【讨论】:

  • 我想更进一步,从服务中完全删除验证,但它似乎变得越来越复杂(对我来说),我相信如果我这样做我会理解项目成长时的受益者。但现在,我要告诉自己,我真的需要吗?实际上项目真的需要吗?拆分服务类对我来说是一个新的视角,因为到目前为止,我一直认为所有域服务方法都应该在同一个类中。我认为这是个好主意。将UOW注入验证器怎么样,这样我也可以通过uow访问存储库来获取用户。谢谢。
  • “我认为所有域服务方法都应该在同一个类中”。我不知道你是从哪里了解到的,但这实际上是一个非常糟糕的主意。
猜你喜欢
  • 2017-01-02
  • 1970-01-01
  • 2010-11-01
  • 2011-04-03
  • 2012-04-23
  • 2013-08-07
  • 2012-01-19
  • 1970-01-01
  • 2011-07-13
相关资源
最近更新 更多