ToDataSourceResult

1 Answer 9404 Views
Grid
Simon831
Top achievements
Rank 1
Simon831 asked on 16 Aug 2012, 10:34 AM
I am taking an existing project and adding KendoUI in place of Grid's by another vendor.
The project uses IoC, UnitOfWork etc and so the MVC controllers do not directly use the Context and I cannot see the ToDataSourceResult method.
I have got it mostly working using .Server(), but now want to start using Ajax.


What does ToDataSourceResult do, and how can I replicate its functionality?

1 Answer, 1 is accepted

Sort by
0
Daniel
Telerik team
answered on 21 Aug 2012, 07:56 AM
Hello,

The ToDataSourceResult is an IEnumerable(IQueryable) extension method and you should include the Kendo.Mvc.Extensions namespace in order to use it. For more detailed information, check the Ajax binding documentation topic.

Regards,
Daniel
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
Shawn
Top achievements
Rank 1
commented on 11 Oct 2012, 03:03 PM

I'm continually fascinated how poor documentation is on the MVC side of things with Kendo.  Poor examples, limited, lack of readable documentation.

For example, overrides on ToDataSourceResult - you have zero doc's that I can find on them - with examples.
Vesselin Obreshkov
Top achievements
Rank 2
commented on 11 Oct 2012, 08:33 PM

