Skip to content

Commit 62237bc

Browse files
Fix IQueryable item type detection (#416)
1 parent 3ac4ead commit 62237bc

File tree

3 files changed

+95
-9
lines changed

3 files changed

+95
-9
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.ComponentModel.DataAnnotations;
5+
using System.ComponentModel.DataAnnotations.Schema;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
using Xunit;
9+
10+
namespace DevExtreme.AspNet.Data.Tests.EFCore {
11+
12+
public class Include {
13+
14+
[Table(nameof(Include) + "_" + nameof(Category))]
15+
public class Category {
16+
[Key]
17+
public int CategoryID { get; set; }
18+
public string CategoryName { get; set; }
19+
20+
[InverseProperty(nameof(Include.Product.Category))]
21+
public virtual ICollection<Product> Products { get; set; }
22+
}
23+
24+
[Table(nameof(Include) + "_" + nameof(ProductID))]
25+
public class Product {
26+
[Key]
27+
public int ProductID { get; set; }
28+
public string ProductName { get; set; }
29+
public decimal UnitPrice { get; set; }
30+
31+
public int? CategoryID { get; set; }
32+
33+
[ForeignKey(nameof(CategoryID))]
34+
[InverseProperty(nameof(Include.Category.Products))]
35+
public virtual Category Category { get; set; }
36+
}
37+
38+
[Fact]
39+
public async Task Scenario() {
40+
await TestDbContext.ExecAsync(context => {
41+
var categories = context.Set<Category>();
42+
var products = context.Set<Product>();
43+
44+
var beverages = new Category {
45+
CategoryName = "Beverages"
46+
};
47+
48+
var chai = new Product {
49+
ProductName = "Chai",
50+
UnitPrice = 18,
51+
Category = beverages
52+
};
53+
54+
categories.Add(beverages);
55+
products.Add(chai);
56+
57+
context.SaveChanges();
58+
59+
Assert.Null(Record.Exception(delegate {
60+
var loadResult = DataSourceLoader.Load(products.Include(p => p.Category), new SampleLoadOptions {
61+
Filter = new[] { "UnitPrice", ">", "15" }
62+
});
63+
64+
loadResult.data.Cast<object>().ToArray();
65+
}));
66+
});
67+
}
68+
69+
}
70+
71+
}

net/DevExtreme.AspNet.Data.Tests.EFCore/TestDbContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) {
2626
#if !EFCORE1 && !EFCORE2
2727
modelBuilder.Entity<ExpandLinqSumType.DataItem>();
2828
#endif
29+
30+
modelBuilder.Entity<Include.Category>();
31+
modelBuilder.Entity<Include.Product>();
2932
}
3033

3134
public static async Task ExecAsync(Func<TestDbContext, Task> action) {

net/DevExtreme.AspNet.Data/DataSourceExpressionBuilder.cs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ public DataSourceExpressionBuilder(Expression expr, DataSourceLoadContext contex
1616
Context = context;
1717
}
1818

19-
Type ItemType => Expr.Type.GenericTypeArguments[0];
20-
2119
public Expression BuildLoadExpr(bool paginate, IList filterOverride = null, IReadOnlyList<string> selectOverride = null) {
2220
AddFilter(filterOverride);
2321
AddSort();
@@ -52,16 +50,16 @@ public Expression BuildGroupCountExpr() {
5250
void AddFilter(IList filterOverride = null) {
5351
if(filterOverride != null || Context.HasFilter) {
5452
var filterExpr = filterOverride != null && filterOverride.Count < 1
55-
? Expression.Lambda(Expression.Constant(false), Expression.Parameter(ItemType))
56-
: new FilterExpressionCompiler(ItemType, Context.GuardNulls, Context.UseStringToLower).Compile(filterOverride ?? Context.Filter);
53+
? Expression.Lambda(Expression.Constant(false), Expression.Parameter(GetItemType()))
54+
: new FilterExpressionCompiler(GetItemType(), Context.GuardNulls, Context.UseStringToLower).Compile(filterOverride ?? Context.Filter);
5755

5856
Expr = QueryableCall(nameof(Queryable.Where), Expression.Quote(filterExpr));
5957
}
6058
}
6159

6260
void AddSort() {
6361
if(Context.HasAnySort)
64-
Expr = new SortExpressionCompiler(ItemType, Context.GuardNulls).Compile(Expr, Context.GetFullSort());
62+
Expr = new SortExpressionCompiler(GetItemType(), Context.GuardNulls).Compile(Expr, Context.GetFullSort());
6563
}
6664

6765
void AddSelect(IReadOnlyList<string> selectOverride = null) {
@@ -79,7 +77,7 @@ void AddPaging() {
7977

8078
void AddRemoteGrouping(bool suppressGroups, bool suppressTotals) {
8179
var compiler = new RemoteGroupExpressionCompiler(
82-
ItemType, Context.GuardNulls, Context.ExpandLinqSumType, Context.CreateAnonTypeNewTweaks(),
80+
GetItemType(), Context.GuardNulls, Context.ExpandLinqSumType, Context.CreateAnonTypeNewTweaks(),
8381
suppressGroups ? null : Context.Group,
8482
suppressTotals ? null : Context.TotalSummary,
8583
suppressGroups ? null : Context.GroupSummary
@@ -92,13 +90,27 @@ void AddCount() {
9290
}
9391

9492
SelectExpressionCompiler CreateSelectCompiler()
95-
=> new SelectExpressionCompiler(ItemType, Context.GuardNulls, Context.CreateAnonTypeNewTweaks());
93+
=> new SelectExpressionCompiler(GetItemType(), Context.GuardNulls, Context.CreateAnonTypeNewTweaks());
9694

9795
Expression QueryableCall(string methodName)
98-
=> Expression.Call(typeof(Queryable), methodName, Expr.Type.GenericTypeArguments, Expr);
96+
=> Expression.Call(typeof(Queryable), methodName, GetQueryableGenericArguments(), Expr);
9997

10098
Expression QueryableCall(string methodName, Expression arg)
101-
=> Expression.Call(typeof(Queryable), methodName, Expr.Type.GenericTypeArguments, Expr, arg);
99+
=> Expression.Call(typeof(Queryable), methodName, GetQueryableGenericArguments(), Expr, arg);
100+
101+
Type[] GetQueryableGenericArguments() {
102+
const string queryable1 = "IQueryable`1";
103+
var type = Expr.Type;
104+
105+
if(type.IsInterface && type.Name == queryable1)
106+
return type.GenericTypeArguments;
107+
108+
return type.GetInterface(queryable1).GenericTypeArguments;
109+
}
110+
111+
Type GetItemType()
112+
=> GetQueryableGenericArguments().First();
113+
102114
}
103115

104116
}

0 commit comments

Comments
 (0)