Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void Build_SkipTake() {

var expr = builder.BuildLoadExpr();

Assert.Equal("data.Skip(111).Take(222)", expr.Body.ToString());
Assert.Equal("data.Skip(111).Take(222)", expr.ToString());
}

[Fact]
Expand All @@ -32,7 +32,7 @@ public void Build_Filter() {

var expr = builder.BuildLoadExpr();

Assert.Equal("data.Where(obj => (obj > 123))", expr.Body.ToString());
Assert.Equal("data.Where(obj => (obj > 123))", expr.ToString());
}

[Fact]
Expand Down Expand Up @@ -82,7 +82,7 @@ public void Build_Sorting() {
});

var expr = builder.BuildLoadExpr();
Assert.Equal("data.OrderBy(obj => obj.Item1).ThenByDescending(obj => obj.Item2)", expr.Body.ToString());
Assert.Equal("data.OrderBy(obj => obj.Item1).ThenByDescending(obj => obj.Item2)", expr.ToString());
}

[Fact]
Expand All @@ -102,11 +102,11 @@ public void GroupingAddedToSorting() {

Assert.Equal(
"data.OrderBy(obj => obj.Item1).ThenByDescending(obj => obj.Item2).ThenBy(obj => obj.Item3)",
builder.BuildLoadExpr().Body.ToString()
builder.BuildLoadExpr().ToString()
);

loadOptions.Sort = null;
Assert.Contains("OrderBy", builder.BuildLoadExpr().Body.ToString());
Assert.Contains("OrderBy", builder.BuildLoadExpr().ToString());
}

[Fact]
Expand All @@ -118,7 +118,7 @@ public void MultiIntervalGroupsSortedOnce() {
}
});

Assert.Equal("data.OrderBy(obj => obj)", builder.BuildLoadExpr().Body.ToString());
Assert.Equal("data.OrderBy(obj => obj)", builder.BuildLoadExpr().ToString());
}

[Fact]
Expand All @@ -137,9 +137,6 @@ public void GuardNulls() {
}
}, true);

var expr = builder.BuildLoadExpr();
var query = expr.Compile();

var data = new[] {
// filtered out
null,
Expand All @@ -151,9 +148,10 @@ public void GuardNulls() {
// kept
Tuple.Create<int?, string, DateTime?>(1, "zz", new DateTime(2000, 1, 2)),
Tuple.Create<int?, string, DateTime?>(1, "zz", new DateTime(2000, 1, 1))
};
}.AsQueryable();

var result = query(data.AsQueryable()).ToArray();
var expr = builder.BuildLoadExpr(data.Expression);
var result = data.Provider.CreateQuery<object>(expr).ToArray();
Assert.Equal(2, result.Length);
}

Expand All @@ -165,16 +163,16 @@ public void DefaultSort() {

var builder = new DataSourceExpressionBuilder<Tuple<int, int>>(options, false);

Assert.Equal("data.OrderBy(obj => obj.Item1)", builder.BuildLoadExpr(false).Body.ToString());
Assert.Equal("data.OrderBy(obj => obj.Item1)", builder.BuildLoadExpr(false).ToString());

options.Sort = new[] {
new SortingInfo { Selector = "Item2" }
};

Assert.Equal("data.OrderBy(obj => obj.Item2).ThenBy(obj => obj.Item1)", builder.BuildLoadExpr(false).Body.ToString());
Assert.Equal("data.OrderBy(obj => obj.Item2).ThenBy(obj => obj.Item1)", builder.BuildLoadExpr(false).ToString());

options.Sort[0].Selector = "Item1";
Assert.Equal("data.OrderBy(obj => obj.Item1)", builder.BuildLoadExpr(false).Body.ToString());
Assert.Equal("data.OrderBy(obj => obj.Item1)", builder.BuildLoadExpr(false).ToString());
}

[Fact]
Expand All @@ -190,7 +188,7 @@ public void NoUnnecessaryOrderingForRemoteGroups() {
};

var builder = new DataSourceExpressionBuilder<Tuple<int, int>>(options, false);
var expr = builder.BuildLoadGroupsExpr().Body.ToString();
var expr = builder.BuildLoadGroupsExpr().ToString();

Assert.True(expr.StartsWith("data.GroupBy"));
}
Expand All @@ -205,7 +203,7 @@ public void AlwaysOrderDataByPrimaryKey() {

Assert.Equal(
"data.OrderBy(obj => obj.Item2).ThenBy(obj => obj.Item1)",
builder.BuildLoadExpr().Body.ToString()
builder.BuildLoadExpr().ToString()
);
}

