Skip to content

Commit b123f12

Browse files
Introduce AnonTypeNewTweaks, for LINQ 2 SQL (#295)
1 parent d506f8b commit b123f12

13 files changed

Lines changed: 174 additions & 34 deletions

net/DevExtreme.AspNet.Data.Tests.L2S/DevExtreme.AspNet.Data.Tests.L2S.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<Compile Include="RemoteGroupingStress.cs" />
9090
<Compile Include="Summary.cs" />
9191
<Compile Include="T670222_TotalSummary.cs" />
92+
<Compile Include="T670222_UnusedAnonTypeMembers.cs" />
9293
<Compile Include="TestDataClasses.cs">
9394
<DependentUpon>TestDataClasses.dbml</DependentUpon>
9495
</Compile>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using DevExtreme.AspNet.Data.ResponseModel;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Xunit;
6+
7+
namespace DevExtreme.AspNet.Data.Tests.L2S {
8+
9+
public class T670222_UnusedAnonTypeMembers {
10+
11+
[Fact]
12+
public void Scenario() {
13+
TestDataContext.Exec(context => {
14+
context.PurgeGenericTestTable();
15+
16+
var table = context.GenericTestDataItems;
17+
table.InsertOnSubmit(new GenericTestDataItem { Num = 123 });
18+
context.SubmitChanges();
19+
20+
var error = Record.Exception(delegate {
21+
DataSourceLoader.Load(table, new SampleLoadOptions {
22+
Group = new[] {
23+
new GroupingInfo { Selector = nameof(GenericTestDataItem.Num), IsExpanded = false },
24+
new GroupingInfo { Selector = nameof(GenericTestDataItem.Num), IsExpanded = false },
25+
new GroupingInfo { Selector = nameof(GenericTestDataItem.Num), IsExpanded = false }
26+
}
27+
});
28+
});
29+
30+
Assert.Null(error);
31+
});
32+
}
33+
34+
}
35+
36+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using DevExtreme.AspNet.Data.Types;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Linq.Expressions;
6+
using Xunit;
7+
8+
namespace DevExtreme.AspNet.Data.Tests {
9+
10+
public class AnonTypeNewTweaksTests {
11+
static readonly ICollection<Expression> EMPTY_EXPR_LIST = Array.Empty<Expression>();
12+
static readonly ICollection<Expression> PARTIAL_EXPR_LIST = new[] {
13+
Expression.Constant(0),
14+
Expression.Constant(1),
15+
Expression.Constant(2),
16+
};
17+
18+
19+
[Fact]
20+
public void Null() {
21+
AssertDefaultBehavior(null);
22+
}
23+
24+
[Fact]
25+
public void Defaults() {
26+
AssertDefaultBehavior(new AnonTypeNewTweaks());
27+
}
28+
29+
30+
static void AssertDefaultBehavior(AnonTypeNewTweaks tweaks) {
31+
Assert.Equal(
32+
"new AnonType()",
33+
AnonType.CreateNewExpression(EMPTY_EXPR_LIST, tweaks).ToString()
34+
);
35+
36+
Assert.Equal(
37+
"new AnonType`4(I0 = 0, I1 = 1, I2 = 2)",
38+
AnonType.CreateNewExpression(PARTIAL_EXPR_LIST, tweaks).ToString()
39+
);
40+
}
41+
42+
[Fact]
43+
public void AllowEmptyFalse() {
44+
var tweaks = new AnonTypeNewTweaks { AllowEmpty = false };
45+
46+
Assert.Equal(
47+
"1",
48+
AnonType.CreateNewExpression(EMPTY_EXPR_LIST, tweaks).ToString()
49+
);
50+
}
51+
52+
[Fact]
53+
public void AllowUnusedMembersFalse() {
54+
var tweaks = new AnonTypeNewTweaks { AllowUnusedMembers = false };
55+
56+
Assert.Equal(
57+
"new AnonType`4(I0 = 0, I1 = 1, I2 = 2, I3 = False)",
58+
AnonType.CreateNewExpression(PARTIAL_EXPR_LIST, tweaks).ToString()
59+
);
60+
}
61+
62+
}
63+
64+
}

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,9 @@ namespace DevExtreme.AspNet.Data.Tests {
88

99
public class DataSourceExpressionBuilderTests {
1010

11-
static DataSourceExpressionBuilder<T> CreateBuilder<T>(DataSourceLoadOptionsBase loadOptions, bool guardNulls = false) {
12-
return new DataSourceExpressionBuilder<T>(loadOptions, guardNulls);
13-
}
14-
1511
[Fact]
1612
public void Build_SkipTake() {
17-
var builder = CreateBuilder<int>(new SampleLoadOptions {
13+
var builder = new DataSourceExpressionBuilder<int>(new SampleLoadOptions {
1814
Skip = 111,
1915
Take = 222
2016
});
@@ -26,7 +22,7 @@ public void Build_SkipTake() {
2622

2723
[Fact]
2824
public void Build_Filter() {
29-
var builder = CreateBuilder<int>(new SampleLoadOptions {
25+
var builder = new DataSourceExpressionBuilder<int>(new SampleLoadOptions {
3026
Filter = new object[] { "this", ">", 123 }
3127
});
3228

@@ -39,7 +35,7 @@ public void Build_Filter() {
3935
public void Build_FilterAsEmptyList() {
4036
// To mitigate cases like https://devexpress.com/issue=T483154
4137

42-
var builder = CreateBuilder<int>(new SampleLoadOptions {
38+
var builder = new DataSourceExpressionBuilder<int>(new SampleLoadOptions {
4339
Filter = new object[0]
4440
});
4541

@@ -48,7 +44,7 @@ public void Build_FilterAsEmptyList() {
4844

4945
[Fact]
5046
public void Build_CountQuery() {
51-
var builder = CreateBuilder<int>(new SampleLoadOptions {
47+
var builder = new DataSourceExpressionBuilder<int>(new SampleLoadOptions {
5248
Skip = 111,
5349
Take = 222,
5450
Filter = new object[] { "this", 123 },
@@ -69,7 +65,7 @@ public void Build_CountQuery() {
6965

7066
[Fact]
7167
public void Build_Sorting() {
72-
var builder = CreateBuilder<Tuple<int, string>>(new SampleLoadOptions {
68+
var builder = new DataSourceExpressionBuilder<Tuple<int, string>>(new SampleLoadOptions {
7369
Sort = new[] {
7470
new SortingInfo {
7571
Selector="Item1"
@@ -98,7 +94,7 @@ public void GroupingAddedToSorting() {
9894
}
9995
};
10096

101-
var builder = CreateBuilder<Tuple<int, int, int>>(loadOptions);
97+
var builder = new DataSourceExpressionBuilder<Tuple<int, int, int>>(loadOptions);
10298

10399
Assert.Equal(
104100
"data.OrderBy(obj => obj.Item1).ThenByDescending(obj => obj.Item2).ThenBy(obj => obj.Item3)",
@@ -111,7 +107,7 @@ public void GroupingAddedToSorting() {
111107

112108
[Fact]
113109
public void MultiIntervalGroupsSortedOnce() {
114-
var builder = CreateBuilder<int>(new SampleLoadOptions {
110+
var builder = new DataSourceExpressionBuilder<int>(new SampleLoadOptions {
115111
Group = new[] {
116112
new GroupingInfo { Selector = "this", GroupInterval = "a" },
117113
new GroupingInfo { Selector = "this", GroupInterval = "b" }
@@ -123,7 +119,7 @@ public void MultiIntervalGroupsSortedOnce() {
123119

124120
[Fact]
125121
public void GuardNulls() {
126-
var builder = CreateBuilder<Tuple<int?, string, DateTime?>>(new SampleLoadOptions {
122+
var builder = new DataSourceExpressionBuilder<Tuple<int?, string, DateTime?>>(new SampleLoadOptions {
127123
Filter = new[] {
128124
new[] { "Item1", ">", "0" },
129125
new[] { "Item2", "contains", "z" },

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public void CompileEmpty() {
8686
var expr = new RemoteGroupExpressionCompiler<DataItem>(null, null, null).Compile(CreateTargetParam<DataItem>());
8787
Assert.Equal(
8888
"data"
89-
+ ".GroupBy(obj => 1)"
89+
+ ".GroupBy(obj => new AnonType())"
9090
+ ".Select(g => new AnonType`1(I0 = g.Count()))",
9191
expr.ToString()
9292
);
@@ -106,7 +106,7 @@ public void GroupInterval_Numeric() {
106106

107107
string Compile<T>(string selector, bool guardNulls) {
108108
var compiler = new RemoteGroupExpressionCompiler<T>(
109-
guardNulls,
109+
guardNulls, null,
110110
new[] {
111111
new GroupingInfo { Selector = selector, GroupInterval = "123" }
112112
},
@@ -130,7 +130,7 @@ public void GroupInterval_Date() {
130130

131131
string Compile<T>(string selector, bool guardNulls) {
132132
var compiler = new RemoteGroupExpressionCompiler<T>(
133-
guardNulls,
133+
guardNulls, null,
134134
new[] {
135135
new GroupingInfo { Selector = selector, GroupInterval = "year" },
136136
new GroupingInfo { Selector = selector, GroupInterval = "quarter" },

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,7 @@ public void TotalSummaryOnly() {
146146
Assert.Contains("Take", loadOptions.ExpressionLog[0]);
147147

148148
// 2 - load summaries
149-
Assert.Contains(".GroupBy(", loadOptions.ExpressionLog[1]);
150-
Assert.Contains(".Sum(", loadOptions.ExpressionLog[1]);
149+
Assert.Contains("AnonType()", loadOptions.ExpressionLog[1]);
151150

152151
Assert.Equal(4M, result.summary[0]);
153152
Assert.Single(result.data.Cast<object>());

net/DevExtreme.AspNet.Data/DataSourceExpressionBuilder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ namespace DevExtreme.AspNet.Data {
1313
class DataSourceExpressionBuilder<T> {
1414
DataSourceLoadOptionsBase _loadOptions;
1515
bool _guardNulls;
16+
AnonTypeNewTweaks _anonTypeNewTweaks;
1617

17-
public DataSourceExpressionBuilder(DataSourceLoadOptionsBase loadOptions, bool guardNulls) {
18+
public DataSourceExpressionBuilder(DataSourceLoadOptionsBase loadOptions, bool guardNulls = false, AnonTypeNewTweaks anonTypeNewTweaks = null) {
1819
_loadOptions = loadOptions;
1920
_guardNulls = guardNulls;
21+
_anonTypeNewTweaks = anonTypeNewTweaks;
2022
}
2123

2224
public Expression BuildLoadExpr(Expression source, bool paginate = true) {
@@ -47,7 +49,7 @@ Expression BuildCore(Expression expr, bool paginate = false, bool isCountQuery =
4749
genericTypeArguments = expr.Type.GetGenericArguments();
4850
}
4951
} else {
50-
expr = new RemoteGroupExpressionCompiler<T>(_guardNulls, _loadOptions.Group, _loadOptions.TotalSummary, _loadOptions.GroupSummary).Compile(expr);
52+
expr = new RemoteGroupExpressionCompiler<T>(_guardNulls, _anonTypeNewTweaks, _loadOptions.Group, _loadOptions.TotalSummary, _loadOptions.GroupSummary).Compile(expr);
5153
}
5254

5355
if(paginate) {

net/DevExtreme.AspNet.Data/DataSourceLoaderImpl.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@ class DataSourceLoaderImpl<S> {
2424

2525
public DataSourceLoaderImpl(IQueryable<S> source, DataSourceLoadOptionsBase options) {
2626
var isLinqToObjects = source is EnumerableQuery;
27+
var isL2S = Compat.IsL2S(source.Provider);
2728

2829
// Until https://github.com/aspnet/EntityFramework/issues/2341 is implemented
2930
// local grouping is more efficient for EF Core
3031
var preferLocalGrouping = Compat.IsEFCore(source.Provider);
3132

32-
Builder = new DataSourceExpressionBuilder<S>(options, isLinqToObjects);
33+
var tweaks = new AnonTypeNewTweaks {
34+
AllowEmpty = !isL2S,
35+
AllowUnusedMembers = !isL2S
36+
};
37+
38+
Builder = new DataSourceExpressionBuilder<S>(options, isLinqToObjects, tweaks);
3339
ShouldEmptyGroups = options.HasGroups && !options.Group.Last().GetIsExpanded();
3440
CanUseRemoteGrouping = options.RemoteGrouping ?? !(isLinqToObjects || preferLocalGrouping);
3541
SummaryIsTotalCountOnly = !options.HasGroupSummary && options.HasSummary && options.TotalSummary.All(i => i.SummaryType == AggregateName.COUNT);

net/DevExtreme.AspNet.Data/Helpers/Compat.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ internal static bool IsEFCore(IQueryProvider provider) {
2727
internal static bool IsXPO(IQueryProvider provider) {
2828
return provider.GetType().FullName.StartsWith("DevExpress.Xpo.XPQuery");
2929
}
30+
31+
internal static bool IsL2S(IQueryProvider provider) {
32+
return provider.GetType().FullName.StartsWith("System.Data.Linq.");
33+
}
3034
}
3135

3236
}

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

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@
1010
namespace DevExtreme.AspNet.Data.RemoteGrouping {
1111

1212
class RemoteGroupExpressionCompiler<T> : ExpressionCompiler {
13+
AnonTypeNewTweaks _anonTypeNewTweaks;
1314
IEnumerable<GroupingInfo> _grouping;
1415
IEnumerable<SummaryInfo>
1516
_totalSummary,
1617
_groupSummary;
1718

18-
public RemoteGroupExpressionCompiler(bool guardNulls, IEnumerable<GroupingInfo> grouping, IEnumerable<SummaryInfo> totalSummary, IEnumerable<SummaryInfo> groupSummary)
19+
public RemoteGroupExpressionCompiler(bool guardNulls, AnonTypeNewTweaks anonTypeNewTweaks, IEnumerable<GroupingInfo> grouping, IEnumerable<SummaryInfo> totalSummary, IEnumerable<SummaryInfo> groupSummary)
1920
: base(guardNulls) {
21+
_anonTypeNewTweaks = anonTypeNewTweaks;
2022
_grouping = grouping;
2123
_totalSummary = totalSummary;
2224
_groupSummary = groupSummary;
2325
}
2426

2527
#if DEBUG
2628
public RemoteGroupExpressionCompiler(IEnumerable<GroupingInfo> grouping, IEnumerable<SummaryInfo> totalSummary, IEnumerable<SummaryInfo> groupSummary)
27-
: this(false, grouping, totalSummary, groupSummary) {
29+
: this(false, null, grouping, totalSummary, groupSummary) {
2830
}
2931
#endif
3032

@@ -44,14 +46,7 @@ public Expression Compile(Expression target) {
4446
}
4547
}
4648

47-
Expression CreateGroupKeyLambdaBody() {
48-
if(groupKeyExprList.Count < 1)
49-
return Expression.Constant(1);
50-
51-
return AnonType.CreateNewExpression(groupKeyExprList);
52-
}
53-
54-
var groupKeyLambda = Expression.Lambda(CreateGroupKeyLambdaBody(), groupByParam);
49+
var groupKeyLambda = Expression.Lambda(AnonType.CreateNewExpression(groupKeyExprList, _anonTypeNewTweaks), groupByParam);
5550
var groupingType = typeof(IGrouping<,>).MakeGenericType(groupKeyLambda.ReturnType, typeof(T));
5651

5752
target = Expression.Call(typeof(Queryable), nameof(Queryable.GroupBy), new[] { typeof(T), groupKeyLambda.ReturnType }, target, Expression.Quote(groupKeyLambda));
@@ -90,7 +85,7 @@ Expression MakeAggregatingProjection(Expression target, Type groupingType, int g
9085
if(groupCount > 0)
9186
projectionExprList.AddRange(MakeAggregates(param, _groupSummary));
9287

93-
var projectionLambda = Expression.Lambda(AnonType.CreateNewExpression(projectionExprList), param);
88+
var projectionLambda = Expression.Lambda(AnonType.CreateNewExpression(projectionExprList, _anonTypeNewTweaks), param);
9489

9590
return Expression.Call(typeof(Queryable), nameof(Queryable.Select), new[] { param.Type, projectionLambda.ReturnType }, target, Expression.Quote(projectionLambda));
9691
}

0 commit comments

Comments
 (0)