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?
2 Answers, 1 is accepted
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
For example, overrides on ToDataSourceResult - you have zero doc's that I can find on them - with examples.
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...
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));
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 ?
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:
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];
}
}
}
}
}
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,
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;
}
}
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/.