ASP.NET MVC视图模型中自定义验证的最佳实践

5
我正试图在ASP.NET MVC 3中构建一个应用,它结合了领域驱动设计和测试驱动开发。我的架构设置包括Repositories, Domain Models, View Models, Controllers和Views。所有验证都将在视图模型中处理。我设置了视图模型继承"IValidatableObject",以便在控制器方法调用"ModelState.IsValid"时执行我的验证属性和自定义验证,我设置的"Validate"方法也被执行。我遇到的问题是,在视图模型的验证方法中访问我的Repository。我需要访问Repository来检查数据库中是否有重复的记录。最好的想法似乎是创建一个IRepository类型的属性,并通过将我的Repository注入到视图模型的构造函数中来设置该属性。例如:
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."
           }
       }
}

你们认为这是一个好主意吗?


个人而言,我会避免拥有一个执行数据库查找的属性。 - Justin Helgerson
我明白你的意思,但是除此之外,我怎么验证重复记录呢?我在考虑创建一个自定义验证方法,控制器方法会调用并传递modelstate。该方法将执行我的自定义验证,并将模型错误添加到modelstate中(副作用- C#对象=按引用传递),但似乎我发布的那个代码会更简洁,代码也会更简单。 - RiceRiceBaby
1
域验证必须封装在域中。验证是一个概念,在应用程序的几个地方都必须应用。通常,当您验证视图模型时,您正在验证以检查用户提供的数据是否安全(应用消毒剂)和表面上正确。然后,您通常会调用聚合根来执行一些操作。在那里,您应该验证域不变量(域规则)。在CQRS架构中,我发现在CommandHandler内部进行验证是一个好习惯。 - Jupaol
更多信息请访问:https://dev59.com/veo6XIcBkEYKwwoYQiK7 - Jupaol
2个回答

2

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

...
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."  
           }  
       }
   }

在你的第一篇帖子中,听起来好像我不应该遵循这种方法。看起来你是在说我应该只把业务逻辑验证放在领域中。 - RiceRiceBaby

0
您可以添加领域服务类来获取与您的条件匹配并在领域服务层验证的对象。
 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!");
        }
    }

看起来你是在说,我应该在视图模型中验证我的表单,但业务逻辑验证应该放在领域或服务中,这很有道理。问题是,我如何将验证错误发送回控制器中的模型状态? - RiceRiceBaby
你可以通过在领域模型中抛出异常并在视图中捕获该异常来实现此操作,因为根据DDD的规定,你的领域模型应该与基础架构逻辑隔离开来。 - Aditya Nagodra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接