Пользовательская модель Binder в Asp.Net Core для подклассов
У меня есть сценарий, где у меня есть определенный базовый класс, мы будем называть его "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;
}
}
а затем у меня есть другие классы, которые будут наследоваться от "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)
{
}
}
Теперь я хотел бы создать связыватель модели, который будет использоваться с методами веб-API, и связыватель модели будет использоваться со всеми подклассами "PagingCriteriaBase", цель связующего устройства состоит в том, чтобы установить некоторые свойства в соответствии с данными, поступающими из запросов AJAX, я попытался сделать следующее:
Я создал класс, который реализует "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, ничего из значений в классе не изменяется, например, свойство PageNo остается 1, поэтому мне нужно иметь связыватель модели устанавливает все связанные свойства для объекта подкласса независимо от типа самого класса, и, в конце концов, как только код достигнет метода Web API, в модели все свойства будут установлены правильно, не могли бы вы указать, что мне делать? нужно изменить в моем коде, чтобы справиться с этим?
Обратите внимание, что я использую Asp.Net Core 2.0
1 ответ
Я полагаю, это потому, что вы не установили какое-либо свойство модели, а только создали его экземпляр.
Я думаю, что мы можем пройти через все свойства подкласса с отражением и установить значение на основе значения состояния модели (при условии, что имя свойства совпадает с ключом состояния модели)
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;
}
}
И удалите атрибут ModelBinder из метода веб-API
public IActionResult GetAll(UserCriteria crit)
{
//Code goes here
}