![](/img/trans.png)
[英]MVC3 & StructureMap, injecting concrete class based on the controller
[英]mvc3 Reflect data annotations off a concrete class from an inteface
我试图从各种类型的视图中抽象我的视图模型。 整个事情编译没有问题,但我遇到了“反映”(正式称为拆箱)数据注释的问题。
我有一个界面:
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;}
}
人物编辑视图:views / Person / Edit as:
@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>
员工编辑视图:views / Employee / Edit:
@model Employee
Html.RenderAction("Edit", "Person", new { person = Model });
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => model.CompanyName)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.CompanyName)
@Html.ValidationMessageFor(model => model.CompanyName)
</div>
</div>
PersonController的位置是:
public ActionResult Edit(IPerson person)
{
return PartialView(person);
}
一切都编译好并且渲染得很好 。 但是, 数据注释正在丢失。
那么,Employee / Edit就像:
FirstName [textfield]
LastName [textfield]
你在什么公司工作? [textfield]公司名称是必填字段
无论如何都要为具体类拆箱那些数据注释?
边注
我尝试将IPerson显式地转换为Employee:
@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>
这样做需要第一个名称,但没有从标签中获取显示属性。
更新经过多次讨论是否已取消装箱后,我还没有找到一个从(更基本的)具体类中获取数据注释的简单解决方案。 在视图(或帮助器)中使用反射来获取具体类的数据注释,这实际上会破坏简单性的目标。
我们有一些基本相同但有必要字段和显示名称略有不同的视图。 如果我可以将视图模型传递到接口视图并且它将找出所需的字段和显示属性,那将是非常方便的。 如果有人想出办法做到这一点,将不胜感激。
我对TextBoxFor
帮助器没有生成正确的标记验证有同样的问题。
我能够解决它的方法是使用TextBox
助手而不是TextBoxFor
助手。
这是为我工作的部分片段
@model Interfaces.Models.EntryPage.ICustomerRegisterVM
<p>
@Html.ValidationMessageFor(model => model.Department)
@Html.TextBox(Html.NameFor(model => model.Department).ToString(), Model.Department)
</p>
如您所见,我使用Html.NameFor
帮助程序从表达式生成正确的名称,然后传入属性。 使用这种方法,MVC能够成功地为具体类生成正确的不显眼的验证标记,该标记实现了作为视图模型引用的接口。
我没有尝试过LabelFor
或其他助手的这种方法。 但我希望结果是一样的。
请注意, Html.NameFor
中提供了Html.NameFor
帮助程序
您正在调用PersonController.Edit
操作时指定一个没有数据属性的模型( IPerson
)。 默认元数据提供程序将仅选取在指定类型(在本例中为IPerson
)或继承的类型上显式定义的数据属性。 您可以共享元数据类或接口,也可以将数据注释属性复制到接口。
但是,我想你可能想重新设计它的工作原理(例如,调用RenderAction
将另一个视图包含到当前视图中是代码味道)。
我会为Person创建一个局部视图。 然后,您可以为每种类型的人(客户端等)创建局部视图。 然后,您可以添加任何其他标记,并使用@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;}
}
查看/共享/ 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>
查看/员工/ Edit.cshtml
@model Employee
@Html.Partial("Person", Model);
<div class="clear paddingbottomxxsm">
<div class="editor-label">
@Html.LabelFor(model => model.CompanyName)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.CompanyName)
@Html.ValidationMessageFor(model => model.CompanyName)
</div>
</div>
控制器/ EmployeesController.cs
public class EmployeesController : Controller
{
public ActionResult Edit(int id)
{
var model = GetEmployee(id); // replace with your actual data access logic
return View(model);
}
}
我发现这篇文章与完全相同的问题挣扎。
(旁注:我已经在此示例之后实现了我自己的DataAnnotationsModelMetadataProvider,以便能够访问我的编辑器模板中的自定义属性: http : //weblogs.asp.net/seanmcalinden/archive/2010/06/11/custom- asp-net-mvc-2-modelmetadataprovider-for-using-custom-view-model-attributes.aspx 。如果你想为这个问题使用你自己的DataAnnotationsModelMetadataProvider,请确保不要错过应用程序启动的步骤)
因此,在几乎放弃让这个工作后,我决定调试我的CreateMetadata覆盖,看看我能抓住那里。 我也找到了这篇文章:
从ASP.NET MVC中的ModelMetadataProvider获取包含对象实例
这与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;
}
}
所以现在我可以有一个EditorTemplate,它有一个接口类型作为模型,然后使用它的不同实现,通过数据注释可以有不同的字段名称和验证规则。 我正在使用它来获取一个带有三个不同地址的表单; 家庭住址,工作地址和发票地址。 这些输入组的用户界面完全相同,但验证规则不同。
这当然是一种基于约定的解决方案,它说当编辑器模板模型是一个接口时,这种行为应该始终适用。 如果你有现有的编辑器模板,其中模型是一个自己有数据注释的接口类型,这个解决方案当然会破坏它。 对于我的情况,我们刚刚开始使用MVC,现在这个约定将起作用。 将接口和实际类型的属性组合发送到基础实现会很有趣,但我会保存该实验以供日后使用。
如果您是一位了解此解决方案存在严重缺陷的读者,也请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.