これが理想的な解決策であるかどうかはわかりませんが、IModelBinder
の一般的な実装を実装して反射魔法を使って解決しました。この実装の規定では、クエリ文字列のJavaScriptからの要素がcamelCaseにあり、C#のクラスが標準スタイルごとにPascalCaseにあると仮定しています。さらに、それはpublic [set-able]プロパティでのみ機能します。
が
public class BracketedQueryStringModelBinder<T> : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite);
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (var p in properties)
{
if (!IsNullable(p.PropertyType))
{
object val = TryGetValueType(p.PropertyType, bindingContext, p.Name);
if (val != null)
{
values.Add(p.Name, val);
}
}
else
{
object val = GetRefernceType(p.PropertyType, bindingContext, p.Name);
values.Add(p.Name, val);
}
}
if (values.Any())
{
object boundModel = Activator.CreateInstance<T>();
foreach (var p in properties.Where(i => values.ContainsKey(i.Name)))
{
p.SetValue(boundModel, values[p.Name]);
}
return boundModel;
}
return null;
}
private static bool IsNullable(Type t)
{
if (t == null)
throw new ArgumentNullException("t");
if (!t.IsValueType)
return true;
return Nullable.GetUnderlyingType(t) != null;
}
private static object TryGetValueType(Type type, ModelBindingContext ctx, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
key = ConvertToPascalCase(key);
ValueProviderResult result = ctx.ValueProvider.GetValue(string.Concat(ctx.ModelName, "[", key, "]"));
if (result == null && ctx.FallbackToEmptyPrefix)
result = ctx.ValueProvider.GetValue(key);
if (result == null)
return null;
try
{
object returnVal = result.ConvertTo(type);
ctx.ModelState.SetModelValue(key, result);
return returnVal;
}
catch (Exception ex)
{
ctx.ModelState.AddModelError(ctx.ModelName, ex);
return null;
}
}
private static object GetRefernceType(Type type, ModelBindingContext ctx, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
key = ConvertToPascalCase(key);
ValueProviderResult result = ctx.ValueProvider.GetValue(string.Concat(ctx.ModelName, "[", key, "]"));
if (result == null && ctx.FallbackToEmptyPrefix)
result = ctx.ValueProvider.GetValue(key);
if (result == null)
return null;
try
{
object returnVal = result.ConvertTo(type);
ctx.ModelState.SetModelValue(key, result);
return returnVal;
}
catch (Exception ex)
{
ctx.ModelState.AddModelError(ctx.ModelName, ex);
return null;
}
}
private static string ConvertToPascalCase(string str)
{
char firstChar = str[0];
if (char.IsUpper(firstChar))
return char.ToLower(firstChar) + str.Substring(1);
return str;
}
}
次に、あなたのコントローラであなたはこのようにそれを使用することができます:ここに私の実装では、下にあります
[HttpGet]
public ActionResult myAction([ModelBinder(typeof(BracketedQueryStringModelBinder<MyClass>))] MyClass mc = null)
{
...
}
この方法の主な没落は、あなたは、ドット表記のクエリ文字列を取得しない場合は、この結合があります標準モデルバインダーに戻ることはないので失敗します。