私は本当にこれについて 2 番目の目を必要としているので、あなたの何人かが私にフィードバックをくれることを願っています。
ASP.NET MVC3 を使用して Web サイトをセットアップしようとしていますが、このサイトでは動的オブジェクトを作成する柔軟性が必要です。しかし、私のデータベースには、これらの動的オブジェクトに含まれる構造とデータに関する情報を格納する一連のテーブルが設定されているということです。既存のデータベースを使用しているため、変更できる内容が (ある程度) 制限されています。データベースに動的オブジェクト (.NET 4.0 の動的オブジェクトではない) を照会すると、ID を渡すと返されるのは、内部使用のみを目的とした少数のプロパティを持つ単純なオブジェクトと、動的オブジェクトのすべてのプロパティを含むコレクションであるプロパティ。したがって、動的オブジェクトが名前、生年月日、および性別を持つ人物用である場合、コレクションには各プロパティに 1 つずつ、合計 3 つのオブジェクトが含まれます。これにより、サイトの管理者は実行時に新しいフィールドを追加でき、Web サイトはそれらを自動的にレンダリングし、更新などを許可します。現在、このデータ構造の表示とポストバックの両方でモデル バインディングが機能しています。コレクション内の各オブジェクトについて、私はプロパティの一意の ID (現在は隠しフィールドであり、ID は Guid です) とプロパティの値の 2 つのデータをレンダリングします。私の問題はセキュリティ面です。
厳密に型指定されたオブジェクトを扱っている場合、カスタム ViewModel を作成してそれを処理するか、アクションのシグネチャに Bind() 属性を追加できますが、これらのオブジェクトのプロパティは柔軟なコレクションであるため、どのようにアプローチすればよいかわかりません。アクション レベルのセキュリティは非常に単純です。カスタム Authorize 属性を作成し、データベースにアクセス許可を問い合わせることができますが、ユーザー アクセス許可に基づいて情報を表示および受け入れるためのコレクションの動作を制限できるようにする必要があります。たとえば、社会保障番号のプロパティを person オブジェクトに追加する場合、特定の人の画面に表示したくありません。ただし、プロパティは実行時に変更できるものであるため、アクセス許可も変更できます。
私の考えが進む限り、私はここにいます...
そのため、ユーザーのアクセス許可に応じて、プロパティのコレクション内のどのオブジェクトを画面にレンダリングしたり、ポストバック時にバインドしたりできるかを判断する方法が必要です。オブジェクトを表示するには、ViewModel オブジェクトにアクセス許可を何らかの方法で含め、プロパティのコレクションで使用されるオブジェクト タイプを対象とした DisplayTemplate でこれらのアクセス許可を照会する以外に、選択肢はあまりないと思います。または、Html.Display() および Html.Editor() の呼び出しに使用されるカスタム ModelBinder を作成し、ModelBinder 内のリストのフィルタリングを調べることもできます。
ただし、ポストバックについても同様の問題があります。それがポストバックされると、Guid と値のみを含むデータのコレクションが返されます。ただし、ユーザーが独自のフィールドをフォームに挿入していないことを確認する必要があります。また、アクションに返されるプロパティについて、ユーザーが適切な権限を持っていることも確認する必要があります。理想的には、このチェックを Model Binding に統合し、可能であれば MetaData から入力された情報の一部を再利用して、渡されたデータを単純に無視して、ユーザーが変更する権利を持たないようにしたいと考えています。これに失敗した場合は、ポストバックを処理するアクションの開始時に行われる IsValid チェックで、ユーザーが設定しようとしているすべての属性にアクセスできることを確認します。
次に、データベース内の情報に基づいて各プロパティの Html.Display() および Html.Editor() への呼び出しで使用する MetaData の動的な構築があります。データ注釈。
問題は、ModelBinders、ModelMetaDataProviders、ModelValidationProviders などのデフォルトの実装をオーバーライドすることに関して、MVC の内部に精通していないことです。
私が説明していることを達成するために考えられる最善の方法についていくつかの提案を提供できますか、またはこの例をカバーする他の記事を知っている場合は、それらを見てみたいと思います.Googleであまり運がありません.これまでのところ、この特定の主題について。
編集:私がしたことの詳細については、以下の私の回答を参照してください
編集: メタデータ プロバイダーが動作するようになりました。独自のクラスを実装し、ModelMetadataProvider から継承する必要がありました。
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
ModelMetadata metadata;
if (containerType == typeof(PseudoObjectAttributeViewModel))
{
switch (propertyName)
{
case "StringValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(string), propertyName);
break;
case "DateValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(DateTime?), propertyName);
break;
case "DoubleValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(double?), propertyName);
break;
case "LongValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(long?), propertyName);
break;
case "BooleanValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(bool?), propertyName);
break;
case "GuidValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(Guid?), propertyName);
break;
default:
return defaultMetadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
break;
}
DataAnnotationsModelMetadata daMetadata = (DataAnnotationsModelMetadata)metadata;
System.Reflection.FieldInfo container = modelAccessor.Target.GetType().GetField("vdi");
AddSupplimentalMetadata(daMetadata, (PseudoObjectAttributeViewModel)((System.Web.Mvc.ViewDataInfo)container.GetValue(modelAccessor.Target)).Container);
}
else
metadata = defaultMetadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
return metadata;
}
最初の部分は一目瞭然で、まず、データの取得元の列名に最も近い .NET 型を渡すことによって GetMetadataForType() でメタデータを設定します。(私のエディター teplate は、このデータを定義するデータ構造で定義されているように、データがどの列にあるかを動的に選択することでこれを支援します)
Html.Editor(Model.PseudoObjectStructure.PseudoObjectControl.DataType)
扱うのは少し奇妙ですが、私が言ったように、それは既存のデータ構造です。
switch ステートメントの後で、おかしくなりました。私が理解しているように、MVC2 では、GetMetadataForProperty()
メソッドは Model 自体をパラメーターとして取得せず、 を使用してプロパティを見つけます。代わりに、MVC がメタデータを必要とするプロパティを指すpropertyName
型の式を渡します。Func<object>
ルート モデルで別のプロパティを使用して構造の詳細を決定する必要があったため、これには問題がありました。ここで、リフレクションを使用してモデルを取得できるという別の解決策を見つけましたが、それにはリフレクションが必要です。私が望んでいたものではありませんが、うまくいきます。モデルを取得したら、これまでのメタデータとモデルを作成したメソッドに渡し、MVC がそこから使用AddSupplimentalMetadata()
するオブジェクトの残りのプロパティを設定します。DataAnnotationsModelMetadata
次に、ユーザーのアクセス許可に応じて、特定のプロパティをレンダリングするかレンダリングしないかを動的に選択する方法を見つけ出す必要があります。私がしなければならないことは、モデルをビューに渡す前に、LINQ などを使用して、プロパティのリストをフィルター処理することだと思います。ビジネス ロジックを Display/EditorTemplate に入れるという考えは好きではありません。変更を保存するには、検証システムを調べて、ユーザーが情報を渡そうとしているプロパティを検証するためにそれにフックできるかどうかを確認する必要があります。