Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# mvc3从接口反映具体类的数据注释_C#_Asp.net Mvc 3_Data Annotations_Unboxing_Asp.net Mvc Views - Fatal编程技术网

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>