ModelStateは、ASP.NET MVC 2のモデル上のプライベートプロパティの無効として報告されました
-
14-09-2020 - |
質問
私はASP.NET MVC 2.0を使用しています。また、コントローラのモデルバインディングやModelState検証を利用しようとしています。しかし、私は問題に反して、あなたが思うものを見るためにここで人々とそれを共有したいと思いました。
OK私のモデルクラスライブラリに私のクリーンなユーザーPOCOを持っています...
namespace Model
{
public partial class User
{
public virtual int Id { get; private set; }
public virtual string UserName { get; private set; }
public virtual string DisplayName { get; set; }
public virtual string Email { get; set; }
public User(string displayName, string userName)
: this()
{
DisplayName = displayName;
UserName = userName;
}
}
}
.
オブジェクトが構築された後に、特定のプロパティを編集できるようにしました。たとえば、オブジェクトが構築されているときにのみユーザー名は設定できます。これはoo senseになりますが、私の問題の鍵であるので、ここでそれを強調したいと思いました。
私のユーザークラスの検証メタデータを定義する「Buddy Class」を持ちます...
namespace Model
{
[MetadataType(typeof(UserMetadata))]
public partial class User
{
class UserMetadata
{
[Required]
public virtual int Id { get; set; }
[Required]
public virtual string UserName { get; set; }
[Required]
public virtual string DisplayName { get; set; }
[RegularExpression(@"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$", ErrorMessage = "Invalid address")]
public virtual string Email { get; set; }
}
}
.
}
その後、私のWebレイヤーにアップして、ユーザーがこのオブジェクトを編集できるようにしたい。だから私は私のプロファイルコントローラに次の2つのアクションメソッドを持っています。
namespace Web.Controllers
{
public class ProfileController : Controller
{
[Authorize]
public ActionResult Edit()
{
var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name );
return View(user);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
[TransactionFilter]
public ActionResult Edit(User updatedUser)
{
// Get the current user to update.
var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name);
if (ModelState.IsValid)
{
TryUpdateModel(user);
// Update store...
}
return View(updatedUser);
}
}
}
.
これには、それに入るための強く型付けされたビューがあります...
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Model.User>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%=Html.Script("jquery.validate.js")%>
<%=Html.Script("MicrosoftMvcJQueryValidation.js")%>
<%=Html.Script("MvcFoolproofJQueryValidation.js")%>
<div class="container">
<div class="column span-14">
<% using (Html.BeginForm()) {%>
<%= Html.AntiForgeryToken() %>
<fieldset>
<%: Html.DisplayFor(model => model.UserName) %>
<%= Html.Label("Display Name") %>
<%= Html.HiddenFor(model => model.DisplayName)%>
<%= Html.ValidationMessageFor(model => model.DisplayName)%>
<%= Html.Label("Email address") %>
<%= Html.EditorFor(model => model.Email)%>
<%= Html.ValidationMessageFor(model => model.Email)%>
<%= Html.HiddenFor(model => model.UserName)%>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</div>
<div class="clear"></div>
<% } %>
</div>
</asp:Content>
.
OK、すべてのコードを邪魔にならないように!!
それでは問題があるため、最初のGETリクエストの後にビューは微細化されます。しかし、ユーザーがフォームバックを投稿すると、表示名を編集した後にModelStateは無効です。これは、usernameプロパティにプライベートセッターがあるためです。しかし、これはデザインによるものです。ただし、プロパティに必要な属性を追加したので、設定されていないので失敗しています!
問題は、これを検証エラーとして報告しているかどうかという問題があるか。プロパティがプライベートなので、私はそれを設定しないように設計しました、したがって、モデルバインダーがそれを設定することを期待していませんが、検証エラーが欲しくありません。私はそれが設定できるプロパティの検証エラーのみを生成するべきであると思います。
OK私がこれまでのところに起きてきた可能な解決策。
プロパティを公開します。
私がこれを行ったら私は既存のユーザーのためにユーザー名を変更することを可能にするために自分自身を開きます。私はこれを捉えるために追加の論理を追加する必要があります、本当にとても素敵ではありません。投稿を介してそれを設定しようとしていないいたずらな人々を止めるために、アクション方法にバインド除外を追加する必要があります。
エラーを取り除く
ModelState Dictionaryからエラーを削除できると思いますが、これはこの機会には問題ありませんが、私はプライベートセッターを持つすべての私のオブジェクトのためにこれを追加する必要があるように、これがいくつかのコードの匂いを招くと思います。私はおそらく忘れるでしょう!
インタフェースに対して私のビューを強く入力
私は自分のビューを自分のモデルのインターフェースにバインドすることを読んでいます、これはBusiness ModelオブジェクトへのModelViewインターフェースの王です。私はこのアイデアが好きですが、私は自動バインディングを失い、私のモデルオブジェクトを私のWeb層のコンストラクタに複製する必要があるでしょう。これに関するいくつかの情報
モデルビューを使う
これは私には乾いていないようですか?既存のモデルオブジェクトが揃っていない場合は、これらを使用できます(たとえば、サインアップモデルビューを使用します)。
CustomModelBinder
私の好みのオプションですが、私は私がしていることを知っているよくわかりません!私がそれが設定できるプロパティにのみバインダーをバインダーに入れることができたら、私は笑っているでしょう!
人々はどう思いますか?上記のオプション、その他の解決策についてのコメントは、私のアーキテクチャとのマークからすぐですか?
ありがとうございました:)
解決 3
JFARは、Brad Comments ...
のBrad Wilsonによって投稿に良いリンクを投稿しました。まだ部分的な編集をすることができますが、 部分的な検証はできません もっと。あなたがバインディングを除外するならば [必須] 属性、検証は失敗します。 あなたは回避するためのいくつかの選択肢を持っています これ:
フォームデータを正確にミラーしたビューモデルを使用する
呼び出し前にデータを含む非解除フィールド (try)UpdateModel 検証は成功します(たとえあっても あなたは何でもするつもりはありません そのデータ)
検証エラーが発生し、からそれらを削除することができます。 検証後のModelState、 彼らは不適切なエラーなので
私の訴訟は「部分的な編集」の場合に収まるようです、ここで特定のフィールドを更新したくない。
私はこれらの解決策としてこれらを調べます。
他のヒント
"私はそれが設定されないように設計されているので、モデルバインダーがそれを設定することを期待していませんが、検証エラーが発生しません。検証を生み出したと思います設定できるプロパティのエラー。 "
このデザインの決定についてもっと読む:
http. ://bradwilson.typepad.com/blog/2010/01/iNput-validation-vs-model-validation-in-aspnet-mvc.html
興味深いことに、ほとんどの人はあなたの不満を訴えるものと完全な反対を訴えました。 ;)
基本的にシステムに設定できないものを常に設定する必要があることを伝えます。だから私はMVCが誤って働いているのではないか、そのようなものは何でもないと言うでしょう。不可能なシナリオをコーディングするだけです。
全体的にあなたのちょうどメタデータベジュディ技術の痛みの点に達しています。主に、新規および編集シナリオに対する異なる検証が必要な必要がある。
"私がこれを行うならば、私は既存のユーザーのためにユーザー名を変更することを可能にするために自分自身を開く。私はこれを捉えるためにどこかに追加の論理を追加する必要があります。 POSTを介して設定しようとしているいたずらな人々を停止するためのアクション方法で除外する。 "
これらのコードへの過食を変更します。単純な文字列を1つのメソッド呼び出しに追加しているでしょう。大したことは何ですか?私はここで実用的なアプローチを取ります。
その仕事に最適なので、ビューモデルを使用したいと思います。DRY意味を考えないでください。この場合、モデルバインディングに対処するセマンティクスはドメインモデルに一致しませんので、翻訳する方法が必要です。