私のモデルクラスには、「保護された内部セット」である複数のプロパティがあります。これらのプロパティが作成されると、変更できなくなります。作成時にこれらのプロパティを設定できるようにするモデル バインダーを作成するのに苦労しています。これらのプロパティを 1 回だけ設定できるようにするための最善の方法は何ですか?
3 に答える
カスタムモデルバインダーを使用してこれを行いました。プライベート セッターがあるだけでなく、MVC アクションへの引数がインターフェイスであり、パラメーターのないコンストラクター (trifecta!) がありませんでした。デフォルトのモデルバインダーではどれも機能しません。
あなたの場合、あなたのタイプのカスタム モデル バインダーを作成するだけです。クラスの新しいインスタンスをデフォルト値で作成します。次に、リフレクションを使用してクラスのプロパティを設定します。リフレクション メソッドは、プロパティのアクセシビリティを気にしません。このようなもの:
// assuming your class looks like this
public class MyClass
{
public int MyInt { get; private set; }
public string MyString { get; private set; }
public MyClass(int myInt, string myString)
{
MyInt = myInt;
MyString = myString;
}
}
// model binder is like this
public class MyModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type modelType)
{
// initialize with default values, they will be overwritten
MyClass myClass = new MyClass(default(int), default(string));
// get the reflection info on the int property
PropertyInfo intProperty = typeof(MyClass).GetProperty("MyInt");
// get the int value from the request
int myIntValue = int.Parse(bindingContext.ValueProvider
.GetValue(intProperty.Name).AttemptedValue);
// btw, attempted value is a string
// set the property value, SetValue ignores accessibility modifiers
intProperty.SetValue(myClass, myIntValue, null);
// do the same stuff for MyString property
PropertyInfo stringProperty = typeof(MyClass).GetProperty("MyString");
string myStringValue = bindingContext.ValueProvider
.GetValue(stringProperty.Name).AttemptedValue;
stringProperty.SetValue(myClass, myStringValue, null);
return myClass;
}
}
// Your controller action is like this
public ActionResult MyAction([ModelBinder(typeof(MyModelBinder))]
MyClass myClass)
{
...
}
リフレクションを使用して設定せずに、適切な値をコンストラクターに渡すことができたと思います。
...
int myIntValue = int.Parse(bindingContext.ValueProvider
.GetValue(intProperty.Name).AttemptedValue);
string myStringValue = bindingContext.ValueProvider
.GetValue(stringProperty.Name).AttemptedValue;
MyClass myClass = new MyClass(myIntValue, myStringValue);
...
しかし、さらに多くのことを行う必要がある場合は、リフレクションを使用すると、アクセス修飾子の制限を回避できます (もちろん、インフラストラクチャ上の理由からです! :D)。私の場合、インターフェイスを実装できるすべてのクラスにモデル バインダーを記述したくなかったので、さらに多くのリフレクションを使用してクラスを照合し、コンストラクターを見つけ、それにデフォルト値を渡し、次に各プロパティをループします。クラスを取得し、リクエスト値から設定します。メッセージに基づいてアクションを実行する前に、メッセージを検証する別の方法があります。
残念ながら、これは自動的に実行できるものではありません。型ごとに一意のモデル バインダーを作成し、コンストラクター パラメーターを使用してオブジェクトを作成する必要があります。
まだ設定されていない場合にのみ、プライベート フィールドを設定する set メソッドを保護できますか?
public string PropertyName
{
get
{
return this.privateField;
}
protected set
{
if (this.privateField == null)
{
this.privateField = value;
}
}
}
private string privateField;