3

ビューモデルをさまざまなタイプのビューから抽象化しようとしています。全体が問題なくコンパイルされますが、データ注釈の「反映」(正式にはボックス化解除と呼ばれます) に問題があります。

私はインターフェースを持っています:

public interface IPerson
{
    string FirstName { get;set;}
    string LastName {get;set;}
}

そして、インターフェイスをそのまま実装する2つのクラスがあります。

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

<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>

従業員の編集ビュー: ビュー/従業員/編集:

@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);
}

すべてが正常にコンパイルおよびレンダリングされます。ただし、データの注釈は失われています。

したがって、従業員/編集は次のようになります。

名 [テキストフィールド]

姓 [テキストフィールド]

どちらの会社にお勤めですか?[テキストフィールド] 会社名は必須フィールドです

具象クラスのデータ注釈をボックス化解除する方法はありますか?

サイドノート

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>

これを行うと、最初の名前が必要になりましたが、ラベルから表示プロパティを取得しませんでした。

更新 これがボックス化解除であるかどうかについて多くの議論を行った後、(より基本的な)具象クラスからデータ注釈を取得する簡単な解決策をまだ見つけていません。ビュー (またはヘルパー) でリフレクションを使用して具体的なクラスのデータ注釈を取得することは、単純化の目標を実際に無効にします。

基本的には同じですが、必須フィールドと表示名がわずかに異なるいくつかのビューがあります。ビューモデルをインターフェース化されたビューに渡すだけで、必要なフィールドと表示プロパティを把握できれば、非常に便利です。誰かがこれを行う方法を見つけた場合、それは大歓迎です。

4

3 に答える 3

2

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 は、viewmodel として参照されるインターフェイスを実装する具体的なクラスに対して、目立たない適切な検証マークアップを正常に生成することができました。

LabelForまたは他のヘルパーに対してこのアプローチを試したことはありません。しかし、結果が同じであることを願っています。

Html.NameForヘルパーは MVC5 で利用できることに注意してください

于 2015-10-15T12:57:17.603 に答える
1

アクションIPersonを呼び出すときに、データ属性を持たないモデル ( ) を指定しています。PersonController.Editデフォルトのメタデータ プロバイダは、指定された型 (この場合は ) で明示的に定義されたデータ属性、IPersonまたは継承されたデータ属性のみを取得します。メタデータ クラスまたはインターフェイスを共有したり、データ注釈属性をインターフェイスにコピーしたりできます。

ただし、これがどのように機能するかを少し再設計することをお勧めします (たとえば、RenderAction現在のビューに別のビューを含めるように呼び出すことはコードの匂いです)。

Person の部分ビューを作成します。次に、各タイプの人 (クライアントなど) の部分ビューを作成できます。その後、追加のマークアップを追加し、Personを使用してビューを含めることができます@Html.Partial("Person", Model)

インターフェイスの代わりに基本クラスを使用することもできます。そうしないと、およびPersonのデータ属性をオーバーライドするのが難しくなります。FirstNameLastName

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);
    }
}
于 2012-10-29T21:25:19.170 に答える
1

この投稿がまったく同じ問題に苦しんでいることがわかりました。

(余談ですが、このサンプルに従って独自の 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 を作成し、そのさまざまな実装を使用して、データ アノテーションを介してさまざまなフィールド名と検証ルールを設定できるようになりました。私はこれを 3 つの異なるアドレスを取るフォームに使用しています。自宅住所、勤務先住所、請求先住所。これらの入力グループのユーザー インターフェイスはまったく同じですが、検証規則が異なります。

もちろん、これは、エディター テンプレート モデルがインターフェイスである場合、この動作が常に適用されるべきであるという、ちょっとした慣例に基づいたソリューションです。モデルがそれ自体がデータ注釈を持つインターフェースタイプである既存のエディターテンプレートがある場合、このソリューションはもちろんそれを壊します。私の場合、MVC の使用を開始したばかりで、今のところこの規則は機能します。インターフェイスからの属性と実際の型の組み合わせを基本実装に送信するのは興味深いことですが、その実験は後で保存します。

また、このソリューションの重大な欠陥を認識している読者であるかどうかもお知らせください。

于 2013-11-02T09:21:56.517 に答える