Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
126 views
in Technique[技术] by (71.8m points)

c# - Issue with Binding a List of complex type object - Dotnet Core 2.1 Razor Pages

I'm using Razor pages for my project on dotnet core 2.1, and the application doesn't seem to bind my properties correctly, the simple types (int and string types) binds correctly but not the list of complex types, is there a work around for this?

my page handler looks like this:

public async Task<IActionResult> OnGetDTResponseAsync(DataTableOptions options) {// Some Code}

When I step through with my debugger all simple type properties for "DataTableOptions options" are well populated but the complex type returns null.

my model looks like this :

public class DataTableOptions
{
    public string Draw { get; set; }
    public int Start { get; set; }
    public int Length { get; set; }
    public List<DataTableColumnOrder> Order { get; set; }
    public List<DataTableColumn> Columns { get; set; }
    public DataTableColumnSearch Search { get; set; }
    public List<string> Params { get; set; }

    public DataTableOptions() { }

    public class DataTableColumn
    {
        public string Data { get; set; }
        public string Name { get; set; }
        public bool Searchable { get; set; }
        public bool Orderable { get; set; }
        public DataTableColumnSearch Search { get; set; }

        public DataTableColumn() { }
    }

    public class DataTableColumnSearch
    {
        public string Value { get; set; }
        public bool Regex { get; set; }

        public DataTableColumnSearch() { }
    }

    public class DataTableColumnOrder
    {
        public int Column { get; set; }
        public string Dir { get; set; }

        public DataTableColumnOrder() { }
    }
}

While trying to solve this, I tried using

public async Task<IActionResult> OnGetDTResponseAsync(List<Dictionary<string, string>> columns)

in my page handler in place of the columns property of DataTableOptions so i could manually bind the properties to my class: I got a full list of my columns with it's properties binded to it, except for the DataTableColumn's DataTableColumnSearch property which is also a complex type that came out as null.

public async Task<IActionResult> OnGetDTResponseAsync(List<Dictionary<string, object>> columns) 

doesn't work either.

This is what the request looks like in fiddler:

GET /CMS/Index?handler=DTResponse&draw=1&columns%5B0%5D%5Bdata%5D=id&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=false&columns%5B0%5D%5Borderable%5D=false&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=name&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=webPage.name&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=value&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=true&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B4%5D%5Bdata%5D=contentType.name&columns%5B4%5D%5Bname%5D=&columns%5B4%5D%5Bsearchable%5D=true&columns%5B4%5D%5Borderable%5D=true&columns%5B4%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B4%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B5%5D%5Bdata%5D=&columns%5B5%5D%5Bname%5D=&columns%5B5%5D%5Bsearchable%5D=false&columns%5B5%5D%5Borderable%5D=false&columns%5B5%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B5%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=2&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1545122652329 HTTP/1.1

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I had to build a custom model binding class to handle this scenario. For some reason a Collection List of a complex object that has another complex object as part of it's properties can't be automatically binded correctly in core 2.1 -Razor pages.

See my solution below:

using Microsoft.AspNetCore.Mvc.ModelBinding;
using RestaurantDataModel.Data.Requests;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ExampleDataModel.Data
{
public class CustomDataTableEntityBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var allValues = bindingContext.HttpContext.Request.Query;
        DataTableOptions DTOs = new DataTableOptions {
            Draw = allValues.FirstOrDefault(a => a.Key == "draw").Value,
            Start = Convert.ToInt32(allValues.FirstOrDefault(a => a.Key == "start").Value),
            Length = Convert.ToInt32(allValues.FirstOrDefault(a => a.Key == "length").Value)
        };

        if (allValues.Any(a => a.Key.Length > 9 && a.Key.Substring(0, 9).Contains("columns")))
        {
            var myListIndex = 0;
            var myListIndexComparer = 0;
            var allColumns = allValues.Where(a => a.Key.Length > 9 && a.Key.Substring(0, 9).Contains("columns")).ToList();
            DataTableColumn DTC = new DataTableColumn();
            DataTableColumnSearch DTCS = new DataTableColumnSearch();
            DTOs.columns = new List<DataTableColumn>();
            foreach (var column in allColumns)
            {
                var perColumnArray = column.Key.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
                var rawIndex = perColumnArray[1];

                if (!int.TryParse(rawIndex, out myListIndex))
                {
                    return Task.CompletedTask;
                }

                if (myListIndexComparer != myListIndex)
                {
                    DTC.search = DTCS;
                    DTOs.columns.Add(DTC);
                    DTC = new DataTableColumn();
                    DTCS = new DataTableColumnSearch();
                }
                myListIndexComparer = myListIndex;
                switch (perColumnArray[2])
                {
                    case "data":
                        DTC.data = column.Value;
                        break;
                    case "name":
                        DTC.name = column.Value;
                        break;
                    case "searchable":
                        DTC.searchable = String.IsNullOrWhiteSpace(column.Value) ? false : Convert.ToBoolean(column.Value);
                        break;
                    case "orderable":
                        DTC.orderable = String.IsNullOrWhiteSpace(column.Value) ? false : Convert.ToBoolean(column.Value);
                        break;
                    case "search":
                        if (perColumnArray[3] == "regex")
                        {
                            DTCS.regex = String.IsNullOrWhiteSpace(column.Value) ? false : Convert.ToBoolean(column.Value);
                        }
                        if (perColumnArray[3] == "value")
                        {
                            DTCS.value = column.Value;
                        }
                        break;
                }

                if(allColumns.IndexOf(column) == allColumns.IndexOf(allColumns.Last()))
                {
                    DTC.search = DTCS;
                    DTOs.columns.Add(DTC);
                }
            }
        }
        if (allValues.Any(a => a.Key.Length > 7 && a.Key.Substring(0, 7).Contains("order")))
        {
            var myListIndex = 0;
            var myListIndexComparer = 0;
            var allOrders = allValues.Where(a => a.Key.Length > 7 && a.Key.Substring(0, 7).Contains("order")).ToList();
            DataTableColumnOrder DTCO = new DataTableColumnOrder();
            DTOs.order = new List<DataTableColumnOrder>();
            foreach (var order in allOrders)
            {
                var perColumnArray = order.Key.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
                var rawIndex = perColumnArray[1];

                if (!int.TryParse(rawIndex, out myListIndex))
                {
                    return Task.CompletedTask;
                }

                if (myListIndexComparer != myListIndex)
                {
                    DTOs.order.Add(DTCO);
                    DTCO = new DataTableColumnOrder();
                }
                myListIndexComparer = myListIndex;
                switch (perColumnArray[2])
                {
                    case "column":
                        DTCO.Column = Convert.ToInt32(order.Value);
                        break;
                    case "dir":
                        DTCO.Dir = order.Value;
                        break;
                }
                if(allOrders.IndexOf(order) == allOrders.IndexOf(allOrders.Last()))
                {
                    DTOs.order.Add(DTCO);
                }
            }
        }
        if (allValues.Any(a => a.Key.Length > 7 && a.Key.Substring(0, 8).Contains("search")))
        {
            var allSearches = allValues.Where(a => a.Key.Length > 8 && a.Key.Substring(0, 8).Contains("search")).ToList();
            DataTableColumnSearch DTCS = new DataTableColumnSearch();
            DTOs.search = new DataTableColumnSearch();
            foreach (var search in allSearches)
            {
                var perColumnArray = search.Key.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);

                switch (perColumnArray[1])
                {
                    case "value":
                        DTCS.value = search.Value;
                        break;
                    case "regex":
                        DTCS.regex = String.IsNullOrWhiteSpace(search.Value) ? false : Convert.ToBoolean(search.Value);
                        break;
                }
                if(allSearches.IndexOf(search) == allSearches.IndexOf(allSearches.Last()))
                {
                    DTOs.search = DTCS;
                }
            }
        }

        bindingContext.Result = ModelBindingResult.Success(DTOs);
        return Task.CompletedTask;
    }
}

}

And then I added this to the top of my model class:

[ModelBinder(BinderType = typeof(CustomDataTableEntityBinder))]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...