C# mvc3从接口反映具体类的数据注释
我试图从各种类型的视图中抽象出我的视图模型。整个编译过程没有任何问题,但我在“反射”(正式称为取消装箱)数据注释方面遇到了问题 我有一个界面:C# mvc3从接口反映具体类的数据注释,c#,asp.net-mvc-3,data-annotations,unboxing,asp.net-mvc-views,C#,Asp.net Mvc 3,Data Annotations,Unboxing,Asp.net Mvc Views,我试图从各种类型的视图中抽象出我的视图模型。整个编译过程没有任何问题,但我在“反射”(正式称为取消装箱)数据注释方面遇到了问题 我有一个界面: public interface IPerson { string FirstName { get;set;} string LastName {get;set;} } 我有两个类实现了这样的接口: public class Employee : IPerson { [Required] [Display(Descrip
public interface IPerson
{
string FirstName { get;set;}
string LastName {get;set;}
}
我有两个类实现了这样的接口:
public class Employee : IPerson
{
[Required]
[Display(Description = "Employee First Name", Name = "Employee First Name")]
public string FirstName {get;set;}
[Required]
[Display(Description = "Employee Last Name", Name = "Employee Last Name")]
public string LastName {get;set;}
public int NumberOfYearsWithCompany {get;set;}
}
public class Client : IPerson
{
[Required]
[Display(Description = "Your first Name", Name = "Your first Name")]
public string FirstName {get;set;}
[Display(Description = "Your last Name", Name = "Your last Name")]
public string LastName {get;set;}
[Display(Description = "Company Name", Name = "What company do you work for?")]
public string CompanyName {get;set;}
}
@model IPerson
@{
var employee = (Employee)Model;
}
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => employee.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => employee.FirstName)
@Html.ValidationMessageFor(model => employee.FirstName)
</div>
</div>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => employee.LastName)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => employee.LastName)
@Html.ValidationMessageFor(model => employee.LastName)
</div>
</div>
人员编辑视图:视图/人员/编辑如下:
@model IPerson
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
一切都编译并呈现良好。但是,数据注释正在丢失
因此,员工/编辑的结果如下:
名字[文本字段]
LastName[textfield]
你在哪家公司工作?[textfield]公司名称是必填字段
对于具体类,是否有取消绑定这些数据注释的方法
旁注
我试图明确地将IPerson转换为员工:
public class Employee : IPerson
{
[Required]
[Display(Description = "Employee First Name", Name = "Employee First Name")]
public string FirstName {get;set;}
[Required]
[Display(Description = "Employee Last Name", Name = "Employee Last Name")]
public string LastName {get;set;}
public int NumberOfYearsWithCompany {get;set;}
}
public class Client : IPerson
{
[Required]
[Display(Description = "Your first Name", Name = "Your first Name")]
public string FirstName {get;set;}
[Display(Description = "Your last Name", Name = "Your last Name")]
public string LastName {get;set;}
[Display(Description = "Company Name", Name = "What company do you work for?")]
public string CompanyName {get;set;}
}
@model IPerson
@{
var employee = (Employee)Model;
}
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => employee.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => employee.FirstName)
@Html.ValidationMessageFor(model => employee.FirstName)
</div>
</div>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => employee.LastName)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => employee.LastName)
@Html.ValidationMessageFor(model => employee.LastName)
</div>
</div>
@model IPerson
@{
var-employee=(employee)模型;
}
@LabelFor(model=>employee.FirstName)
@Html.EditorFor(model=>employee.FirstName)
@Html.ValidationMessageFor(model=>employee.FirstName)
@LabelFor(model=>employee.LastName)
@Html.TextBoxFor(model=>employee.LastName)
@Html.ValidationMessageFor(model=>employee.LastName)
这样做会要求使用第一个名称,但不会从标签中获取显示属性
更新
在对是否取消装箱进行了大量讨论之后,我还没有找到从(更基本的)具体类中获取数据注释的简单解决方案。如果使用视图中的反射(或帮助器)来获取具体类的数据注释,那么它将真正挫败简单性的目标
我们有一些基本相同的视图,但它们的必填字段和显示名称略有不同。如果我能将一个视图模型传递到一个接口视图中,它就能计算出所需的字段和显示属性,那将非常方便。如果有人想出了这样做的方法,我们将不胜感激。您正在指定一个模型(IPerson
),当您调用PersonController.Edit
操作时,该模型没有数据属性。默认元数据提供程序将仅拾取在指定类型(在本例中为IPerson
)或继承的类型上明确定义的数据属性。可以共享元数据类或接口,也可以将数据注释属性复制到接口
但是,我认为您可能需要重新设计一下它的工作方式(例如,调用RenderAction
将另一个视图包含到当前视图中是一种代码味道)
我会为个人创建一个局部视图。然后,可以为每种类型的人(客户机等)创建局部视图。然后,您可以添加任何附加标记,并使用@Html.Partial(“Person”,Model)
包含您的Person
视图
您可能还希望使用基类Person
而不是接口,否则重写FirstName
和LastName
的数据属性会变得很棘手
public abstract class Person
{
public virtual string FirstName {get;set;}
public virtual string LastName {get;set;}
}
public class Employee : Person
{
[Required]
[Display(Description = "Employee First Name", Name = "Employee First Name")]
public override string FirstName {get;set;}
[Required]
[Display(Description = "Employee Last Name", Name = "Employee Last Name")]
public override string LastName {get;set;}
public int NumberOfYearsWithCompany {get;set;}
}
public class Client : Person
{
[Required]
[Display(Description = "Your first Name", Name = "Your first Name")]
public override string FirstName {get;set;}
[Display(Description = "Your last Name", Name = "Your last Name")]
public override string LastName {get;set;}
[Display(Description = "Company Name", Name = "What company do you work for?")]
public string CompanyName {get;set;}
}
视图/Shared/Person.cshtml
@model Person
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
我发现这篇文章与同样的问题格格不入 (附带说明:我已经按照此示例实现了自己的DataAnnotationsModelMetadataProvider,以便能够访问编辑器模板中的自定义属性: 。如果要使用自己的DataAnnotationsModelMetadataProvider解决此问题,请确保不要错过应用程序启动中的步骤) 所以,在几乎放弃让它工作之后,我决定调试我的CreateMatadata覆盖,看看我能在那里得到什么。我还发现了以下帖子: 这与DataAnnotationsModelMetadataProvider类的某些反映相结合,使我找到了以下解决方案:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
//If containerType is an interface, get the actual type and the attributes of the current property on that type.
if (containerType != null && containerType.IsInterface)
{
object target = modelAccessor.Target;
object container = target.GetType().GetField("container").GetValue(target);
containerType = container.GetType();
var propertyDescriptor = this.GetTypeDescriptor(containerType).GetProperties()[propertyName];
attributes = this.FilterAttributes(containerType, propertyDescriptor, Enumerable.Cast<Attribute>((IEnumerable)propertyDescriptor.Attributes));
}
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
//This single line is for the "sidenote" in my text above, remove if you don't use this:
attributes.OfType<MetadataAttribute>().ToList().ForEach(x => x.Process(modelMetadata));
return modelMetadata;
}
}
公共类MyModelMetadataProvider:DataAnnotationsModelMetadataProvider
{
受保护的重写ModelMetadata CreateMatadata(
IEnumerable属性,
类型containerType,
函数模型访问器,
类型modelType,
字符串属性名称)
{
//如果containerType是接口,则获取该类型的实际类型和当前属性的属性。
if(containerType!=null&&containerType.IsInterface)
{
objecttarget=modelacessor.target;
对象容器=target.GetType().GetField(“容器”).GetValue(目标);
containerType=container.GetType();
var propertyDescriptor=this.GetTypeDescriptor(containerType.GetProperties()[propertyName];
attributes=this.FilterAttributes(containerType、propertyDescriptor、Enumerable.Cast((IEnumerable)propertyDescriptor.attributes));
}
var modelMetadata=base.CreateMetadata(属性、containerType、modelAccessor、modelType、propertyName);
//这一行是我上面文本中的“侧注”,如果不使用,请删除:
attributes.OfType().ToList().ForEach(x=>x.Process(modelMetadata));
返回模型元数据;
}
}
所以现在我可以有一个EditorTemplate,它有一个接口类型作为模型,然后使用它的不同实现,通过数据注释可以有不同的字段名和验证规则。我用这个来表示一个表单,它有三个不同的地址;家庭地址、工作地址和发票地址。这些输入组的用户界面完全相同,但验证规则不同
当然,这是一个基于约定的解决方案,表示当编辑器模板模型是一个接口时,这种行为应该始终适用。如果您有现有的编辑器模板,其中模型是一种接口类型,并且它本身具有数据注释,那么这个解决方案当然会打破这一点。就我而言,我们刚刚开始使用MVC,现在
public class EmployeesController : Controller
{
public ActionResult Edit(int id)
{
var model = GetEmployee(id); // replace with your actual data access logic
return View(model);
}
}
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
//If containerType is an interface, get the actual type and the attributes of the current property on that type.
if (containerType != null && containerType.IsInterface)
{
object target = modelAccessor.Target;
object container = target.GetType().GetField("container").GetValue(target);
containerType = container.GetType();
var propertyDescriptor = this.GetTypeDescriptor(containerType).GetProperties()[propertyName];
attributes = this.FilterAttributes(containerType, propertyDescriptor, Enumerable.Cast<Attribute>((IEnumerable)propertyDescriptor.Attributes));
}
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
//This single line is for the "sidenote" in my text above, remove if you don't use this:
attributes.OfType<MetadataAttribute>().ToList().ForEach(x => x.Process(modelMetadata));
return modelMetadata;
}
}
@model Interfaces.Models.EntryPage.ICustomerRegisterVM
<p>
@Html.ValidationMessageFor(model => model.Department)
@Html.TextBox(Html.NameFor(model => model.Department).ToString(), Model.Department)
</p>