Yeah, the MVC extensions lack documentation a little bit. The ToDataSourceResult is simply an IQueryable extension that translates and handles filtering, sorting, grouping and aggregates. It works together with DataSourceRequest and can also handle projections for you (myIQueryable.ToDataSourceResult<TSource, TDestination>(request, model => AutoMapper.Mapper.Map<TSource, TDestination>(model)) which is pretty cool - we use it all the time. Go download Telerik JustDecompile and explore away I guess - this is how we had to figure out a lot of it to integrate with our own stuff.



They're improving their documentation all the time but they just released the MVC extensions not too long ago so it'll probably take them some time to get those up to speed.



If you have specific questions though, the guys are usually pretty good with their support tickets and stuff. I've hit them up about the stupidest things at 5PM when my brain is fried and usually have tickets taken care of with code samples, etc the next day for the most part...
Shawn
Top achievements
Rank 1
commented on 11 Oct 2012, 09:17 PM

yes, reflecting via dotPeek/Reflector has been the only way right now.  I haven't tried teleriks tool yet.  I hate mucking up VS with all those add-ins.
Vesselin Obreshkov
Top achievements
Rank 2
commented on 11 Oct 2012, 11:43 PM

Reflecting, sorry... and you don't need to install any VSExtensions - you just need to include Kendo.Mvc.dll in your project and you're good to go (they have a -vsdoc for the .js files too). On my work VM, the only Telerik extension I have installed is JustCode which has nothing to do with Kendo. I have the whole suite on my "play" box and no issues there honestly. Only thing is JustCode has a conflict with the Productivity Power Tools by MS if you use that one - need to tweak some type assistance settings.
Stef Heyenrath
Top achievements
Rank 1
commented on 15 Oct 2012, 06:04 PM

I've a simple database model class like:
public class Product
{
 public long Product_Id;
 public string Product_Name;
}

And a simple ViewModel like:
public class ProductVM
{
  public long ProductId;
  public string ProductName;
}


When filtering on the ProductName (a ViewModel property) in the grid on the webpage, I get an ArgumentException like : "Invalid property or field - 'ProductName' for type: Product" when I call the code :
var result = products.ToDataSourceResult(request, model => AutoMapper.Mapper.Map<ProductVM>(model));
which is logical because the database model class (Product) does not contain the property ProductName but only Product_Name.

Is there an easy way or an extension method available to change the filter based on the AutoMapper defined mapping, or should I just write my own method to modify the filters ?
Vesselin Obreshkov
Top achievements
Rank 2
commented on 15 Oct 2012, 07:57 PM

public static class DataSourceRequestExtension
    {
        public static DataSourceRequest Remap(this DataSourceRequest Request, Type TSource, Type TDestination)
        {
            Request.Sorts = RemapSort(Request.Sorts, TSource, TDestination);
            Request.Filters = RemapFilter(Request.Filters, TSource, TDestination);
  
            return Request;
        }
  
        private static IList<Kendo.Mvc.SortDescriptor> RemapSort(IList<Kendo.Mvc.SortDescriptor> Sorts, Type TSource, Type TDestination)
        {
            if (Sorts != null)
            {
                // Get currently mapped properties for the source and destination types
                List<AutoMapper.PropertyMap> propertyMaps = AutoMapper.Mapper.FindTypeMapFor(TSource, TDestination).GetPropertyMaps().ToList();
  
                foreach (AutoMapper.PropertyMap propertyMap in propertyMaps)
                {
                    // We are only interested in custom expressions because they do not map field to field
                    if (propertyMap.CustomExpression != null)
                    {
                        // Get the linq expression body
                        string body = propertyMap.CustomExpression.Body.ToString();
  
                        // Get the item tag
                        string tag = propertyMap.CustomExpression.Parameters[0].Name;
  
                        Regex regex = new Regex(string.Format("{0}.", tag));
  
                        string destination = regex.Replace(body, string.Empty, 1);
                        string source = propertyMap.DestinationProperty.Name;
  
                        // Go throug each sort and update
                        foreach (Kendo.Mvc.SortDescriptor sort in Sorts)
                        {
                            if (sort.Member.ToLower().Equals(source.ToLower()))
                            {
                                sort.Member = destination;
                            }
                        }
                    }
                }
            }
  
            return Sorts;
        }
  
        private static IList<Kendo.Mvc.IFilterDescriptor> RemapFilter(IList<Kendo.Mvc.IFilterDescriptor> Filters, Type TSource, Type TDestination)
        {
            if (Filters != null)
            {
                // Get currently mapped properties for the source and destination types
                List<AutoMapper.PropertyMap> propertyMaps = AutoMapper.Mapper.FindTypeMapFor(TSource, TDestination).GetPropertyMaps().ToList();
  
                foreach (AutoMapper.PropertyMap propertyMap in propertyMaps)
                {
                    // We are only interested in custom expressions because they do not map field to field
                    if (propertyMap.CustomExpression != null)
                    {
                        // Get the linq expression body
                        string body = propertyMap.CustomExpression.Body.ToString();
  
                        // Get the item tag
                        string tag = propertyMap.CustomExpression.Parameters[0].Name;
  
                        string destination = body.Replace(string.Format("{0}.", tag), string.Empty);
                        string source = propertyMap.DestinationProperty.Name;
  
                        // Go throug each sort and update
                        foreach (Kendo.Mvc.IFilterDescriptor filter in Filters)
                        {
                            if (filter is CompositeFilterDescriptor)
                            {
                                ProcessCompositeFilter((CompositeFilterDescriptor)filter, source, destination);
                            }
  
                            if (filter is FilterDescriptor)
                            {
                                if (((FilterDescriptor)filter).Member.ToLower().Equals(source.ToLower()))
                                {
                                    ((FilterDescriptor)filter).Member = destination;
                                }
                            }
                        }
                    }
                }
            }
  
            return Filters;
        }
  
        private static void ProcessCompositeFilter(CompositeFilterDescriptor filter, string source, string destination)
        {
            foreach (Kendo.Mvc.IFilterDescriptor subFilter in filter.FilterDescriptors)
            {
                if (subFilter is CompositeFilterDescriptor)
                {
                    ProcessCompositeFilter((CompositeFilterDescriptor)subFilter, source, destination);
                }
                else
                {
                    Kendo.Mvc.FilterDescriptor filterDescriptor = (Kendo.Mvc.FilterDescriptor)subFilter;
  
                    if (filterDescriptor.Member.ToLower().Equals(source.ToLower()))
                    {
                        filterDescriptor.Member = destination;
                    }
                }
            }
        }
    }

Not quite sure you can do this out of the box. I have to name all my properties exactly the same, otherwise, nothing works. Check this out though, we've used it and seems to better in that case. Only iterates though filters and sorts though, no aggregates and group support:

Stef Heyenrath
Top achievements
Rank 1
commented on 15 Oct 2012, 08:40 PM

I've created this code which has less code and uses more recursion. Can you verify if my code covers the same functionality ?
No support for aggregates and group support yet...
using System;
using System.Linq;
using System.Collections.Generic;
using AutoMapper;
using Kendo.Mvc;
using Kendo.Mvc.UI;
  
namespace KendoUIMvcApplication1
{
    public static class MyDataSourceRequestExtensions
    {
        public static void ReMap<TSource, TDestination>(this DataSourceRequest request)
        {
            var mappings = GetPropertyMappings(new Dictionary<string, string>(), typeof(TSource), typeof(TDestination));
  
            MapFilterDescriptors<TSource, TDestination>(mappings, request.Filters);
            MapSortDescriptors(mappings, request.Sorts);
        }
  
        private static IDictionary<string, string> GetPropertyMappings(IDictionary<string, string> mappings, Type sourceType, Type destinationType)
        {
            var map = Mapper.FindTypeMapFor(sourceType, destinationType);
  
            // We are only interested in custom expressions because they do not map field to field
            foreach (var propertyMap in map.GetPropertyMaps().Where(pm => pm.CustomExpression != null))
            {
                // Get the linq expression body
                string body = propertyMap.CustomExpression.Body.ToString();
         
                // Get the item tag
                string tag = propertyMap.CustomExpression.Parameters[0].Name;
         
                string destination = body.Replace(string.Format("{0}.", tag), string.Empty);
                string source = propertyMap.DestinationProperty.Name;
         
                mappings.Add(source, destination);
            }
  
            return mappings;
        }
  
        private static void MapFilterDescriptors<TModel, TResult>(IDictionary<string, string> mappings, IEnumerable<IFilterDescriptor> filters)
        {
            foreach (var filter in filters)
            {
                if (filter is FilterDescriptor)
                {
                    var normalFilter = (FilterDescriptor)filter;
                    if (mappings.ContainsKey(normalFilter.Member))
                    {
                        normalFilter.Member = mappings[normalFilter.Member];
                    }
                }
                else if (filter is CompositeFilterDescriptor)
                {
                    var compositeFilter = (CompositeFilterDescriptor)filter;
                    MapFilterDescriptors<TModel, TResult>(mappings, compositeFilter.FilterDescriptors);
                }
            }
        }
  
        private static void MapSortDescriptors(IDictionary<string, string> mappings, IEnumerable<SortDescriptor> sortDescriptors)
        {
            foreach (var sortDescriptor in sortDescriptors)
            {
                string sourceMemberName = sortDescriptor.Member;
                if (mappings.ContainsKey(sourceMemberName))
                {
                    sortDescriptor.Member = mappings[sourceMemberName];
                }
            }
        }
    }
}
Vesselin Obreshkov
Top achievements
Rank 2
commented on 17 Oct 2012, 08:42 PM

Well I'm not a human compiler so I can't tell you how well that code will work (or not). One thing I'll say though is you don't want to do much recursion of the data that you present to your client. You should be using flattened ViewModels whenever possible and don't do any recursion on there if possible. Specifically if you use JSON/AJAX binding.
Tomislav
Top achievements
Rank 1
commented on 19 Oct 2013, 11:42 PM

Can you please provide example for use of this code
Steve
Top achievements
Rank 1
commented on 29 Oct 2016, 05:24 PM

I'm getting tired of mis leading documentation and 12 hours to get partial responses - to then re-reply to support response only to wait another 12 hours.   My subscription has 45 days left and I'm looking at alternatives.
Marin Bratanov
Telerik team
commented on 01 Nov 2016, 08:14 AM

Hello Steve,

If you have concerns about out support services I suggest you either open a private ticket, or email me directly at marin <dot> bratanov <at> telerik <dot> com so we can review the situation privately instead of diluting this thread.

I would only like to point out that the guaranteed response time for private tickets is 24 hours during business days (Mon-Fri), as noted in the Support Plans for DevTools page.

Regards,

Marin Bratanov
Support Lead @ Web Components team
Telerik by Progress
Check out the new UI for ASP.NET Core, the most complete UI suite for ASP.NET Core development on the market, with 60+ tried-and-tested widgets, based on Kendo UI.
Dave
Top achievements
Rank 1
commented on 24 Nov 2020, 09:03 AM

Here you go Telerik. I'm surprised you don't offer this given the countless developers who map to flattened view models

The following Extension method to DataSourceRequest will allow flattened members to be renamed to their corresponding database member.

Example: request.RemapMember("ChildObjectName", "ChildObject.Name");

Send me an email and we can chat about you using my code officially... :)

 

 

