Sub 类 的 Asp.Net 核心中的自定义模型绑定器
Custom Model Binder In Asp.Net Core for Sub Classes
我有一个场景,我有一定的基础class我们称之为"PagingCriteriaBase"
public class PagingCriteriaBase : CriteriaBase
{
public Int32 CountOfItemsPerPage { get; set; }
public SortOrder SortingOrder { get; set; }
public String SortBy { get; set; }
public Int32 PageNo { get; set; }
public PagingCriteriaBase(Int32 pageNo,Int32 countOfItemsPerPage, SortOrder sortingOrder, String sortBy,Int32 draw)
{
this.PageNo = pageNo>0?pageNo:1;
this.CountOfItemsPerPage = countOfItemsPerPage>0?countOfItemsPerPage:10;
this.SortBy = sortBy;
this.SortingOrder = sortingOrder;
this.Draw = draw;
}
}
然后我还有其他 class 继承自 "PagingCriteriaBase",例如
public class UserCriteria:PagingCriteriaBase
{
public String Email { get; set; }
public String DisplayName { get; set; }
public UserCriteria():base(1,0,SortOrder.Asc,"",1)
{
}
public UserCriteria(Int32 pageNo,Int32 countOfItemsPerPage, SortOrder sortingOrder, String sortBy, Int32 draw)
:base(pageNo, countOfItemsPerPage,sortingOrder,sortBy,draw)
{
}
}
现在我想做的是创建一个模型绑定器,它将与 Web API 方法一起使用,模型绑定器将与所有子[=57= "PagingCriteriaBase" 的 ]es,此模型绑定器的目的是根据来自 ajax 请求的数据设置一些属性,我尝试执行以下操作:
我创建了一个 class 实现 "IModelBinder" 如下:
public class PagingModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (!bindingContext.ModelType.IsSubclassOf(typeof(PagingCriteriaBase)))
{
return Task.FromResult(false);
}
String startModelName = "start";
String lengthModelName = "length";
var startResult = bindingContext.ValueProvider.GetValue(startModelName);
var lengthResult = bindingContext.ValueProvider.GetValue(lengthModelName);
Int32 start, length;
if (!Int32.TryParse(startResult.FirstValue, out start))
{
start = 0;
}
if (!Int32.TryParse(lengthResult.FirstValue, out length))
{
length = SystemProp.PAGE_SIZE;
}
else
{
length = 20;
}
var model = Activator.CreateInstance(bindingContext.ModelType);
Int32 pageNo = (int)Math.Ceiling((decimal)start / length);
bindingContext.ModelState.SetModelValue("PageNo", new ValueProviderResult(pageNo.ToString()));
bindingContext.ModelState.SetModelValue("CountOfItemsPerPage", new ValueProviderResult(length.ToString()));
bindingContext.Model = model;
var mProv = (IModelMetadataProvider)bindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
我创建了一个ModelBinderProvider如下:
public class PagingEntityBinderProvider:IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(PagingCriteriaBase))
{
return new BinderTypeModelBinder(typeof(PagingModelBinder));
}
return null;
}
}
我使用以下方式注册了模型绑定器:
services.AddMvc(op => op.ModelBinderProviders.Insert(0, new PagingEntityBinderProvider())) ;
在我的 Web API 方法中,我执行了以下操作:
public IActionResult GetAll([ModelBinder(typeof(PagingModelBinder))]UserCriteria crit)
{
//Code goes here
}
当我使用上面的模型绑定器时,我发现一旦代码到达 Web API 方法,class 中的值没有任何改变,例如 "PageNo" 属性 保持 1,所以我需要做的是让模型联编程序为 subclass 对象设置所有相关的 属性 而不管 class 的类型本身,最后一旦代码到达 Web API 方法,模型将正确设置所有属性,你能指出我需要在我的代码中更改什么来处理这个吗?
请注意我使用的是Asp.Net Core 2.0
我猜那是因为你没有设置任何模型 属性,只是实例化了它。
我认为我们可以通过反射遍历子类的所有属性,并根据模型状态值设置值(假设 属性 名称与模型状态键相同)
public Task BindModelAsync(ModelBindingContext bindingContext)
{
...
bindingContext.ModelState.SetModelValue("PageNo", new ValueProviderResult(pageNo.ToString()));
bindingContext.ModelState.SetModelValue("CountOfItemsPerPage", new ValueProviderResult(length.ToString()));
ModelStateEntry v;
foreach (PropertyInfo pi in bindingContext.ModelType.GetProperties())
{
if (bindingContext.ModelState.TryGetValue(pi.Name, out v))
{
try
{
pi.SetValue(model, v.RawValue);
}
catch
{
}
}
}
bindingContext.Model = model;
...
}
并更改您的 PagingEntityBinderProvider
public class PagingEntityBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (typeof(PagingCriteriaBase).IsAssignableFrom(context.Metadata.ModelType))
{
return new BinderTypeModelBinder(typeof(PagingModelBinder));
}
return null;
}
}
并从 Web API 方法中删除 ModelBinder 属性
public IActionResult GetAll(UserCriteria crit)
{
//Code goes here
}
我有一个场景,我有一定的基础class我们称之为"PagingCriteriaBase"
public class PagingCriteriaBase : CriteriaBase
{
public Int32 CountOfItemsPerPage { get; set; }
public SortOrder SortingOrder { get; set; }
public String SortBy { get; set; }
public Int32 PageNo { get; set; }
public PagingCriteriaBase(Int32 pageNo,Int32 countOfItemsPerPage, SortOrder sortingOrder, String sortBy,Int32 draw)
{
this.PageNo = pageNo>0?pageNo:1;
this.CountOfItemsPerPage = countOfItemsPerPage>0?countOfItemsPerPage:10;
this.SortBy = sortBy;
this.SortingOrder = sortingOrder;
this.Draw = draw;
}
}
然后我还有其他 class 继承自 "PagingCriteriaBase",例如
public class UserCriteria:PagingCriteriaBase
{
public String Email { get; set; }
public String DisplayName { get; set; }
public UserCriteria():base(1,0,SortOrder.Asc,"",1)
{
}
public UserCriteria(Int32 pageNo,Int32 countOfItemsPerPage, SortOrder sortingOrder, String sortBy, Int32 draw)
:base(pageNo, countOfItemsPerPage,sortingOrder,sortBy,draw)
{
}
}
现在我想做的是创建一个模型绑定器,它将与 Web API 方法一起使用,模型绑定器将与所有子[=57= "PagingCriteriaBase" 的 ]es,此模型绑定器的目的是根据来自 ajax 请求的数据设置一些属性,我尝试执行以下操作:
我创建了一个 class 实现 "IModelBinder" 如下:
public class PagingModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (!bindingContext.ModelType.IsSubclassOf(typeof(PagingCriteriaBase))) { return Task.FromResult(false); } String startModelName = "start"; String lengthModelName = "length"; var startResult = bindingContext.ValueProvider.GetValue(startModelName); var lengthResult = bindingContext.ValueProvider.GetValue(lengthModelName); Int32 start, length; if (!Int32.TryParse(startResult.FirstValue, out start)) { start = 0; } if (!Int32.TryParse(lengthResult.FirstValue, out length)) { length = SystemProp.PAGE_SIZE; } else { length = 20; } var model = Activator.CreateInstance(bindingContext.ModelType); Int32 pageNo = (int)Math.Ceiling((decimal)start / length); bindingContext.ModelState.SetModelValue("PageNo", new ValueProviderResult(pageNo.ToString())); bindingContext.ModelState.SetModelValue("CountOfItemsPerPage", new ValueProviderResult(length.ToString())); bindingContext.Model = model; var mProv = (IModelMetadataProvider)bindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider)); bindingContext.Result = ModelBindingResult.Success(model); return Task.CompletedTask; } }
我创建了一个ModelBinderProvider如下:
public class PagingEntityBinderProvider:IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.ModelType == typeof(PagingCriteriaBase)) { return new BinderTypeModelBinder(typeof(PagingModelBinder)); } return null; } }
我使用以下方式注册了模型绑定器:
services.AddMvc(op => op.ModelBinderProviders.Insert(0, new PagingEntityBinderProvider())) ;
在我的 Web API 方法中,我执行了以下操作:
public IActionResult GetAll([ModelBinder(typeof(PagingModelBinder))]UserCriteria crit) { //Code goes here }
当我使用上面的模型绑定器时,我发现一旦代码到达 Web API 方法,class 中的值没有任何改变,例如 "PageNo" 属性 保持 1,所以我需要做的是让模型联编程序为 subclass 对象设置所有相关的 属性 而不管 class 的类型本身,最后一旦代码到达 Web API 方法,模型将正确设置所有属性,你能指出我需要在我的代码中更改什么来处理这个吗?
请注意我使用的是Asp.Net Core 2.0
我猜那是因为你没有设置任何模型 属性,只是实例化了它。
我认为我们可以通过反射遍历子类的所有属性,并根据模型状态值设置值(假设 属性 名称与模型状态键相同)
public Task BindModelAsync(ModelBindingContext bindingContext)
{
...
bindingContext.ModelState.SetModelValue("PageNo", new ValueProviderResult(pageNo.ToString()));
bindingContext.ModelState.SetModelValue("CountOfItemsPerPage", new ValueProviderResult(length.ToString()));
ModelStateEntry v;
foreach (PropertyInfo pi in bindingContext.ModelType.GetProperties())
{
if (bindingContext.ModelState.TryGetValue(pi.Name, out v))
{
try
{
pi.SetValue(model, v.RawValue);
}
catch
{
}
}
}
bindingContext.Model = model;
...
}
并更改您的 PagingEntityBinderProvider
public class PagingEntityBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (typeof(PagingCriteriaBase).IsAssignableFrom(context.Metadata.ModelType))
{
return new BinderTypeModelBinder(typeof(PagingModelBinder));
}
return null;
}
}
并从 Web API 方法中删除 ModelBinder 属性
public IActionResult GetAll(UserCriteria crit)
{
//Code goes here
}