Skip to content

Commit a2d415e

Browse files
Better error messages (#351)
1 parent 1138728 commit a2d415e

4 files changed

Lines changed: 91 additions & 31 deletions

File tree

net/DevExtreme.AspNet.Data.Tests/DataSourceLoaderTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,26 @@ public void Issue246(bool remoteSelect) {
530530
var department = (IDictionary<string, object>)item["Department"];
531531
Assert.Equal("abc", department["Title"]);
532532
}
533+
534+
[Fact]
535+
public void CustomAggregatorWithRemoteGrouping() {
536+
// https://github.com/DevExpress/DevExtreme.AspNet.Data/issues/341
537+
538+
StaticBarrier.Run(delegate {
539+
Aggregation.CustomAggregators.RegisterAggregator("my1", typeof(Object));
540+
541+
var exception = Record.Exception(delegate {
542+
DataSourceLoader.Load(new object[0], new SampleLoadOptions {
543+
RemoteGrouping = true,
544+
TotalSummary = new[] {
545+
new SummaryInfo { Selector = "this", SummaryType = "my1" }
546+
}
547+
});
548+
});
549+
550+
Assert.Contains("custom aggregate 'my1' cannot be translated", exception.Message);
551+
});
552+
}
533553
}
534554

535555
}

net/DevExtreme.AspNet.Data.Tests/RemoteGroupingTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,27 @@ public void Issue324() {
364364
Assert.Equal(2d, loadResult.summary[0]);
365365
}
366366

367+
[Fact]
368+
public void AggregateTranslationError() {
369+
// https://github.com/DevExpress/DevExtreme.AspNet.Data/issues/331
370+
371+
var data = new[] {
372+
new { P1 = "abc" }
373+
};
374+
375+
var exception = Record.Exception(delegate {
376+
DataSourceLoader.Load(data, new SampleLoadOptions {
377+
RemoteGrouping = true,
378+
TotalSummary = new[] {
379+
new SummaryInfo { Selector = "P1", SummaryType = "avg" }
380+
}
381+
});
382+
});
383+
384+
Assert.Equal("Failed to translate the 'sum' aggregate for the 'P1' member (System.String). See InnerException for details.", exception.Message);
385+
Assert.Contains("No coercion operator", exception.InnerException.Message);
386+
}
387+
367388
}
368389

369390
}

net/DevExtreme.AspNet.Data/Aggregation/CustomAggregators.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ internal static Aggregator<T> CreateAggregator<T>(string summaryType, IAccessor<
2727
return null;
2828
}
2929

