2014-04-08 74 views
0

我有20个字段的模型。模型的部分验证

我的观点分为4个面板,每个面板有5个区域;用户可以一次单独保存一个面板的数据。

我创建了4个视图,每个面板一个。这些视图都使用相同的模型,因为这些数据都在同一个数据库表中,所以我有一个单一的CRUD集。

我的问题是如何才能打开/关闭数据注解的必填字段只有当这些显示在其特定的面板?

如果我宣布他们都在20场模型的属性,它们将失效模式,即使他们不显示在屏幕上...

+0

有没有这样的事ASP.NET MVC 6(还)。请检查您认为正在使用的技术的版本号... – Charles

+0

呵呵你是对的,它是MVC5(与实体框架verion 6.1混淆) - 将更新post -thx。 – MaurGi

+0

可能有充分的理由需要这些字段,任何绕过验证的努力都可能导致比解决问题更多的问题。话虽如此,你有没有考虑使用默认值? – RyanCJI

回答

1

您最好将数据注释移动到查看模型。您可以为每个视图创建不同的模型,以便只触发受该视图影响的注释。

这就是说,有时很难重新设计你的数据模型。在这种情况下,您可以使用自定义的requiredif注释来添加触发部分数据验证的属性。具体请参阅RequiredIf Conditional Validation Attribute

我在第一个MVC项目中遇到了这个问题。以下是我如何解决这个问题。请注意,我将它们放在Model \ DataValidations.cs文件中以保持我生成的类清洁。

[MetadataType(typeof(Location_Validation))] 
public partial class Location 
{ 
    public bool DisableValidation { get; set; } 
} 

