Custom aggregate for QueryableDataProvider

2 posts, 0 answers
  1. Yehudah
    Yehudah avatar
    20 posts
    Member since:
    Feb 2014

    Posted 03 Mar Link to this post

    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;
            }
        }
    }
  2. Polya
    Admin
    Polya avatar
    238 posts

    Posted 07 Mar Link to this post

    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.
Back to Top