dot net core custom model binding for generic type

2019-08-13 00:20发布

I am building a simple search, sort, page feature. I have attached the code below. Below are the usecases:

  1. My goal is to pass the "current filters" via each request to persist them particularly while sorting and paging.

  2. Instead of polluting my action method with many (if not too many) parameters, I am thinking to use a generic type parameter that holds the current filters.

  3. I need a custom model binder that can be able to achieve this.

Could someone please post an example implementation?

PS: I am also exploring alternatives as opposed to passing back and forth the complex objects. But i would need to take this route as a last resort and i could not find a good example of custom model binding generic type parameters. Any pointers to such examples can also help. Thanks!.

public async Task<IActionResult> Index(SearchSortPage<ProductSearchParamsVm> currentFilters, string sortField, int? page)
{
    var currentSort = currentFilters.Sort;
    // pass the current sort and sortField to determine the new sort & direction
    currentFilters.Sort = SortUtility.DetermineSortAndDirection(sortField, currentSort);
    currentFilters.Page = page ?? 1;

    ViewData["CurrentFilters"] = currentFilters;

    var bm = await ProductsProcessor.GetPaginatedAsync(currentFilters);

    var vm = AutoMapper.Map<PaginatedResult<ProductBm>, PaginatedResult<ProductVm>>(bm);

    return View(vm);
}

public class SearchSortPage<T> where T : class
{
    public T Search { get; set; }
    public Sort Sort { get; set; }
    public Nullable<int> Page { get; set; }
}

public class Sort
{
    public string Field { get; set; }
    public string Direction { get; set; }
}

public class ProductSearchParamsVm
{
    public string ProductTitle { get; set; }
    public string ProductCategory { get; set; }
    public Nullable<DateTime> DateSent { get; set; }
}

1条回答
时光不老,我们不散
2楼-- · 2019-08-13 00:52

First create the Model Binder which should be implementing the interface IModelBinder

SearchSortPageModelBinder.cs

public class SearchSortPageModelBinder<T> : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }   

        SearchSortPage<T> ssp = new SearchSortPage<T>();

        //TODO: Setup the SearchSortPage<T> model 

        bindingContext.Result = ModelBindingResult.Success(ssp);

        return TaskCache.CompletedTask;
    }
}

And then create the Model Binder Provider which should be implementing the interface IModelBinderProvider

SearchSortPageModelBinderProvider.cs

public class SearchSortPageModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType.GetTypeInfo().IsGenericType && 
            context.Metadata.ModelType.GetGenericTypeDefinition() == typeof(SearchSortPage<>))
        {
            Type[] types = context.Metadata.ModelType.GetGenericArguments();
            Type o = typeof(SearchSortPageModelBinder<>).MakeGenericType(types);

            return (IModelBinder)Activator.CreateInstance(o);
        }

        return null;
    }
}

And the last thing is register the Model Binder Provider, it should be done in your Startup.cs

public void ConfigureServices(IServiceCollection services)
{
        .
        .

        services.AddMvc(options=>
        {
            options.ModelBinderProviders.Insert(0, new SearchSortPageModelBinderProvider());
        });
        .
        .
}
查看更多
登录 后发表回答