pac*_*acu 9 razor unobtrusive-validation asp.net-mvc-3
我的模型中有以下设置:
namespace QuickTest.Models
{
public class Person
{
[Required]
[Display(Name = "Full name")]
public string FullName { get; set; }
[Display(Name = "Address Line 1")]
public virtual string Address1 { get; set; }
}
public class Sender : Person
{
[Required]
public override string Address1 { get; set; }
}
public class Receiver : Person
{
}
}
Run Code Online (Sandbox Code Playgroud)
在我看来:
@model QuickTest.Models.Person
@{
ViewBag.Title = "Edit";
}
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm()) {
<fieldset>
<legend>Person</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FullName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FullName)
@Html.ValidationMessageFor(model => model.FullName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Address1)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Address1)
@Html.ValidationMessageFor(model => model.Address1)
</div>
<div class="errors">
@Html.ValidationSummary(true)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Run Code Online (Sandbox Code Playgroud)
启用客户端验证.但是,如果我将一个Sender类型的对象发送到View,则客户端验证不会检测到需要Address1字段.有没有办法让客户端验证在这种情况下工作?
PS:我发现如果我使用以下内容在视图中显示Address1字段,则客户端验证有效:
<div class="editor-field">
@Html.Editor("Address1", Model.Address1)
@Html.ValidationMessageFor(model => model.Address1)
</div>
Run Code Online (Sandbox Code Playgroud)
您可以自定义验证器和元数据来自您的具体类,但该解决方案有几个移动部件,包括两个自定义元数据提供程序.
首先,创建一个自定义Attribute来装饰基类的每个属性.这对于我们的自定义提供程序来说是必需的,以指示何时需要进一步分析.这是属性:
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class BaseTypeAttribute : Attribute { }
Run Code Online (Sandbox Code Playgroud)
接下来,创建一个ModelMetadataProvider继承自的自定义DataAnnotationsModelMetadataProvider:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
var attribute = attributes.FirstOrDefault(a => a.GetType().Equals(typeof(BaseTypeAttribute))) as BaseTypeAttribute;
if (attribute != null && modelAccessor != null)
{
var target = modelAccessor.Target;
var containerField = target.GetType().GetField("container");
if (containerField == null)
{
var vdi = target.GetType().GetField("vdi").GetValue(target) as ViewDataInfo;
var concreteType = vdi.Container.GetType();
return base.CreateMetadata(attributes, concreteType, modelAccessor, modelType, propertyName);
}
else
{
var container = containerField.GetValue(target);
var concreteType = container.GetType();
var propertyField = target.GetType().GetField("property");
if (propertyField == null)
{
concreteType = base.GetMetadataForProperties(container, containerType)
.FirstOrDefault(p => p.PropertyName == "ConcreteType").Model as System.Type;
if (concreteType != null)
return base.GetMetadataForProperties(container, concreteType)
.FirstOrDefault(pr => pr.PropertyName == propertyName);
}
return base.CreateMetadata(attributes, concreteType, modelAccessor, modelType, propertyName);
}
}
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,创建一个ModelValidatorProvider继承自的自定义DataAnnotationsModelValidatorProvider:
public class MyModelMetadataValidatorProvider : DataAnnotationsModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
List<ModelValidator> vals = base.GetValidators(metadata, context, attributes).ToList();
var baseTypeAttribute = attributes.FirstOrDefault(a => a.GetType().Equals(typeof(BaseTypeAttribute)))
as BaseTypeAttribute;
if (baseTypeAttribute != null)
{
// get our parent model
var parentMetaData = ModelMetadataProviders.Current.GetMetadataForProperties(context.Controller.ViewData.Model,
metadata.ContainerType);
// get the concrete type
var concreteType = parentMetaData.FirstOrDefault(p => p.PropertyName == "ConcreteType").Model;
if (concreteType != null)
{
var concreteMetadata = ModelMetadataProviders.Current.GetMetadataForProperties(context.Controller.ViewData.Model,
Type.GetType(concreteType.ToString()));
var concretePropertyMetadata = concreteMetadata.FirstOrDefault(p => p.PropertyName == metadata.PropertyName);
vals = base.GetValidators(concretePropertyMetadata, context, attributes).ToList();
}
}
return vals.AsEnumerable();
}
}
Run Code Online (Sandbox Code Playgroud)
之后,Application_Start在Global.asax.cs中注册两个自定义提供程序:
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new MvcApplication8.Controllers.MyModelMetadataValidatorProvider());
ModelMetadataProviders.Current = new MvcApplication8.Controllers.MyModelMetadataProvider();
Run Code Online (Sandbox Code Playgroud)
现在,改变你的模型:
public class Person
{
public Type ConcreteType { get; set; }
[Required]
[Display(Name = "Full name")]
[BaseType]
public string FullName { get; set; }
[Display(Name = "Address Line 1")]
[BaseType]
public virtual string Address1 { get; set; }
}
public class Sender : Person
{
public Sender()
{
this.ConcreteType = typeof(Sender);
}
[Required]
[Display(Name = "Address Line One")]
public override string Address1 { get; set; }
}
public class Receiver : Person
{
}
Run Code Online (Sandbox Code Playgroud)
请注意,基类具有新属性ConcreteType.这将用于指示哪个继承类已实例化此基类.每当继承类具有覆盖基类中元数据的元数据时,继承类的构造函数应该设置基类ConcreteType属性.
现在,即使您的视图使用基类,特定于任何具体继承类的属性也将出现在您的视图中,并将影响模型的验证.
此外,您应该能够将View转换为Person类型的模板,并将该模板用于使用基类的任何实例或从中继承.
| 归档时间: |
|
| 查看次数: |
5600 次 |
| 最近记录: |