10
10
using System . Collections . Generic ;
11
11
using System . ComponentModel ;
12
12
using System . ComponentModel . DataAnnotations ;
13
+ using System . ComponentModel . DataAnnotations . Schema ;
13
14
using System . Linq ;
14
15
using System . Linq . Expressions ;
15
16
using System . Reflection ;
@@ -36,6 +37,7 @@ public class SelectExpandBinderTest
36
37
private readonly SelectExpandBinder _binder ;
37
38
private readonly SelectExpandBinder _binder_lowerCamelCased ;
38
39
private readonly IQueryable < QueryCustomer > _queryable ;
40
+ private readonly IQueryable < QueryCustomer > _queryable1 ;
39
41
private readonly IQueryable < QueryCustomer > _queryable_lowerCamelCased ;
40
42
private readonly ODataQueryContext _context ;
41
43
private readonly ODataQueryContext _context_lowerCamelCased ;
@@ -82,7 +84,19 @@ public SelectExpandBinderTest()
82
84
QueryOrder order = new QueryOrder { Id = 42 , Title = "The order" , Customer = customer } ;
83
85
customer . Orders . Add ( order ) ;
84
86
87
+ QueryCustomer customer1 = new QueryCustomer
88
+ {
89
+ Orders = new List < QueryOrder > ( )
90
+ } ;
91
+
92
+ QueryOrder order1 = new QueryOrder { Id = 42 , Title = "The order" , Customer = customer1 } ;
93
+ QueryOrder order2 = null ;
94
+
95
+ customer1 . Orders . Add ( order1 ) ;
96
+ customer1 . Orders . Add ( order2 ) ;
97
+
85
98
_queryable = new [ ] { customer } . AsQueryable ( ) ;
99
+ _queryable1 = new [ ] { customer1 } . AsQueryable ( ) ;
86
100
87
101
SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption ( "Orders" , expand : null , context : _context ) ;
88
102
@@ -257,6 +271,138 @@ public void Bind_GeneratedExpression_ContainsExpandedObject()
257
271
Assert . Same ( _queryable . First ( ) , innerInnerCustomer . Instance ) ;
258
272
}
259
273
274
+ [ Theory ]
275
+ [ InlineData ( HandleNullPropagationOptionHelper . EntityFrameworkQueryProviderNamespace ) ]
276
+ [ InlineData ( HandleNullPropagationOptionHelper . ObjectContextQueryProviderNamespaceEFCore2 ) ]
277
+ [ InlineData ( HandleNullPropagationOptionHelper . ObjectContextQueryProviderNamespaceEF5 ) ]
278
+ [ InlineData ( HandleNullPropagationOptionHelper . ObjectContextQueryProviderNamespaceEF6 ) ]
279
+ public void Bind_UsingEFQueryProvider_GeneratedExpression__DoesNot_ContainExpandedObject ( string queryProvider )
280
+ {
281
+ // Arrange
282
+ SelectExpandQueryOption selectExpand = new SelectExpandQueryOption ( "Orders" , "Orders,Orders($expand=Customer)" , _context ) ;
283
+
284
+ // Act
285
+ SelectExpandBinder binder = new SelectExpandBinder ( ) ;
286
+ _queryBinderContext . QueryProvider = queryProvider ;
287
+ IQueryable queryable = binder . ApplyBind ( _queryable1 , selectExpand . SelectExpandClause , _queryBinderContext ) ;
288
+
289
+ // Assert
290
+ IEnumerator enumerator = queryable . GetEnumerator ( ) ;
291
+ Assert . True ( enumerator . MoveNext ( ) ) ;
292
+ var partialCustomer = Assert . IsAssignableFrom < SelectExpandWrapper < QueryCustomer > > ( enumerator . Current ) ;
293
+ Assert . False ( enumerator . MoveNext ( ) ) ;
294
+ Assert . Null ( partialCustomer . Instance ) ;
295
+ Assert . Equal ( "Microsoft.AspNetCore.OData.Tests.Query.Expressions.QueryCustomer" , partialCustomer . InstanceType ) ;
296
+ IEnumerable < SelectExpandWrapper < QueryOrder > > innerOrders = partialCustomer . Container
297
+ . ToDictionary ( PropertyMapper ) [ "Orders" ] as IEnumerable < SelectExpandWrapper < QueryOrder > > ;
298
+ Assert . NotNull ( innerOrders ) ;
299
+ SelectExpandWrapper < QueryOrder > partialOrder = innerOrders . FirstOrDefault ( ) ;
300
+
301
+ // We only write structural properties to the instance.
302
+ // This means that navigation properties on the instance property will be null
303
+ // when using any instance of EF query provider, and all structural properties
304
+ // will be assigned.
305
+ Assert . Null ( partialOrder . Instance . Customer ) ;
306
+ Assert . NotEqual ( 0 , partialOrder . Instance . Id ) ;
307
+ Assert . NotNull ( partialOrder . Instance . Title ) ;
308
+
309
+ object customer = partialOrder . Container . ToDictionary ( PropertyMapper ) [ "Customer" ] ;
310
+ SelectExpandWrapper < QueryCustomer > innerInnerCustomer = Assert . IsAssignableFrom < SelectExpandWrapper < QueryCustomer > > ( customer ) ;
311
+
312
+ Assert . Null ( innerInnerCustomer . Instance . Orders ) ;
313
+ Assert . Equal ( 2 , innerInnerCustomer . Instance . TestReadonlyProperty . Count ) ;
314
+ Assert . Equal ( "Test1" , innerInnerCustomer . Instance . TestReadonlyProperty [ 0 ] ) ;
315
+ Assert . Equal ( "Test2" , innerInnerCustomer . Instance . TestReadonlyProperty [ 1 ] ) ;
316
+ Assert . Equal ( 2 , innerInnerCustomer . Instance . TestReadOnlyWithAttribute ) ;
317
+ }
318
+
319
+ [ Theory ]
320
+ [ InlineData ( HandleNullPropagationOptionHelper . EntityFrameworkQueryProviderNamespace ) ]
321
+ [ InlineData ( HandleNullPropagationOptionHelper . ObjectContextQueryProviderNamespaceEFCore2 ) ]
322
+ [ InlineData ( HandleNullPropagationOptionHelper . ObjectContextQueryProviderNamespaceEF5 ) ]
323
+ [ InlineData ( HandleNullPropagationOptionHelper . ObjectContextQueryProviderNamespaceEF6 ) ]
324
+ public void Bind_UsingEFQueryProvider_LowerCamelCasedModel_GeneratedExpression__DoesNot_ContainExpandedObject ( string queryProvider )
325
+ {
326
+ // Arrange
327
+ SelectExpandQueryOption selectExpand = new SelectExpandQueryOption ( "Orders" , "Orders,Orders($expand=Customer)" , _context_lowerCamelCased ) ;
328
+
329
+ // Act
330
+ SelectExpandBinder binder = new SelectExpandBinder ( ) ;
331
+ _queryBinderContext_lowerCamelCased . QueryProvider = queryProvider ;
332
+ IQueryable queryable = binder . ApplyBind ( _queryable_lowerCamelCased , selectExpand . SelectExpandClause , _queryBinderContext_lowerCamelCased ) ;
333
+
334
+ // Assert
335
+ IEnumerator enumerator = queryable . GetEnumerator ( ) ;
336
+ Assert . True ( enumerator . MoveNext ( ) ) ;
337
+ var partialCustomer = Assert . IsAssignableFrom < SelectExpandWrapper < QueryCustomer > > ( enumerator . Current ) ;
338
+ Assert . False ( enumerator . MoveNext ( ) ) ;
339
+ Assert . Null ( partialCustomer . Instance ) ;
340
+ Assert . Equal ( "Microsoft.AspNetCore.OData.Tests.Query.Expressions.QueryCustomer" , partialCustomer . InstanceType ) ;
341
+ IEnumerable < SelectExpandWrapper < QueryOrder > > innerOrders = partialCustomer . Container
342
+ . ToDictionary ( PropertyMapper ) [ "orders" ] as IEnumerable < SelectExpandWrapper < QueryOrder > > ;
343
+ Assert . NotNull ( innerOrders ) ;
344
+ SelectExpandWrapper < QueryOrder > partialOrder = innerOrders . FirstOrDefault ( ) ;
345
+
346
+ // We only write structural properties to the instance.
347
+ // This means that navigation properties on the instance property will be null
348
+ // when using any instance of EF query provider, and all structural properties
349
+ // will be assigned.
350
+ Assert . Null ( partialOrder . Instance . Customer ) ;
351
+ Assert . NotEqual ( 0 , partialOrder . Instance . Id ) ;
352
+ Assert . NotNull ( partialOrder . Instance . Title ) ;
353
+
354
+ object customer = partialOrder . Container . ToDictionary ( PropertyMapper ) [ "customer" ] ;
355
+ SelectExpandWrapper < QueryCustomer > innerInnerCustomer = Assert . IsAssignableFrom < SelectExpandWrapper < QueryCustomer > > ( customer ) ;
356
+
357
+ Assert . Null ( innerInnerCustomer . Instance . Orders ) ;
358
+ }
359
+
360
+ [ Fact ]
361
+ public void Bind_SelectAndExpand_WithNullProperties_DoesNotThrowException ( )
362
+ {
363
+ // Arrange
364
+ IQueryable < User > users ;
365
+ string expand = "FileRefNavigation" ;
366
+
367
+ User user = new User
368
+ {
369
+ UserId = 1 ,
370
+ Name = "Alex" ,
371
+ Age = 35 ,
372
+ DataFileRef = null ,
373
+ FileRefNavigation = null
374
+ } ;
375
+
376
+ users = new [ ] { user } . AsQueryable ( ) ;
377
+ ODataQueryContext context = new ODataQueryContext ( _model , typeof ( User ) ) { RequestContainer = new MockServiceProvider ( ) } ;
378
+
379
+ SelectExpandQueryOption selectExpand = new SelectExpandQueryOption ( select : null , expand : expand , context : context ) ;
380
+
381
+ QueryBinderContext queryBinderContext = new QueryBinderContext ( _model , _settings , selectExpand . Context . ElementClrType )
382
+ {
383
+ NavigationSource = context . NavigationSource
384
+ } ;
385
+
386
+ queryBinderContext . QueryProvider = HandleNullPropagationOptionHelper . EntityFrameworkQueryProviderNamespace ;
387
+
388
+ // Act
389
+ SelectExpandBinder binder = new SelectExpandBinder ( ) ;
390
+ IQueryable queryable = binder . ApplyBind ( users , selectExpand . SelectExpandClause , queryBinderContext ) ;
391
+
392
+ // Assert
393
+ Assert . NotNull ( queryable ) ;
394
+
395
+ IEnumerator enumerator = queryable . GetEnumerator ( ) ;
396
+ Assert . True ( enumerator . MoveNext ( ) ) ;
397
+ var usr = Assert . IsAssignableFrom < SelectExpandWrapper < User > > ( enumerator . Current ) ;
398
+ Assert . False ( enumerator . MoveNext ( ) ) ;
399
+ Assert . NotNull ( usr . Instance ) ;
400
+ Assert . Equal ( "Microsoft.AspNetCore.OData.Tests.Query.Expressions.User" , usr . Instance . GetType ( ) . ToString ( ) ) ;
401
+ IEnumerable < SelectExpandWrapper < DataFile > > fileRefsNavigations = usr . Container
402
+ . ToDictionary ( PropertyMapper ) [ "FileRefNavigation" ] as IEnumerable < SelectExpandWrapper < DataFile > > ;
403
+ Assert . Null ( fileRefsNavigations ) ;
404
+ }
405
+
260
406
[ Fact ]
261
407
public void Bind_GeneratedExpression_CheckNullObjectWithinChainProjectionByKey ( )
262
408
{
@@ -1996,6 +2142,7 @@ public static IEdmModel GetEdmModel()
1996
2142
builder . EntitySet < QueryOrder > ( "Orders" ) ;
1997
2143
builder . EntitySet < QueryCity > ( "Cities" ) ;
1998
2144
builder . EntitySet < QueryProduct > ( "Products" ) ;
2145
+ builder . EntitySet < User > ( "Users" ) ;
1999
2146
2000
2147
customer . Collection . Function ( "IsUpgraded" ) . Returns < bool > ( ) . Namespace = "NS" ;
2001
2148
customer . Collection . Action ( "UpgradeAll" ) . Namespace = "NS" ;
@@ -2143,9 +2290,9 @@ public class QueryCustomer
2143
2290
2144
2291
public IList < QueryAddress > Addresses { get ; set ; }
2145
2292
2146
- public QueryOrder PrivateOrder { get ; set ; }
2293
+ public QueryOrder ? PrivateOrder { get ; set ; }
2147
2294
2148
- public IList < QueryOrder > Orders { get ; set ; }
2295
+ public IList < QueryOrder > ? Orders { get ; set ; }
2149
2296
2150
2297
public List < string > TestReadonlyProperty
2151
2298
{
@@ -2206,4 +2353,27 @@ public enum QueryColor
2206
2353
2207
2354
Blue
2208
2355
}
2356
+
2357
+ public class User
2358
+ {
2359
+ [ Key ]
2360
+ public int UserId { get ; set ; }
2361
+ [ Required ]
2362
+ public string Name { get ; set ; }
2363
+ [ Required ]
2364
+ public int Age { get ; set ; }
2365
+
2366
+ //Navigations
2367
+ [ ForeignKey ( "FileRefNavigation" ) ]
2368
+ public int ? DataFileRef { get ; set ; }
2369
+ public DataFile ? FileRefNavigation { get ; set ; }
2370
+ }
2371
+
2372
+ public class DataFile
2373
+ {
2374
+ [ Key ]
2375
+ public int FileId { get ; set ; }
2376
+ [ Required ]
2377
+ public string FileName { get ; set ; }
2378
+ }
2209
2379
}
0 commit comments