質問

使い始めた頃 xVal クライアント側の検証では、ドメイン モデル オブジェクトをビューモデルとして使用するか、ビューモデルにそれらのオブジェクトの埋め込みインスタンスを使用するアクション メソッドのみを実装していました。

このアプローチはほとんどの場合正常に機能しますが、ビューでモデルのプロパティのサブセットのみを表示してポストバックする必要がある場合があります (たとえば、ユーザーがパスワードを更新したいが、残りのプロファイル データは更新したくない場合)。 。

(醜い) 回避策の 1 つは、フォーム上に他の方法では存在しない各プロパティの非表示の入力フィールドをフォーム上に設けることです。

どうやら、ここでのベストプラクティスは、ビューに関連するプロパティのみを含むカスタムビューモデルを作成し、次のようにビューモデルに値を設定することです。 オートマッパー. 。ビューに関連するデータのみを転送するため、はるかにクリーンですが、ドメイン モデル オブジェクトに既に存在する同じ検証属性を繰り返す必要があるため、完璧とは程遠いです。

理想的には、MetaData 属性 (これは「バディ クラス」とも呼ばれます) を介してドメイン モデル オブジェクトをメタ クラスとして指定したいのですが、メタデータ クラスに次のようなプロパティがある場合に xVal がスローされるため、これは機能しません。ビューモデルには存在しません。

これに対するエレガントな回避策はありますか?xVal ソースコードをハッキングすることを検討していますが、おそらくこれまで見落としてきた他の方法があるでしょう。

ありがとう、

エイドリアン

編集: ASP.NET MVC 2 の登場により、これは検証属性に関連する問題だけでなく、エディター属性や表示属性にも当てはまります。

役に立ちましたか?

解決

これが、入力画面をモデルに緊密に結合すべきではない典型的な理由です。この質問は実際に、MVC タグで月に 3 ~ 4 回ほど表示されます。前の質問を見つけることができれば、私は騙されますが、ここでのコメントの議論の一部は興味深いものです。;)

あなたが抱えている問題は、モデルの 2 つの異なる検証コンテキストを 1 つのモデルに強制的に組み込もうとしていることですが、これは大量のシナリオで失敗します。最良の例は、新しいユーザーをサインアップし、後で管理者にユーザー フィールドを編集させることです。登録中にユーザー オブジェクトのパスワードを検証する必要がありますが、ユーザーの詳細を編集する管理者にはパスワード フィールドは表示されません。

これらを回避するための選択肢はすべて次善です。私はこれまで 3 つのプロジェクトでこの問題に取り組んできましたが、次の解決策の実装は決してクリーンなものではなく、通常はイライラさせられます。私は努力するつもりです 実用的 他のみんなが行っている DDD/db/model/hotnessofthemonth の議論はすべて忘れてください。

1) 複数のビューモデルほぼ同じビューモデルを持つことは DRY 原則に違反しますが、このアプローチのコストは非常に低いと感じます。通常、DRYアンプに違反するとメンテナンスコストが高くなりますが、個人的な意見では、これにかかるコストは最も低く、大した額ではありません。仮定の話ですが、LastName フィールドに含めることができる最大文字数は頻繁には変更されません。

2) 動的メタデータMVC 2 には、モデルに独自のメタデータを提供するためのフックがあります。このアプローチを使用すると、メタデータの提供に使用するものをすべて、現在の HTTPRequest に基づいて特定のフィールド、したがってアクションとコントローラーを除外することができます。私はこの手法を使用して、DB にアクセスし、DataAnnotationsMetadataProvider のサブクラスにデータベースに格納されているプロパティ ベースの値を除外するように指示するデータベース主導のアクセス許可システムを構築しました。

この手法は非常にうまく機能していますが、唯一の問題は検証です。 UpdateModel(). 。この問題を解決するために、私たちは SmartUpdateModel() このメソッドもデータベースにアクセスし、許可されていないフィールドが検証されないように exclude string[] 配列を自動的に生成します。もちろんパフォーマンス上の理由からこれをキャッシュしたので、悪くはありません。

繰り返しになりますが、モデルで [ValidationAttributes] を使用し、実行時にそれらを新しいルールで置き換えました。最終的な結果は、 [Required] ユーザーにアクセス権限がない場合、User.LastName フィールドは検証されませんでした。

3) クレイジーなインターフェイスの動的プロキシの事私が試みた最後の手法は、ViewModel のインターフェイスを使用することでした。最終的には、次のようなインターフェイスから継承した User オブジェクトができました。 IAdminEdit そして IUserRegistration. 。IAdminEdit と IUserRegistration には両方とも、インターフェイスの Password プロパティなどのコンテキスト固有の検証をすべて実行する DataAnnotation 属性が含まれます。

