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

Asp.net mvc ASP.NET MVC视图中自定义验证模型最佳实践,asp.net-mvc,validation,viewmodel,Asp.net Mvc,Validation,Viewmodel,我正在尝试将域驱动设计与测试驱动开发相结合,用于我在ASP.NET MVC 3中构建的应用程序。我的体系结构由存储库、域模型、视图模型、控制器和视图组成。所有验证都将在视图模型中处理。我将视图模型设置为从“IValidatableObject”继承,这样,当控制器方法调用“ModelState.IsValid”时,我在“Validate”方法中设置的验证属性和自定义验证都会执行。我遇到的问题是在视图模型的Validate方法中访问我的存储库。我需要访问存储库以检查数据库中的重复记录。似乎最好的办

我正在尝试将域驱动设计与测试驱动开发相结合,用于我在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."
           }
       }
}
public类UserViewModel:IValidatableObject
{
公共用户视图模型(用户用户、IUserRepository用户存储库)
{
FirstName=user.FirstName;
LastName=user.LastName;
UserRepository=UserRepository;
用户名=user.UserName;
}
公共字符串用户名{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共IUserRepository UserRepository{get;set;}
公共IEnumerable验证()
{
UserCriteria=newusercriteria{UserName=this.UserName};
IList users=UserRepository.SearchUsers(标准);
if(users!=null&&users.count()>0)
{
返回新的ValidationResult(“用户名为“+this.username+”的用户已存在。”
}
}
}

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

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

...
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."  
           }  
       }
   }
。。。
私有只读Func userRepositoryFactory;
...
公共IEnumerable验证()
{  
UserCriteria=newusercriteria{UserName=this.UserName};
使用(var UserRepository=userRepositoryFactory())
{
IList users=UserRepository.SearchUsers(标准);
if(users!=null&&users.count()>0)
{  
返回新的ValidationResult(“用户名为“+this.username+”的用户已存在。”
}  
}
}

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

 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!");
        }
    }
公共类采购订单
{
公共字符串Id{get;private set;}
公共字符串PONumber{get;private set;}
公共字符串说明{get;private set;}
公共十进制总数{get;private set;}
公共日期时间提交日期{get;private set;}
公共ICollection发票{get;private set;}
公共十进制发票总额
{
获取{返回this.Invoices.Select(x=>x.Amount.Sum();}
}
}
公共类PurchaseOrderService
{
public PurchaseOrderService(IPurchaseOrderRepository存储库)
{
this.repository=存储库;
}
只读IPurchaseOrderRepository存储库;
public void checkpurchasedorderxsist(字符串purchaseOrderId)
{
var purchaseOrder=this.repository.Get(purchaseOrderId);
if(purchaseOrder!=null)
抛出新异常(“PO已存在!”);
}
}

就我个人而言,我会避免使用执行数据库查找的属性。我明白你的意思,但我如何针对重复记录进行验证。我正在考虑创建一个自定义验证方法,控制器方法将在modelstate中调用并传递该方法。然后,该方法将执行自定义验证并添加模型状态的模型错误(副作用-c#对象=通过引用传递),但我发布的内容似乎更简洁,代码也更简单。域验证必须封装在域中。验证是一个概念,必须应用于应用程序中的多个位置。通常,在验证视图模型时,您要验证以检查用户提供的数据是否安全,(应用消毒剂)并且显然是正确的。然后您通常会调用聚合根来执行一些操作。在那里您应该验证域不变量(域规则)。在CQRS体系结构中,我发现在CommandHandler中进行验证是一种很好的做法。在您的第一篇文章中,听起来我不应该遵循这种方法。您似乎在说我应该只在域中进行业务逻辑验证。您似乎在说,我应该在视图模型中验证我的表单,但业务逻辑lidation应该放在有意义的域或服务中。问题是,我如何将验证错误发送回控制器中的modelstate?您可以通过从域模型中抛出异常并在视图中捕获该异常来实现这一点,因为根据DDD,您的域模型应该与您的基础结构逻辑隔离。。。