public class Location_Validation : HomeIndex_Validation 
{ 
    [RequiredIf(DependentProperty = "DisableValidation", TargetValue = false, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldRequired")] 
    [MinLength(2, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldTooShort")] 
    [MaxLength(50, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldTooLong")] 
    [Display(ResourceType = typeof(Language), Name = "City")] 
    public string City { get; set; } 

    [RequiredIf(DependentProperty = "DisableValidation", TargetValue = false, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldRequired")] 
    [MinLength(2, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldTooShort")] 
    [MaxLength(2, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldTooLong")] 
    [ReadOnly] 
    [Display(ResourceType = typeof(Language), Name = "State")] 
    public string State { get; set; } 

    [RequiredIf(DependentProperty = "DisableValidation", TargetValue = false, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldRequired")] 
    [MinLength(2, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldTooShort")] 
    [MaxLength(50, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldTooLong")] 
    [Display(ResourceType = typeof(Language), Name = "County")] 
    public string County { get; set; } 

    [Required(ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldRequired")] 
    [MaxLength(5, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldInvalid")] 
    [MinLength(5, ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldInvalid")] 
    [RegularExpression(@"^\d+$", ErrorMessageResourceType = typeof(Language), ErrorMessageResourceName = "FieldInvalid")] 
    [Display(ResourceType = typeof(Language), Name = "ZipCode")] 
    public string ZipCode { get; set; } 

} 

不是最好的解决方案,但它的工作原理和数据模型完全验证,因为用户通过网站进展。当然,如果用户输入一个值,格式化验证将被强制执行。

+0

最后一点,我有一个本地化的应用程序。如果您不需要,请改用ErrorMessage =“...”。 – B2K

+0

感谢 - 这就是我一直在寻找 - 是啊,这是一个数据库,第一种方式,所以它不是容易改变DB .. – MaurGi

1

真的有没有简单的方法来完成这项工作。我唯一的建议是一个自定义模型验证器,它考虑了每个局部视图中的前一个字段。例如:

class LabRatModel 
{ 
    public int a { get; set; } 
    public int b { get; set; } // Say the partial splits right here 
    public int c { get; set; } 
    public int d { get; set; } 
} 

在你自定义的验证,尝试:

public override bool IsValid(object model) 
{ 
    var labrat = model as LabRatModel; 
    return labrat.b > 0 && labrat.c > 0; 
} 

的一点是要检查前一个字段,并从那里走。

+0

好建议,必填如果我正在寻找解决方案。谢谢! – MaurGi

1

由于B2K Answer指出,使用ViewModel有优势。我不是一直反复地来回映射字段的粉丝,所以我喜欢使用MetaData和Validation类来封装这个概念。

public TheMainClass 
{ 
    public string Prop1 { get; set; } 
    public string Prop2 { get; set; } 
    public string Prop3 { get; set; } 
    public string Prop4 { get; set; } 
} 

如果我只显示和验证每个支撑每个视图,然后我将创建4款专为验证设计:

[Metadata(typeof(Main1Model.IView1Validation))] 
public Main1Model : TheMainClass 
{ 
    internal interface IView1Validation 
    { 
    [Required] 
    string Prop1 { get; set; } 

    [ScaffoldColumn(false)] 
    string Prop2 { get; set; } 

    //etc 
    } 
} 

ViewModel2:

[Metadata(typeof(IView2Validation))] 
public Main2Model : TheMainClass 
{ 
    internal interface IView1Validation 
    { 
    [HiddenInput(DisplayValue = false)] 
    [Required] 
    string Prop1 { get; set; } 

    [Required] 
    string Prop2 { get; set; } 

    //etc 
    } 
} 

等等。随着观点的进展,如果有人决定使用浏览器工具来隐藏隐藏字段,您仍然有验证。

一个视图模型将封装所有视图模式:(我总是这样做,因为B2K建议为好)

public MainViewModel 
{ 
    public MainModel { get; set; } 
} 

等。

然后操作会看起来像:

[HttpPost] 
public ActionResult View1(MainViewModelmodel) 
{ 
    if (ModelState.IsValid) 
    { 
    TempData["View1"] = model; 
    return this.RedirectToAction("View2"); 
    } 

    return this.View(model); 
} 

public ActionResult View2() 
{ 
    model = TempData["View"] as Main1ViewModel; 
    if (model == null) 
    { 
    return this.RedirectToAction("View1"); 
    } 

    return this.View(model) 
} 

[HttpPost] 
public ActionResult View2(MainViewModel model) 
{ 
    // and so on (like HttpPost View1 
} 

然后你可能会问..但等待这是如何工作的? ViewModel使用基类型,这就是MVC的魔力。你会发现,View2.cshtml是强类型来MainViewModel但视图使用具体类型传入,而不是定义类型的传递

所有视图看起来像(甚至可以是相同的观点。):

@model MainViewModel 

    @EditFor(m => m.MainModel.Prop1) 
    @EditFor(m => m.MainModel.Prop2) 
    @EditFor(m => m.MainModel.Prop3) 
    @EditFor(m => m.MainModel.Prop4) 

如果Model1View与支架falsê传递,()■不会产生任何HTML元素editfor。

随着用户的进展和您指定[HiddenInput]的editfor()S创建一个隐藏字段。

真棒酱的最后一位是双重的;首先你不必坚持内存模型(因为每一个模型是完全传递给视图),其次后退按钮的工作原理,因为该模型被存储在视图投入(包括隐藏字段)

+0

谢谢 - 我想和子类类似的方法,但是我觉得RequiredIf是一个简单的解决方案我案件。 – MaurGi

0

为了与“DRY”原则保持一致,当您的域实体可以整齐地包含所有需要的验证元数据注释时,我认为将所有实体映射到视图模型是有害的。在为这个部分验证问题寻找一个更令人满意的解决方案时,我在视图模型中保留一个“步骤”数,并将我的域实体添加为该视图中的子组件。我发现我可以在我的if(Model.IsValid){和删除违规条目之前检查此步数,从而保留域实体上的注释。例如。

public MainViewModel 
{ 
    public ModelType MainModel { get; set; }//Domain entity containing my validation metadata annotations 
    public int StepCount { get; set; } 
} 

然后我的行动

public ActionResult SubmitProcess(MainViewModel model) 
{ 
    int NextStep = model.StepCount+1; 
    if (NextStep <= 4)//I am not ready to perform the below validation at this time. 
    ModelState.Remove("MainModel.Prop1");//So remove the key! 

    if (!ModelState.IsValid)//Now validate and allow next step only if valid 
    NextStep = model.StepCount;//Failed so retract movement to NextStep. 

    if (NextStep != model.StepCount) 
    { 
    ModelState.Remove("StepCount");//Another 'gotcha' - model needs a 'nudge' to refresh hidden fields! 
    model.StepCount = NextStep; 
    } 

    return View(model)//model.StepCount now incremented only if partial validation passed - so do an action based on that. 
}