私が取り組んでいるプロジェクトの重要な機能の 1 つは、ユーザーが既存のフィールド タイプ (よく知られているタイプなど) のプールに基づいてフォームを構成できることです ("フォーム" のように)。 「ユーザー名」、「生年月日」などだけでなく、「文字列」、「DateTime」などの「ジェネリック型」もあります)。
以前は、「よく知られている」型に対して正常に機能する静的な ViewModel があり、次のように見えました。
public class UserInputModel
{
[StringLength(200)]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
public DateTime BirthDate { get; set; }
//Here comes a lot of other properties
}
すべての既知のプロパティがリストされ、コンテキストに応じてそれらを表示または非表示にしていました。
しかし、最後の要件が来て、すべてが変わりました。ユーザーは、必要な数のジェネリック型フィールドを追加できるようになりました。これを行うために、この InputModel を完全に動的にすることにしました。次のようになります。
public class UserInputModel
{
// Each ModelProperty has an "Id" and a "Value" property
public ICollection<ModelProperty> Properties { get; set; }
}
これは魅力のように機能します。Razor ビューは、コレクションを反復処理し、コレクションの各プロパティに対応するコントロールを標準以上の方法で作成するだけで済みます。
@Html.TextBoxFor(m => m.Properties[index].Value);
...そして、入力されたフォームとしてデータをうまく取得します。
=> これは問題なく動作しますが、クライアント側の検証はありません。このためには、いくつかのメタデータが必要になります...モデルを動的に作成しているため、注釈を介してはもうありません。
これらの MetaData を提供するために、CustomModelMetadataProvider
継承するを作成し、Global.asaxDataAnnotationsModelMetadataProvider
に新規登録しました。ModelMetadataProvider
このCreateMetadata()
関数は、ViewModel の作成時に呼び出され、ViewModel の各プロパティに対して呼び出されます。
問題の始まり: 現在のプロパティにいくつかのメタデータを追加するには、まず、現在見ているプロパティを特定する必要があります (「名前」の最大長は 200 ですが、「生年月日」はそうでないため、割り当てることができません)。デフォルトではすべてのプロパティに maxlength があります)。Value
そして、すべてのプロパティが同じ名前と同じコンテナタイプを持っているため、どういうわけかまだそれを行うことができませんでしたModelProperty
。
リフレクションを介してプロパティのコンテナーにアクセスしようとしましたが、ModelAccessor のターゲットは ViewModel 自体であるため (ラムダ式のためm => m.Properties
)、次の構成により、ModelProperty だけでなく ViewModel 全体が得られます。
var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);
これを何度もめくっていますが、手元にある ModelProperty を特定する方法が見つかりません。これを行う方法はありますか?
更新:しばらくあらゆる方向にこれをひっくり返した後、最終的に別の方向に進みました。基本的に、控えめな JavaScript を使用して、属性やメタデータに触れることなく MVC の検証機能を使用しています。value-data="true"
つまり、HTML 属性(および他のすべての必須属性) を@Html.TextBoxFor()
ステートメントに追加します。これは、すべてのアトミック検証 (必須、文字列長など) でうまく機能します。