30+
internal static bool IsRegistered(string summaryType) {
31+
return _aggregatorTypes.ContainsKey(summaryType);
32+
}
33+
3034
#if DEBUG
3135
internal static void Clear() {
3236
_aggregatorTypes.Clear();

net/DevExtreme.AspNet.Data/RemoteGrouping/RemoteGroupExpressionCompiler.cs

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -92,38 +92,44 @@ Expression MakeAggregatingProjection(Expression target, Type groupingType, int g
9292

9393
IEnumerable<Expression> MakeAggregates(Expression aggregateTarget, IEnumerable<SummaryInfo> summary) {
9494
foreach(var s in TransformSummary(summary)) {
95-
var itemParam = CreateItemParam(typeof(T));
96-
var selectorExpr = CompileAccessorExpression(itemParam, s.Selector, liftToNullable: true);
97-
var selectorType = selectorExpr.Type;
98-
99-
var callType = typeof(Enumerable);
100-
var isCountNotNull = s.SummaryType == AggregateName.COUNT_NOT_NULL;
101-
102-
if(isCountNotNull && Utils.CanAssignNull(selectorType)) {
103-
yield return Expression.Call(
104-
callType,
105-
nameof(Enumerable.Sum),
106-
Type.EmptyTypes,
107-
Expression.Call(
108-
typeof(Enumerable),
109-
nameof(Enumerable.Select),
110-
new[] { typeof(T), typeof(int) },
111-
aggregateTarget,
112-
Expression.Lambda(
113-
Expression.Condition(
114-
Expression.NotEqual(selectorExpr, Expression.Constant(null, selectorType)),
115-
Expression.Constant(1),
116-
Expression.Constant(0)
117-
),
118-
itemParam
119-
)
95+
yield return MakeAggregate(aggregateTarget, s);
96+
}
97+
}
98+
99+
Expression MakeAggregate(Expression aggregateTarget, SummaryInfo s) {
100+
var itemParam = CreateItemParam(typeof(T));
101+
var selectorExpr = CompileAccessorExpression(itemParam, s.Selector, liftToNullable: true);
102+
var selectorType = selectorExpr.Type;
103+
104+
var callType = typeof(Enumerable);
105+
var isCountNotNull = s.SummaryType == AggregateName.COUNT_NOT_NULL;
106+
107+
if(isCountNotNull && Utils.CanAssignNull(selectorType)) {
108+
return Expression.Call(
109+
callType,
110+
nameof(Enumerable.Sum),
111+
Type.EmptyTypes,
112+
Expression.Call(
113+
typeof(Enumerable),
114+
nameof(Enumerable.Select),
115+
new[] { typeof(T), typeof(int) },
116+
aggregateTarget,
117+
Expression.Lambda(
118+
Expression.Condition(
119+
Expression.NotEqual(selectorExpr, Expression.Constant(null, selectorType)),
120+
Expression.Constant(1),
121+
Expression.Constant(0)
122+
),
123+
itemParam
120124
)
121-
);
122-
} else {
123-
var callMethod = GetPreAggregateMethodName(s.SummaryType);
124-
var callMethodTypeParams = new List<Type> { typeof(T) };
125-
var callArgs = new List<Expression> { aggregateTarget };
125+
)
126+
);
127+
} else {
128+
var callMethod = GetPreAggregateMethodName(s.SummaryType);
129+
var callMethodTypeParams = new List<Type> { typeof(T) };
130+
var callArgs = new List<Expression> { aggregateTarget };
126131

132+
try {
127133
if(!IsWellKnownAggregateDataType(selectorType)) {
128134
if(s.SummaryType == AggregateName.MIN || s.SummaryType == AggregateName.MAX) {
129135
callMethodTypeParams.Add(selectorType);
@@ -140,7 +146,10 @@ IEnumerable<Expression> MakeAggregates(Expression aggregateTarget, IEnumerable<S
140146
if(!isCountNotNull)
141147
callArgs.Add(Expression.Lambda(selectorExpr, itemParam));
142148

143-
yield return Expression.Call(callType, callMethod, callMethodTypeParams.ToArray(), callArgs.ToArray());
149+
return Expression.Call(callType, callMethod, callMethodTypeParams.ToArray(), callArgs.ToArray());
150+
} catch(Exception x) {
151+
var message = $"Failed to translate the '{s.SummaryType}' aggregate for the '{s.Selector}' member ({selectorExpr.Type}). See InnerException for details.";
152+
throw new Exception(message, x);
144153
}
145154
}
146155
}
@@ -184,6 +193,12 @@ static string GetPreAggregateMethodName(string summaryType) {
184193
return nameof(Enumerable.Count);
185194
}
186195

196+
if(CustomAggregators.IsRegistered(summaryType)) {
197+
var message = $"The custom aggregate '{summaryType}' cannot be translated to a LINQ expression."
198+
+ $" Set {nameof(DataSourceLoadOptionsBase)}.{nameof(DataSourceLoadOptionsBase.RemoteGrouping)} to False to enable in-memory aggregate calculation.";
199+
throw new InvalidOperationException(message);
200+
}
201+
187202
throw new NotSupportedException();
188203
}
189204

0 commit comments

Comments
 (0)