15

问题

我知道在 MVC 中有很多方法可以进行模型验证,并且有很多关于这个主题的文档。但是,我不太确定验证模型属性的最佳方法是相同类型“子模型”

请记住以下几点

  • 我仍然想从TryUpdateModel/TryValidateModel方法中获利
  • 这些“子模型”中的每一个都有强类型视图
  • 有一个用于MainModel呈现整体显示视图的类的强类型视图

这可能听起来有点混乱,但我会抛出一些代码来澄清。以以下类为例:

主要型号:

class MainModel{
    public SomeSubModel Prop1 { get; set; }
    public SomeSubModel Prop2 { get; set; }
}

一些子模型:

class SomeSubModel{
      public string Name { get; set; }
      public string Foo { get; set; }
      public int Number { get; set; }
}

主模型控制器:

class MainModelController{

    public ActionResult MainDisplay(){
         var main = db.retrieveMainModel();
         return View(main); 
    }

    [HttpGet]
    public ActionResult EditProp1(){
         //hypothetical retrieve method to get MainModel from somewhere
         var main = db.retrieveMainModel();

         //return "submodel" to the strictly typed edit view for Prop1
         return View(main.Prop1);
    }

    [HttpPost]
    public ActionResult EditProp1(SomeSubModel model){

         if(TryValidateModel(model)){
              //hypothetical retrieve method to get MainModel from somewhere
              var main = db.retrieveMainModel();
              main.Prop1 = model;
              db.Save();

              //when succesfully saved return to main display page 
              return RedirectToAction("MainDisplay");
         }
         return View(main.Prop1);
    }

    //[...] similar thing for Prop2 
    //Prop1 and Prop2 could perhaps share same view as its strongly 
    //typed to the same class
}

我相信这段代码到目前为止都是有意义的(如果不是这样,请纠正我)因为TryValidateModel()正在验证一个没有ValidationAttribute.

问题出在哪里,哪里是最好的地方,或者什么是最好和最优雅的方式来对不同的验证约束Prop1同时Prop2仍然利用TryValidateModel()而不是用条件语句填充 Edit 方法和ModelState.AddModelError()

通常你可以在SomeSubModel类中有验证属性,但在这种情况下它不起作用,因为每个属性都有不同的约束。

其他选项是MainModel类中可能有自定义验证属性,但在这种情况下它也不起作用,因为SomeSubModel对象直接传递给视图并且在验证时没有对其MainModel对象的引用。

我能想到的唯一剩下的选择是每个属性的 ValidationModel,但我并不是最好的方法。

解决方案

这是我根据@MrMindor 的回答实施的解决方案。

基础 ValidationModel 类:

public class ValidationModel<T> where T : new()
{
    protected ValidationModel() {
        this.Model = new T();
    }
    protected ValidationModel(T obj) { 
        this.Model = obj; 
    }

    public T Model { get; set; }
}

Prop1 的验证模型

public class Prop1ValidationModel:ValidationModel<SomeSubModel>
{
    [StringLength(15)]
    public string Name { get{ return base.Model.Name; } set { base.Model.Name = value; } }

    public Prop1ValidationModel(SomeSubModel ssm)
        : base(ssm) { }
}

Prop2 的验证模型

public class Prop2ValidationModel:ValidationModel<SomeSubModel>
{
    [StringLength(70)]
    public string Name { get{ return base.Model.Name; } set { base.Model.Name = value; } }

    public Prop2ValidationModel(SomeSubModel ssm)
        : base(ssm) { }
}

行动

[HttpPost]
public ActionResult EditProp1(SomeSubModel model){

     Prop1ValidationModel vModel = new Prop1ValidationModel(model);
     if(TryValidateModel(vModel)){

          //[...] persist data

          //when succesfully saved return to main display page 
          return RedirectToAction("MainDisplay");
     }
     return View(model);
}
4

2 回答 2

4

我们在其中一个应用程序中遇到了类似的情况,其中每个应用程序都SomeSubModel代表一个作业的参数设置。由于每种类型的作业都有不同数量和类型的参数,因此我们的作业模型具有这些参数的集合,而不仅仅是设置属性。