Expand All @@ -221,15 +219,15 @@ public void DefaultSortAndPrimaryKey() {

Assert.Equal(
"data.OrderBy(obj => obj.Item1)",
builder.BuildLoadExpr().Body.ToString()
builder.BuildLoadExpr().ToString()
);

options.DefaultSort = "Item2";
options.Sort[0].Selector = "Item3";

Assert.Equal(
"data.OrderBy(obj => obj.Item3).ThenBy(obj => obj.Item2).ThenBy(obj => obj.Item1)",
builder.BuildLoadExpr().Body.ToString()
builder.BuildLoadExpr().ToString()
);
}
}
Expand Down
29 changes: 29 additions & 0 deletions net/DevExtreme.AspNet.Data.Tests/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace DevExtreme.AspNet.Data.Tests {

static class Extensions {

public static Expression BuildLoadExpr<T>(this DataSourceExpressionBuilder<T> builder, bool paginate = true) {
return builder.BuildLoadExpr(CreateSourceExpr<T>(), paginate);
}

public static Expression BuildCountExpr<T>(this DataSourceExpressionBuilder<T> builder) {
return builder.BuildCountExpr(CreateSourceExpr<T>());
}

public static Expression BuildLoadGroupsExpr<T>(this DataSourceExpressionBuilder<T> builder) {
return builder.BuildLoadGroupsExpr(CreateSourceExpr<T>());
}

static Expression CreateSourceExpr<T>() {
return Expression.Parameter(typeof(IQueryable<T>), "data");
}

}

}
47 changes: 14 additions & 33 deletions net/DevExtreme.AspNet.Data/DataSourceExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,65 +18,46 @@ public DataSourceExpressionBuilder(DataSourceLoadOptionsBase loadOptions, bool g
_guardNulls = guardNulls;
}