public static class DataSourceRequestExtensions
{
public static void RemapMember(this DataSourceRequest request, string memberName, string newMemberName)
{
foreach (var sort in request.Sorts)
{
if (sort.Member.Equals(memberName))
{
sort.Member = newMemberName;
}
}

foreach (var filter in request.Filters)
{
if (filter is CompositeFilterDescriptor compositeFilterDescriptor)
{
foreach (var compositeFilter in compositeFilterDescriptor.FilterDescriptors)
{
RemapFilterDescription(compositeFilter, memberName, newMemberName);
}
}

RemapFilterDescription(filter, memberName, newMemberName);
}
}

private static void RemapFilterDescription(IFilterDescriptor filter, string memberName, string newMemberName)
{
if (!(filter is FilterDescriptor descriptor)) return;

if (descriptor.Member.Equals(memberName))
{
descriptor.Member = newMemberName;
}
}

Angel Petrov
Telerik team
commented on 26 Nov 2020, 03:41 PM

Hello,

Generally there are plenty of solutions for remapping models. It is up to the developer to choose how to implement this. We do not plan to include this in our source for now. If we do at some point we will need to examine all the cases so we could ensure that there is not functionality that is left out.

Regards,
Angel Petrov
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
Grid
Asked by
Simon831
Top achievements
Rank 1
Answers by
Daniel
Telerik team
Share this question
or