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
;
}
}
}