ALMMa / datatables.aspnet

Microsoft AspNet bindings and automatic parsing for jQuery DataTables along with extension methods to help on data queries.
MIT License
303 stars 136 forks source link

WebApi2 Model Binder? #7

Closed mr-t-the-beerdrinker closed 9 years ago

mr-t-the-beerdrinker commented 10 years ago

First thing first: Great Work :)

I've just wanted to ask, if you'll implement WebApi2 Model Binder (It has to be System.Web.Http.ModelBinding.IModelBinder instead of System.Web.Mvc.IModelBinder). I think it's quite quick as i'm lokoing into the code. Anyway, thx for this.

EDIT:

for me, it was enough to implement the System.Web.Http.ModelBinding with your methods, updating just the ResolveNameValueCollection to this:

protected virtual NameValueCollection ResolveNameValueCollection(HttpRequestMessage request)
        {
            if (request.Method == HttpMethod.Get) return HttpUtility.ParseQueryString(request.RequestUri.Query);
            else if (request.Method == HttpMethod.Post)
            {
                var data = request.Content.ReadAsStringAsync().Result;
                return HttpUtility.ParseQueryString(data);
            }
            else throw new ArgumentException(String.Format("The provided HTTP method ({0}) is not a valid method to use with DataTablesBinder. Please, use HTTP GET or POST methods only.", request.Method), "method");
        }
jawa-the-hutt commented 10 years ago

HairyOneMan,

Do I understand that you have successfully converted this project to WebAPI2? If so, is the above code all you changed? Seems to me based on what I'm doing that there are some other things that would need changed as well.

mr-t-the-beerdrinker commented 10 years ago

You're right, I have a mistake above. You have to implement interface System.Web.Http.ModelBinding.IModelBinder instead of System.Web.Mvc.IModelBinder in MVC version. Bind method has different types of arguments - you will have to update those and update the ResolveNameValueCollection with the code above. Then it should work. In you web api controller, use ModelBinder from System.Web.Http.ModelBinding (not that for MVC). Mapping additional properties works as for MVC version of binder. Hope it will work for you :)

jawa-the-hutt commented 10 years ago

Great. Thanks for the feedback. Got this working mostly. I'm pulling data through the model. What I don't have working as of yet is Global Search, Column Ordering and Column Filtering. Mostly because I'm not clear from the README where the example code should be used.

Now, that may be becuase my client side is pure HTML/JS and not a View. So my question now is, did you get Search/Ordering/Filtering to work and if so, how?

Thanks again.

mr-t-the-beerdrinker commented 10 years ago

Yes, I have everything working just fine. How to use it:

in JS, it is important you have server side, valid URL, and if you have some extra paramaters, also method type, for example:

serverSide: true,
ajax: {
            url: '/api/applications/paged',
            type: 'POST',
            data: function (d) {
                d.IsAdminView = WWT.model.isAdminView();
            }
        },

in this case, you have to add [HttpPost] attribute above your action:

[HttpPost]
[Route("api/applications/paged")]
public DataTablesResponse Paged([ModelBinder(typeof(DataTablesWithAdminPropertyWebApiBinder))] DataTablesRequestWithAdminProperty model)
        {

and for filtering/ordering, you have to handle those server-side by yourself, for example:

var applications = _unit.Repository<Application>().GetAll() // here you have IQueryable<Application>
var totalRecordCount = applications.Count();

Filter(applications, cmd.DtModel.Columns.GetFilteredColumns(), out applications, cmd.IsAdminView);
var searchRecordCount = applications.Count();

IOrderedQueryable<Application> sortedApplications = null;

Sort(applications, cmd.DtModel.Columns.GetSortedColumns(), out sortedApplications);

if (cmd.DtModel.Length == -1)
{
   return sortedApplications == null ? applications.OrderBy(s => s.Id).ToList() :             sortedApplications.ToList();
}
else
{
   return sortedApplications == null ?
      applications.OrderBy(s => s.Id).Skip(cmd.DtModel.Start).Take(cmd.DtModel.Length).ToList() :
      sortedApplications.Skip(cmd.DtModel.Start).Take(cmd.DtModel.Length).ToList();
}

and Filter / Sort methods, should look like this:

private void Filter(IQueryable<Application> applications, IEnumerable<Column> filteredColumns, out IQueryable<Application> filtered, bool isAdminView)
        {
            foreach (var filteredColumn in filteredColumns)
            {
                switch (filteredColumn.Data.ToLower())
                {
                    case "id":
                        int iValue = -1;
                        if( int.TryParse(filteredColumn.Search.Value, out iValue))
                        {
                                applications = applications.Where(a => a.Id == iValue);
                        }
                        break;

...

private void Sort(IQueryable<Application> applications, IEnumerable<Column> sortedColumns, out IOrderedQueryable<Application> sorted)
        {
            IOrderedQueryable<Application> sortedApplications = null;
            foreach (var sortedColumn in sortedColumns)
            {
                switch (sortedColumn.Data.ToLower())
                {
                    case "id":
                        sortedApplications = sortedApplications == null ? applications.CustomSort(sortedColumn.SortDirection, a => a.Id)
                            : sortedApplications.CustomSort(sortedColumn.SortDirection, a => a.Id);
                        break;

where custom sort is my extension:

public static class CollectionSortHelper
    {
        public static IOrderedQueryable<TSource> CustomSort<TSource, TKey>(this IQueryable<TSource> items, Column.OrderDirection direction, Expression<Func<TSource, TKey>> keySelector)
        {
            if (direction == Column.OrderDirection.Ascendant)
                return items.OrderBy(keySelector);

            return items.OrderByDescending(keySelector);
        }

        public static IOrderedQueryable<TSource> CustomSort<TSource, TKey>(this IOrderedQueryable<TSource> items, Column.OrderDirection direction, Expression<Func<TSource, TKey>> keySelector)
        {
            if (direction == Column.OrderDirection.Ascendant)
                return items.ThenBy(keySelector);

            return items.ThenByDescending(keySelector);
        }
    }

hope, this will help you somehow :)

Incognito-Nemo commented 9 years ago

Hello HairyOneMan!

I don't understand what is the object DataTablesWithAdminPropertyWebApiBinder… Could you explain more ?

Thanks a lot !

mr-t-the-beerdrinker commented 9 years ago

Hello,

it's just extended web api binder with additional property (admin flag is posted from datatables request) . Check the custom parameters section on readme of this project.

ALMMa commented 9 years ago

Today's commit just implemented WebApi2 support. NuGet packages are available now.