在 ASP.NET Core 中使用反射的自定义模型绑定
Custom model binding using reflection in ASP.NET Core
我有以下型号
public class MyModel
{
public string Name {get;set;}
public int? Age {get;set;}
public string City {get;set;}
public decimal? Salary {get;set;}
public JObject ExtraFields {get;set;}
}
我正在尝试实施自定义模型绑定器。如果提交的表单具有与模型的 属性 匹配的 key
,则设置模型的 属性 值,否则将键和值添加到 ExtraFields
。
注意 ExtraFields
是 JObject
public class MyModelBinder: IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
MyModel model = new MyModel()
{
ExtraFields = new JObject()
};
var form = bindingContext.HttpContext.Request.Form;
var properties = typeof(MyModel).GetProperties();
foreach (var key in form.Keys)
{
var p = properties.FirstOrDefault(x => x.Name == key);
var val = form[key];
if (p != null)
{
p.SetValue(model, val); // throws exception
}
else
{
var v = StringValues.IsNullOrEmpty(val) ? null : val.ToString();
model.ExtraFields.Add(key, v);
}
}
bindingContext.Model = model;
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
问题
设置模型值时出现异常
Object of type 'Microsoft.Extensions.Primitives.StringValues' cannot be converted to type 'System.String
如果可能,我想避免检查模型目标的类型 属性,然后将值转换为目标类型。这应该隐式发生。
基本上,对于所有匹配的键调用 ASP.NET 的默认绑定器,并为所有剩余的键添加值 ExtraFields
Object of type 'Microsoft.Extensions.Primitives.StringValues' cannot
be converted to type 'System.String
val
是类型 Microsoft.Extensions.Primitives.StringValues 不能直接将其值分配给模型字段。
首先可以直接通过val.ToString()
.
得到对应值的字符串形式
由于每个字段的类型不同,您可以创建自定义Convert.ChangeType方法到动态转换类型通过传递 val.ToString()
和 p.PropertyType
.
更多细节,参考这个demo:
public class MyModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
MyModel model = new MyModel()
{
ExtraFields = new JObject()
};
var form = bindingContext.HttpContext.Request.Form;
var properties = typeof(MyModel).GetProperties();
foreach (var key in form.Keys)
{
var p = properties.FirstOrDefault(x => x.Name == key);
var val = form[key];
if (p != null)
{
// call custom method ChangeType and pass two parameters.
p.SetValue(model, ChangeType(val.ToString(),p.PropertyType));
}
else
{
var v = StringValues.IsNullOrEmpty(val) ? null : val.ToString();
model.ExtraFields.Add(key, v);
}
}
bindingContext.Model = model;
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
// custom method
public static object ChangeType(object value, Type conversion)
{
var t = conversion;
if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
t = Nullable.GetUnderlyingType(t);
}
return Convert.ChangeType(value, t);
}
}
更新
@LP13 提供了更简单的解决方案:
TypeConverter typeConverter = TypeDescriptor.GetConverter(p.PropertyType);
object propValue = typeConverter.ConvertFromString(val); p.PropertyType));
p.SetValue(model, propValue);
这是我的测试结果:
我有以下型号
public class MyModel
{
public string Name {get;set;}
public int? Age {get;set;}
public string City {get;set;}
public decimal? Salary {get;set;}
public JObject ExtraFields {get;set;}
}
我正在尝试实施自定义模型绑定器。如果提交的表单具有与模型的 属性 匹配的 key
,则设置模型的 属性 值,否则将键和值添加到 ExtraFields
。
注意 ExtraFields
是 JObject
public class MyModelBinder: IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
MyModel model = new MyModel()
{
ExtraFields = new JObject()
};
var form = bindingContext.HttpContext.Request.Form;
var properties = typeof(MyModel).GetProperties();
foreach (var key in form.Keys)
{
var p = properties.FirstOrDefault(x => x.Name == key);
var val = form[key];
if (p != null)
{
p.SetValue(model, val); // throws exception
}
else
{
var v = StringValues.IsNullOrEmpty(val) ? null : val.ToString();
model.ExtraFields.Add(key, v);
}
}
bindingContext.Model = model;
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
问题
设置模型值时出现异常
Object of type 'Microsoft.Extensions.Primitives.StringValues' cannot be converted to type 'System.String
如果可能,我想避免检查模型目标的类型 属性,然后将值转换为目标类型。这应该隐式发生。
基本上,对于所有匹配的键调用 ASP.NET 的默认绑定器,并为所有剩余的键添加值 ExtraFields
Object of type 'Microsoft.Extensions.Primitives.StringValues' cannot be converted to type 'System.String
val
是类型 Microsoft.Extensions.Primitives.StringValues 不能直接将其值分配给模型字段。
首先可以直接通过val.ToString()
.
由于每个字段的类型不同,您可以创建自定义Convert.ChangeType方法到动态转换类型通过传递 val.ToString()
和 p.PropertyType
.
更多细节,参考这个demo:
public class MyModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
MyModel model = new MyModel()
{
ExtraFields = new JObject()
};
var form = bindingContext.HttpContext.Request.Form;
var properties = typeof(MyModel).GetProperties();
foreach (var key in form.Keys)
{
var p = properties.FirstOrDefault(x => x.Name == key);
var val = form[key];
if (p != null)
{
// call custom method ChangeType and pass two parameters.
p.SetValue(model, ChangeType(val.ToString(),p.PropertyType));
}
else
{
var v = StringValues.IsNullOrEmpty(val) ? null : val.ToString();
model.ExtraFields.Add(key, v);
}
}
bindingContext.Model = model;
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
// custom method
public static object ChangeType(object value, Type conversion)
{
var t = conversion;
if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
t = Nullable.GetUnderlyingType(t);
}
return Convert.ChangeType(value, t);
}
}
更新
@LP13 提供了更简单的解决方案:
TypeConverter typeConverter = TypeDescriptor.GetConverter(p.PropertyType);
object propValue = typeConverter.ConvertFromString(val); p.PropertyType));
p.SetValue(model, propValue);
这是我的测试结果: