【问题标题】:ASP.NET MVC Custom Validation in View Model Best Practice视图模型最佳实践中的 ASP.NET MVC 自定义验证
【发布时间】:2012-09-20 16:00:35
【问题描述】:

我正在尝试将域驱动设计与测试驱动开发相结合,用于我在 ASP.NET MVC 3 中构建的这个应用程序。我的架构设置有存储库、域模型、视图模型、控制器和视图。所有验证都将在视图模型中处理。我将视图模型设置为从“IValidatableObject”继承,这样当我的控制器方法调用“ModelState.IsValid”时,我的验证属性和我在“Validate”方法中设置的自定义验证都会执行。我遇到的问题是在我的视图模型的 Validate 方法中访问我的存储库。我需要访问存储库以检查数据库中的重复记录。似乎最好的主意是创建一个 IRepository 类型的属性,并通过将我的存储库注入到视图模型的构造函数中来设置该属性。例如:

public class UserViewModel : IValidatableObject
{
       public UserViewModel(User user, IUserRepository userRepository)
       {
              FirstName = user.FirstName;
              LastName = user.LastName;
              UserRepository = userRepository;
              UserName = user.UserName;
       }
       public string UserName { get; set; }
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public IUserRepository UserRepository { get; set; }
       public IEnumerable<ValidationResult> Validate()
       {
           UserCriteria criteria = new UserCriteria { UserName = this.UserName };
           IList<User> users = UserRepository.SearchUsers(criteria);

           if (users != null && users.count() > 0)
           {
               yield return new ValidationResult("User with username " + this.UserName + " already exists."
           }
       }
}

你们觉得这是个好主意吗?

【问题讨论】:

  • 就我个人而言,我会避免使用执行数据库查找的属性。
  • 我明白你在说什么,但我还能如何验证重复记录。我正在考虑创建一个自定义验证方法,控制器方法将调用并传入模型状态。然后该方法将执行我的自定义验证并将模型错误添加到模型状态(副作用 - c# object = 通过引用传递),但看起来我发布的内容会更简洁,代码也会更简单。
  • 域验证必须封装在域中。验证是一个必须在应用程序的多个位置应用的概念。通常,当您验证 View-Model 时,您正在验证以检查用户提供的数据是否安全(应用消毒剂)并且显然正确。然后你通常会调用你的聚合根来执行一些操作。在那里您应该验证域不变量(域规则)。在 CQRS 架构中,我发现在 CommandHandler 内部进行验证是一种很好的做法

标签: asp.net-mvc validation viewmodel


【解决方案1】:

这已经足够了,但如果我是你,我会使用

...
private readonly Func<IUserRepository> userRepositoryFactory;
...
public IEnumerable<ValidationResult> Validate()  
   {  
       UserCriteria criteria = new UserCriteria { UserName = this.UserName };  
       using(var UserRepository = userRepositoryFactory())
       {
           IList<User> users = UserRepository.SearchUsers(criteria);  

           if (users != null && users.count() > 0)  
           {  
               yield return new ValidationResult("User with username " + this.UserName + " already exists."  
           }  
       }
   }

【讨论】:

  • 在您的第一篇文章中,听起来我不应该采用这种方法。看来您是在说我应该只将业务逻辑验证放在域中。
【解决方案2】:

您可以添加域服务类以获取与您的条件匹配的对象并在域服务级别进行验证

 public class PurchaseOrder
    {
        public string Id { get; private set; }
        public string PONumber { get; private set; }
        public string Description { get; private set; }
        public decimal Total { get; private set; }
        public DateTime SubmissionDate { get; private set; }
        public ICollection<Invoice> Invoices { get; private set; }

        public decimal InvoiceTotal
        {
            get { return this.Invoices.Select(x => x.Amount).Sum(); }
        }

    }

    public class PurchaseOrderService
    {
        public PurchaseOrderService(IPurchaseOrderRepository repository)
        {
            this.repository = repository;
        }

        readonly IPurchaseOrderRepository repository;

        public void CheckPurchasedOrderExsist(string purchaseOrderId)
        {
                var purchaseOrder = this.repository.Get(purchaseOrderId);
                if (purchaseOrder != null)
                    throw new Exception("PO already exist!");
        }
    }

【讨论】:

  • 您似乎在说,我应该在视图模型中验证我的表单,但业务逻辑验证应该在域或服务中进行,这是有意义的。问题是,我如何将验证错误发送回控制器中的模型状态?
  • 您可以通过从域模型中抛出异常并在视图中捕获它来做到这一点,因为根据 DDD,您的域模型应该与您的基础架构逻辑隔离...
猜你喜欢
  • 2011-09-16
  • 2011-04-08
  • 1970-01-01
  • 2010-11-28
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多