11

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

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

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

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

理想的には、MetaData 属性を介して Domain Model オブジェクトをメタ クラスとして指定したいと考えています (これは、「バディ クラス」とも呼ばれます)。ビューモデルには存在しません。

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

ありがとう、

エイドリアン

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

4

5 に答える 5

7

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

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

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

1) 複数 のビュー モデル ほぼ同じビューモデルを持つことは DRY 原則に違反しますが、このアプローチのコストは非常に低いと感じています。通常、DRY に違反するとメンテナンス コストが増加しますが、IMHO のコストは最も低く、大した額にはなりません。仮説的に言えば、LastName フィールドの最大文字数を頻繁に変更することはありません。

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

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

モデルで [ValidationAttributes] を使用し、実行時に新しいルールで置き換えたことを繰り返したいと思います。その結果[Required]、ユーザーがアクセス許可を持っていない場合、User.LastName フィールドは検証されませんでした。

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

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

私の最大の障害は、ユーザー オブジェクト全体をビューに送信したくなかったため、動的プロキシを使用してランタイム インスタンスを作成することになりました。IAdminEdit

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

于 2010-11-01T17:39:07.303 に答える
4

検証属性を ViewModel レイヤーに移動しました。私たちの場合、そもそも無効な状態にならないようにドメイン モデルを設計することができたので、とにかく、これにより問題がより明確に分離されました。たとえば、BillingTransaction オブジェクトでは Date が必要になる場合があります。したがって、Nullable にしたくありません。ただし、ViewModel では、ユーザーが値を入力しなかった状況を把握できるように、Nullable を公開する必要がある場合があります。

他の場合では、ページ/フォームごとに固有の検証があり、一連のものを設定してドメイン モデルに尋ねるのではなく、ユーザーが実行しようとしているコマンドに基づいて検証する必要があります。 XYZ を実行しようとする場合に有効です。"ABC" を実行する場合、これらの値は有効です。

于 2010-01-17T19:45:29.177 に答える
3

ViewModel が強制されていると仮定している場合は、ドメインに依存しない要件のみを適用することをお勧めします。これには、「ユーザー名が必要です」や「電子メールの形式が適切である」などが含まれます。

ビュー モデルのドメイン モデルから検証を複製すると、ドメインが UI に密結合されたことになります。ドメインの検証が変更された場合 (「週に 2 つのクーポンのみ適用可能」が「週に 1 つのクーポンのみ適用可能」になる)、UI を更新する必要があります。一般的に言えば、これはひどいものであり、敏捷性に悪影響を及ぼします。

検証をドメイン モデルから UI に移すと、実質的にドメインを破壊し、UI に検証の責任を負わせることになります。2 番目の UI はすべての検証を複製する必要があり、2 つの別個の UI を結合しました。顧客が iPhone から在庫を管理するための特別なインターフェイスを必要とする場合、iPhone プロジェクトは Web サイト UI にもあるすべての検証を複製する必要があります。これは、上記の検証の重複よりもさらにひどいものです。

将来を予測してその可能性を排除できない限り、ドメインにとらわれない要件のみを検証してください。

于 2010-01-23T00:36:10.103 に答える
2

これがクライアント側の検証でどのように機能するかはわかりませんが、部分的な検証が問題である場合は、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);
     }
}
于 2010-02-18T04:22:41.263 に答える
0

ViewModels (ASP.NET MVC) には何のメリットもないと宣言します。特に、ViewModels の作成と維持のオーバーヘッドを考慮してください。アイデアがドメインから分離することである場合、それは弁解の余地がありません。ドメインから切り離された UI は、そのドメインの UI ではありません。UIはドメインに依存する必要があるため、ビュー/アクションをドメイン モデルに結合するか、ViewModel 管理ロジックをドメイン モデルに結合します。したがって、アーキテクチャの議論は議論の余地があります。

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

ドメインがエンティティのコピーではなく、ライブのメモリ内オブジェクト グラフのようなクレイジーなものを公開していない限り、ViewModel は無駄な労力です。したがって、あなたの質問に答えるには、ドメイン モデルでドメインの検証を維持してください。

于 2010-01-23T00:29:01.710 に答える