This is a migrated thread and some comments may be shown as answers.

Custom aggregate for QueryableDataProvider

1 Answer 96 Views
PivotGrid
This is a migrated thread and some comments may be shown as answers.
Yehudah
Top achievements
Rank 1
Yehudah asked on 03 Mar 2017, 09:33 AM

Hi!

There is any way to create custom aggregate for QueryableDataProvider?

I want to create CustomDistinct aggregate, I did it successfully for LocalDataSourceProvider, and I need it also for QueryableDataProvider.

Here is my code, unfortunately, it is not working at all..

Hi!
There is any way to create custom aggregate for QueryableDataProvider?
I want to create CustomDistinct aggregate, I did it successfully for LocalDataSourceProvider, and I neet also for QueryableDataProvider.
Here is my code, unfurtonately, it is not working at all..

I see "CountDistinct" in "More aggregate options" window, but when i choose it, the pivot got an error.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Telerik.Pivot.Core;
using Telerik.Pivot.Core.Fields;
using Telerik.Pivot.Queryable;
 
namespace YA.Pivot.Queryable
{
    public class MyQueryableDataProvider : QueryableDataProvider
    {
        protected override void OnPrepareDescriptionForField(PrepareDescriptionForFieldEventArgs args)
        {
            base.OnPrepareDescriptionForField(args);
            if (args.DescriptionType == DataProviderDescriptionType.Aggregate)
            {
                var old = args.Description as QueryablePropertyAggregateDescriptionBase;
 
                args.Description = new CustomPropertyAggregateDescription()
                {
                    PropertyName = old.PropertyName,
                    FunctionName = old.FunctionName,
                    AggregateFunction = old.AggregateFunction,
                    CustomName = old.CustomName,
                    StringFormat = old.StringFormat
                };
            }
        }
    }
 
    internal class CustomPropertyAggregateDescription : QueryablePropertyAggregateDescriptionBase
    {
        private enum MyAggregateFunctions
        {
            CountDistinct = -100
        }
 
        protected override Cloneable CreateInstanceCore()
        {
            return new CustomPropertyAggregateDescription()
            {
                AggregateFunction = this.AggregateFunction,
                CustomName = this.CustomName,
                FunctionName = this.FunctionName,
                PropertyName = this.PropertyName,
                StringFormat = this.StringFormat,
                StringFormatSelector = this.StringFormatSelector,
                TotalFormat = this.TotalFormat
            };
        }
 
        private bool IsCountDistinct => (int)base.AggregateFunction == (int)MyAggregateFunctions.CountDistinct;
 
        protected override Expression CreateAggregateExpression(Expression enumerableExpression, string aggregatedValueName)
        {
            if (enumerableExpression == null)
            {
                throw new ArgumentNullException(nameof(enumerableExpression));
            }
 
            if (IsCountDistinct)
            {
                return CreateCountDistinctExpression(enumerableExpression, aggregatedValueName);
            }
 
            return base.CreateAggregateExpression(enumerableExpression, aggregatedValueName);
        }
 
        private Expression CreateCountDistinctExpression(Expression enumerableExpression, string aggregatedValueName)
        {
            Type itemType = TypeExtensions.ExtractItemTypeFromEnumerableType(enumerableExpression.Type);
            var selectParamExp = Expression.Parameter(itemType, "e");
            var selectPropAccessExp = Expression.Property(selectParamExp, aggregatedValueName);
            LambdaExpression selectLambdaExpression = Expression.Lambda(selectPropAccessExp, selectParamExp);
 
            var select = Expression.Call(
              typeof(Enumerable),
              "Select",
              new[] { itemType, selectLambdaExpression.Body.Type },
              enumerableExpression,
              selectLambdaExpression);
 
            var distinct = Expression.Call(
                typeof(Enumerable),
                "Distinct",
                new[] { selectLambdaExpression.Body.Type },
                select);
 
            var count = Expression.Call(
                typeof(Enumerable),
                "Count",
                new[] { selectLambdaExpression.Body.Type },
                distinct);
 
            return count;
        }
 
        protected override IEnumerable<object> SupportedAggregateFunctions =>
                base.SupportedAggregateFunctions.Concat(new object[] { MyAggregateFunctions.CountDistinct });
    }
 
    internal static class TypeExtensions
    {
        public static Type ExtractItemTypeFromEnumerableType(Type type)
        {
            var enumerableType = type.FindGenericType(typeof(IEnumerable<>));
 
            if (enumerableType == null)
            {
                throw new ArgumentException("Provided type is not IEnumerable<>", nameof(type));
            }
 
            return enumerableType.GetGenericArguments().First();
        }
 
        private static Type FindGenericType(this Type type, Type genericType)
        {
            while (type != null && type != typeof(object))
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == genericType)
                {
                    return type;
                }
 
                if (genericType.IsInterface)
                {
                    foreach (Type intfType in type.GetInterfaces())
                    {
                        Type found = intfType.FindGenericType(genericType);
 
                        if (found != null)
                        {
                            return found;
                        }
                    }
                }
 
                type = type.BaseType;
            }
 
            return null;
        }
    }
}

1 Answer, 1 is accepted

Sort by
0
Polya
Telerik team
answered on 07 Mar 2017, 02:21 PM
Hi Yehudah,

With the current implementation of QueryableDataProvider there is no available way of working with custom aggregate functions. The QueryableDataProvider does not know what AggregateValue should be created for the custom CountDistinct function and throws an InvalidOperationException.

You can add a feature request about this in our Feedback portal where you can describe your scenario: https://feedback.telerik.com/Project/143/Feedback/List/All%20Feedback

Regards,
Polya
Telerik by Progress
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
Tags
PivotGrid
Asked by
Yehudah
Top achievements
Rank 1
Answers by
Polya
Telerik team
Share this question
or