これにはある程度のハッキングが必要であり、何よりも学術的な演習でした。2 と 3 の問題は、この手法を認識できるように UpdateModel と DataAnnotationsAttribute プロバイダーをカスタマイズする必要があることです。

私の最大の障害は、ユーザー オブジェクト全体をビューに送信したくなかったため、最終的に動的プロキシを使用して、 IAdminEdit

これは非常に xVal 固有の質問であることは理解しましたが、このような動的検証への道はすべて、内部 MVC メタデータ プロバイダーのカスタマイズにつながります。メタデータに関するものはすべて新しいものであるため、現時点ではそれほどクリーンで簡単な作業はありません。MVC の検証動作をカスタマイズするために必要な作業は難しくありませんが、すべての内部構造がどのように機能するかについての深い知識が必要です。

他のヒント

私たちは、検証がViewModelに層に属性を移動しました。我々はそれが最初の場所で無効な状態に入ることができなかったように私たちのドメインモデルを設計することがあったように私たちのケースでは、これは、とにかく懸念のクリーンな分離を提供します。たとえば、日付はBillingTransactionオブジェクト上で必要になる場合があります。だから我々は、それがNULL可能にする必要はありません。しかし、私たちのViewModelに、我々は、ユーザが値を入力していなかった状況をキャッチすることができるようなのNullableを公開する必要があるかもしれません。

他の例では、ページ/フォームごとに固有の検証を持っているかもしれない、とあなたは、ユーザーが実行するのではなく、原料の束を設定し、ドメインモデルを依頼しようとしているコマンドに基づいて検証したいと思います、 「ABC」を行う際に、これらの値が有効である場合には、「あなたはXYZをやろうとしているために有効です」。

viewmodelsが仮にあなたに強制されている場合は、

、そして、私は彼らが唯一のドメインに依存しない要件を適用することをお勧めします。これは、「ユーザ名が必要です」と「電子メールが正しくフォーマットされた」のようなものが含まれます。

あなたは、ビューモデルのドメインモデルから検証を複製する場合は、

、あなたはしっかりUIにドメインを結合しています。場合は、ドメインの検証の変更(「のみ週2クーポンを適用することができます」「のみ週1つのクーポンを適用することができます」となります)、UIを更新する必要があります。一般に、これはひどい、と俊敏性に有害である、話すます。

あなたはUIに、ドメインモデルから検証を移動する場合は、

、あなたは基本的に、あなたのドメインを全焼し、UI上での検証の責任を配置しました。二UIは、すべての検証を複製しなければならない、とあなたは、2つの別々のUIの一緒に連結されています。顧客が自分のiPhoneからの在庫を管理するために特別なインタフェースを望んでいるならば今、iPhoneのプロジェクトはまた、ウェブサイトのUIに発見されたすべての検証を複製する必要があります。 これは、上記の検証の重複よりもさらにひどいでしょう。

あなたは未来を予測することができ、これらの可能性を排除できない限り、

、唯一のドメインに依存しない要件を検証します。

これは、クライアント側の検証のためにプレーする方法を私は知らないが、部分的な検証があなたの問題である場合は、次のようにあなたは、プロパティ名のDataAnnotationsValidationRunnerリストに取るためにここで説明IEnumerable<string>を変更することができます:

public static class DataAnnotationsValidationRunner
{
     public static IEnumerable<ErrorInfo> GetErrors(object instance, IEnumerable<string> fieldsToValidate)
     {
           return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>().Where(p => fieldsToValidate.Contains(p.Name))
                  from attribute in prop.Attributes.OfType<ValidationAttribute>()
                  where !attribute.IsValid(prop.GetValue(instance))
                  select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
     }
}

私は(ASP.NET MVCで)のviewmodelsに何のメリットが特に作成し、それらを維持するためのオーバーヘッドを考慮すると、存在しないことのリスクdownvotesと状態つもりです。アイデアは、ドメインから分離することであるならば、それは弁解の余地があります。ドメインから切り離さUIは、そのドメインのUIではありません。 UI は、ドメインに依存しなければならないので、あなたは、どちらかのドメインモデルに結合され、あなたの閲覧/操作をするんだ、またはあなたのViewModel管理ロジックは、ドメインモデルに結合されました。アーキテクチャの引数は、このように議論の余地がある。

アイデアは、ドメインがこの要件を強制しなければならない)、彼らは変更することが許されるべきではないフィールド、その後、Aを変異させる結合ASP.NET MVCのモデルを利用する悪質なHTTPポストをハッキングからユーザーを防ぐことであり、場合B)アクションは、モデルバインダーに更新可能なプロパティのホワイトリストを提供する必要があります。

あなたは、ドメインではなく、エンティティのコピーのライブ、メモリ内のオブジェクトグラフのようなクレイジーなものを公開しているでない限り、

、のviewmodelsは努力を無駄にしています。だから、あなたの質問に答えるドメインモデルでドメインの検証を維持する。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top