我们有一个JobParameter子类到可用的不同类型(StringParameter, BoolParameter, DoubleParameter, ...)。这些子类有自己的验证属性集。
共享的“JobParameterModel”用于将参数传递给视图。
对于验证,返回的模型被转换为其特定的 JobParameter。
参数类型:

public enum ParameterType
{
    Empty = 0,
    Boolean = 1,
    Integer = 2,
    String = 3,
    DateTime = 4,
    ...
}

作业参数:

class JobParameter
{ 
  [AValidationAttributeForAllParamters] 
  public string Name { get; set; }  
  public virtual string Foo { get; set; }  
  public int Number { get; set; }
  public ParameterType Type {get;set;}

  private static readonly IDictionary<ParameterType, Func<object>> ParameterTypeDictionary =
  new Dictionary<ParameterType, Func<object>>{
                {ParameterType.Empty, () => new EmptyParameter() },
                {ParameterType.String, ()=>new StringParameter()},
                {ParameterType.Password, ()=>new PasswordParameter()},
                ...
              };
    public static ScriptParameter Factory(ParameterType type)
    {
        return (ScriptParameter)ParameterTypeDictionary[type]();
    }
}  

布尔参数:

[ABoolClassLevelValidationAttribute]
class BoolParameter:JobParameter
{
    [AValidationAttribute]
    public override string Foo {get;set;}
}

....

在我们的验证框架中(我被告知它的模型非常接近 MS),ViewModel 总是被转换回它的域对象进行验证。
参数型号:

class ParameterModel: JobParameter
{
    public JobParameter ToDomain()
    {
        var domainObject = JobParameter.Factory(Type);
        Mapper.Map(this, domainObject);
        return domainObject;
    }
    public bool Validate()
    {
        var dom = ToDomain();
        return TryValidate(dom);
    }

}

控制器:

class Controller(){

    [HttpPost]                                
    public ActionResult SaveParameter(JobParameter model){                                

         if(TryValidateModel(model)){                                

              //persist stuff to db.

          //when succesfully saved return to main display page                                 
              return RedirectToAction("MainDisplay");                                
         }                                
         return View(main.Prop1);
    }                                
}                                

为了您的具体情况,您不需要搞得这么复杂(或者相信我们的验证框架的细节会为您工作)。
为每个道具编辑/保存操作:为每个道具
创建一个验证模型。Prop1ValidationModel,Prop2ValidationModel

[HttpGet]
public ActionResult EditProp1()
{
    var main = db.retrieveMainModel();
    db.Prop1.SubmitUrl = Url.Action("SaveProp1","Controller");
    return View(main.Prop1);
}
[HttpPost]                                
public ActionResult SaveProp1(SomeSubModel model){                                
     var validationModel = new Prop1ValidationModel{
     ///copy properties                                   
         };
     if(TryValidateModel(validationModel)){                                

          var main = db.retrieveMainModel();                                
          main.Prop1 = model;                                
          db.Save();                                

          //when succesfully saved return to main display page                                 
          return RedirectToAction("MainDisplay");                                
     }                                
     return View(main.Prop1);
} 

有了这个,您可以对 Prop1 和 Prop2 使用相同的强类型视图。

于 2012-10-22T17:45:23.517 回答
2

如果 SomeSubModel 具有不同的验证属性,具体取决于它是在 Prop1 还是 Prop2 中应用...意味着实际上 prop1 和 prop2 的两个 SomeSubModel 是两个不同的类,因为如果它们具有相同的字段,则该字段的含义会有所不同,具体取决于如果它们附加到 prop1 或 prop2(这就是它们具有不同验证属性的原因。因此,最好的方法是定义 SomeSubClass 的两个子类,例如从 Common SomeSubClass 继承的 SomeSubClass1 和 SomeSubClass2。一旦继承,您不能添加新属性,而只是新的验证规则通过使用流式验证或使用MetaDataTypeAttribute在类定义之外指定验证属性。所以你会得到类似的东西:

[MetaDataType(typeof(ValidationClass1)]
public class SomeSubClass1: SomeSubclass{}

[MetaDataType(typeof(ValidationClass2)]
public class SomeSubClass2: SomeSubclass{}
于 2012-10-24T20:04:07.280 回答