public Expression<Func<IQueryable<T>, IQueryable<T>>> BuildLoadExpr(bool paginate = true) {
var param = CreateParam();
return Expression.Lambda<Func<IQueryable<T>, IQueryable<T>>>(
BuildCore(param, paginate: paginate),
param
);
public Expression BuildLoadExpr(Expression source, bool paginate = true) {
return BuildCore(source, paginate: paginate);
}

public Expression<Func<IQueryable<T>, int>> BuildCountExpr() {
var param = CreateParam();
return Expression.Lambda<Func<IQueryable<T>, int>>(
BuildCore(param, isCountQuery: true),
param
);
public Expression BuildCountExpr(Expression source) {
return BuildCore(source, isCountQuery: true);
}

public Expression<Func<IQueryable<T>, IQueryable<AnonType>>> BuildLoadGroupsExpr() {
var param = CreateParam();
return Expression.Lambda<Func<IQueryable<T>, IQueryable<AnonType>>>(
BuildCore(param, remoteGrouping: true),
param
);
public Expression BuildLoadGroupsExpr(Expression source) {
return BuildCore(source, remoteGrouping: true);
}

Expression BuildCore(ParameterExpression param, bool paginate = false, bool isCountQuery = false, bool remoteGrouping = false) {
Expression BuildCore(Expression expr, bool paginate = false, bool isCountQuery = false, bool remoteGrouping = false) {
var queryableType = typeof(Queryable);
var genericTypeArguments = new[] { typeof(T) };

Expression body = param;

if(_loadOptions.HasFilter)
body = Expression.Call(queryableType, "Where", genericTypeArguments, body, Expression.Quote(new FilterExpressionCompiler<T>(_guardNulls).Compile(_loadOptions.Filter)));
expr = Expression.Call(queryableType, "Where", genericTypeArguments, expr, Expression.Quote(new FilterExpressionCompiler<T>(_guardNulls).Compile(_loadOptions.Filter)));

if(!isCountQuery) {
if(!remoteGrouping) {
if(_loadOptions.HasAnySort)
body = new SortExpressionCompiler<T>(_guardNulls).Compile(body, _loadOptions.GetFullSort());
expr = new SortExpressionCompiler<T>(_guardNulls).Compile(expr, _loadOptions.GetFullSort());
} else {
body = new RemoteGroupExpressionCompiler<T>(_loadOptions.Group, _loadOptions.TotalSummary, _loadOptions.GroupSummary).Compile(body);
expr = new RemoteGroupExpressionCompiler<T>(_loadOptions.Group, _loadOptions.TotalSummary, _loadOptions.GroupSummary).Compile(expr);
}

if(paginate) {
if(_loadOptions.Skip > 0)
body = Expression.Call(queryableType, "Skip", genericTypeArguments, body, Expression.Constant(_loadOptions.Skip));
expr = Expression.Call(queryableType, "Skip", genericTypeArguments, expr, Expression.Constant(_loadOptions.Skip));

if(_loadOptions.Take > 0)
body = Expression.Call(queryableType, "Take", genericTypeArguments, body, Expression.Constant(_loadOptions.Take));
expr = Expression.Call(queryableType, "Take", genericTypeArguments, expr, Expression.Constant(_loadOptions.Take));
}
}

if(isCountQuery)
body = Expression.Call(queryableType, "Count", genericTypeArguments, body);

return body;
}

expr = Expression.Call(queryableType, "Count", genericTypeArguments, expr);

ParameterExpression CreateParam() {
return Expression.Parameter(typeof(IQueryable<T>), "data");
return expr;
}
}

Expand Down
21 changes: 13 additions & 8 deletions net/DevExtreme.AspNet.Data/DataSourceLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace DevExtreme.AspNet.Data {

Expand All @@ -20,7 +21,7 @@ public static object Load<T>(IQueryable<T> source, DataSourceLoadOptionsBase opt
var builder = new DataSourceExpressionBuilder<T>(options, isLinqToObjects);

if(options.IsCountQuery)
return builder.BuildCountExpr().Compile()(source);
return ExecCount(builder, source);

var accessor = new DefaultAccessor<T>();
var result = new DataSourceLoadResult();
Expand All @@ -47,12 +48,12 @@ public static object Load<T>(IQueryable<T> source, DataSourceLoadOptionsBase opt
options.DefaultSort = EFSorting.FindSortableMember(typeof(T));

var deferPaging = options.HasGroups || options.HasSummary && !canUseRemoteGrouping;
var queryResult = ExecQuery(builder.BuildLoadExpr(!deferPaging).Compile(), source, options);
var dataQuery = AppendExpr<T, T>(source, builder.BuildLoadExpr(source.Expression, !deferPaging), options);

IEnumerable data = queryResult;
IEnumerable data = dataQuery;

if(options.HasGroups) {
data = new GroupHelper<T>(accessor).Group(queryResult, options.Group);
data = new GroupHelper<T>(accessor).Group(dataQuery, options.Group);
if(options.RequireGroupCount) {
result.groupCount = (data as IList).Count;
}
Expand All @@ -64,7 +65,7 @@ public static object Load<T>(IQueryable<T> source, DataSourceLoadOptionsBase opt
result.summary = groupingResult.Totals;
} else {
if(options.RequireTotalCount)
result.totalCount = builder.BuildCountExpr().Compile()(source);
result.totalCount = ExecCount(builder, source);

if(options.HasSummary) {
data = Buffer<T>(data);
Expand Down Expand Up @@ -95,8 +96,12 @@ static IEnumerable Buffer<T>(IEnumerable data) {
return data;
}

static IQueryable<R> ExecQuery<S, R>(Func<IQueryable<S>, IQueryable<R>> query, IQueryable<S> source, DataSourceLoadOptionsBase options) {
var result = query(source);
static int ExecCount<T>(DataSourceExpressionBuilder<T> builder, IQueryable<T> source) {
return (int)source.Provider.Execute(builder.BuildCountExpr(source.Expression));
}

static IQueryable<R> AppendExpr<S, R>(IQueryable<S> source, Expression expr, DataSourceLoadOptionsBase options) {
var result = source.Provider.CreateQuery<R>(expr);

#if DEBUG
if(options.UseQueryableOnce)
Expand All @@ -112,7 +117,7 @@ static IQueryable<R> ExecQuery<S, R>(Func<IQueryable<S>, IQueryable<R>> query, I

static RemoteGroupingResult ExecRemoteGrouping<T>(IQueryable<T> source, DataSourceExpressionBuilder<T> builder, DataSourceLoadOptionsBase options) {
return RemoteGroupTransformer.Run(
ExecQuery(builder.BuildLoadGroupsExpr().Compile(), source, options),
AppendExpr<T, AnonType>(source, builder.BuildLoadGroupsExpr(source.Expression), options),
options.HasGroups ? options.Group.Length : 0,
options.TotalSummary,
options.GroupSummary
Expand Down