Asp.net mvc 如何在MVC6中编写多态模型绑定器和提供程序
在MVC3的上下文中,这个问题以前在SO和其他地方被问过,关于它有一些与ASP.NET核心RC1和RC2相关的细节,但是没有一个例子真正展示了如何在MVC 6中正确地完成它 有以下几类Asp.net mvc 如何在MVC6中编写多态模型绑定器和提供程序,asp.net-mvc,polymorphism,modelbinder,Asp.net Mvc,Polymorphism,Modelbinder,在MVC3的上下文中,这个问题以前在SO和其他地方被问过,关于它有一些与ASP.NET核心RC1和RC2相关的细节,但是没有一个例子真正展示了如何在MVC 6中正确地完成它 有以下几类 public abstract class BankAccountTransactionModel { public long Id { get; set; } public DateTime Date { get; set; } public decimal Amount { get
public abstract class BankAccountTransactionModel {
public long Id { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public readonly string ModelType;
public BankAccountTransactionModel(string modelType) {
this.ModelType = modelType;
}
}
public class BankAccountTransactionModel1 : BankAccountTransactionModel{
public bool IsPending { get; set; }
public BankAccountTransactionModel1():
base(nameof(BankAccountTransactionModel1)) {}
}
public class BankAccountTransactionModel2 : BankAccountTransactionModel{
public bool IsPending { get; set; }
public BankAccountTransactionModel2():
base(nameof(BankAccountTransactionModel2)) {}
}
在我的控制器里,我有这样的东西
[Route(".../api/[controller]")]
public class BankAccountTransactionsController : ApiBaseController
{
[HttpPost]
public IActionResult Post(BankAccountTransactionModel model) {
try {
if (model == null || !ModelState.IsValid) {
// failed to bind the model
return BadRequest(ModelState);
}
this.bankAccountTransactionRepository.SaveTransaction(model);
return this.CreatedAtRoute(ROUTE_NAME_GET_ITEM, new { id = model.Id }, model);
} catch (Exception e) {
this.logger.LogError(LoggingEvents.POST_ITEM, e, string.Empty, null);
return StatusCode(500);
}
}
}
我的客户可能会发布BankAccountTransactionModel1或BankAccountTransactionModel2,我希望使用自定义模型绑定器根据抽象基类BankAccountTransactionModel上定义的属性ModelType中的值来确定要绑定的具体模型
因此,我做了以下工作
1) 编写了一个简单的模型绑定器提供程序,用于检查类型是否为BankAccountTransactionModel。如果是这种情况,则返回BankAccountTransactionModelBinder的实例
public class BankAccountTransactionModelBinderProvider : IModelBinderProvider {
public IModelBinder GetBinder(ModelBinderProviderContext context) {
if (context == null) throw new ArgumentNullException(nameof(context));
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType) {
var type1 = context.Metadata.ModelType;
var type2 = typeof(BankAccountTransactionModel);
// some other code here?
// tried this but not sure what to do with it!
foreach (var property in context.Metadata.Properties) {
propertyBinders.Add(property, context.CreateBinder(property));
}
if (type1 == type2) {
return new BankAccountTransactionModelBinder(propertyBinders);
}
}
return null;
}
}
2) 对BankAccountTransactionModel进行编码
public class BankAccountTransactionModelBinder : IModelBinder {
private readonly IDictionary<ModelMetadata, IModelBinder> _propertyBinders;
public BankAccountTransactionModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders){
this._propertyBinders = propertyBinders;
}
public Task BindModelAsync(ModelBindingContext bindingContext) {
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
// I would like to be able to read the value of the property
// ModelType like this or in some way...
// This does not work and typeValue is...
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
// then once I know whether it is a Model1 or Model2 I would like to
// instantiate one and get the values from the body of the Http
// request into the properties of the instance
var model = Activator.CreateInstance(type);
// read the body of the request in some way and set the
// properties of model
var key = some key?
var result = ModelBindingResult.Success(key, model);
// Job done
return Task.FromResult(result);
}
}
整个事情看起来还可以,因为提供者实际上被调用了,模型生成器也是如此。然而,在模型绑定器的bindmodelsync中编写逻辑似乎没有任何进展
正如代码中的注释所述,我想在我的模型绑定器中做的所有事情都是从http请求的主体中读取,特别是从JSON中读取ModelType的值。然后在此基础上,我想实例化BankAccountTransactionModel1或BankAccountTransactionModel,最后通过读取主体中JSON的值来为这个实例的属性赋值
我知道这仅仅是一个粗略的近似方法,但我非常感谢一些帮助,也许是关于如何做到或已经做到这一点的例子
我在ModelBinder中遇到了以下代码行的示例
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
应该读取值。然而,它在我的模型活页夹中不起作用,typeValue总是如下所示
typeValue
{}
Culture: {}
FirstValue: null
Length: 0
Values: {}
Results View: Expanding the Results View will enumerate the IEnumerable
我也注意到
bindingContext.ValueProvider
Count = 2
[0]: {Microsoft.AspNetCore.Mvc.ModelBinding.RouteValueProvider}
[1]: {Microsoft.AspNetCore.Mvc.ModelBinding.QueryStringValueProvider}
这可能意味着我没有机会从身体上读到任何东西
我是否需要在混合中使用“格式化程序”以获得所需的结果
类似的自定义模型绑定器的参考实现是否已经存在,这样我就可以简单地使用它,或者使用一些简单的mod
多谢各位
bindingContext.ValueProvider
Count = 2
[0]: {Microsoft.AspNetCore.Mvc.ModelBinding.RouteValueProvider}
[1]: {Microsoft.AspNetCore.Mvc.ModelBinding.QueryStringValueProvider}