【问题标题】:Custom Model Binder In Asp.Net Core for Sub ClassesAsp.Net Core 中用于子类的自定义模型绑定器
【发布时间】:2017-09-26 13:08:05
【问题描述】:

我有一个场景,我有一个特定的基类,我们称之为“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)
    {
    }
}

现在我想做的是我想创建一个模型绑定器,它将与 Web API 方法一起使用,模型绑定器将与“PagingCriteriaBase”的所有子类一起使用,这个模型的目的binder 是根据来自 ajax 请求的数据设置一些属性,我尝试执行以下操作:

  1. 我创建了一个实现“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;
    }
    }
    
  2. 我创建了一个 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;
    }
    }
    
  3. 我使用以下方法注册了模型绑定器:

    services.AddMvc(op => op.ModelBinderProviders.Insert(0, new PagingEntityBinderProvider())) ;
    
  4. 在我的 Web API 方法中,我执行了以下操作:

     public IActionResult GetAll([ModelBinder(typeof(PagingModelBinder))]UserCriteria crit)
    {
     //Code goes here
    }
    

当我使用上面的模型绑定器时,我发现一旦代码到达 Web API 方法,类中的值没有任何改变,例如“PageNo”属性保持为 1,所以我需要做的是让模型绑定器为子类对象设置所有相关属性,无论类本身的类型如何,最后一旦代码到达Web API方法,模型将正确设置所有属性,你能指点我吗我需要在我的代码中更改什么来处理这个问题?

请注意,我使用的是 Asp.Net Core 2.0

【问题讨论】:

    标签: c# asp.net-core model-binding asp.net-core-2.0 custom-model-binder


    【解决方案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;
        }
    }
    

    并从 Web API 方法中移除 ModelBinder 属性

    public IActionResult GetAll(UserCriteria crit)
    {
        //Code goes here
    }
    

    【讨论】:

    • 感谢@laksmono,起初“PropertyInfo”的 SetValue 方法给了我一个错误,例如由于 String 和 Int32 之间的类型不同,但我将代码更改为: pi.SetValue(model, Convert.ChangeType(v.RawValue,pi.PropertyType)),它按预期工作。
    猜你喜欢
    • 1970-01-01
    • 2017-02-09
    • 2017-12-05
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 2018-12-11
    • 1970-01-01
    相关资源
    最近更新 更多