@@ -5035,6 +5035,60 @@ public virtual void Setting_navigation_to_null_is_detected_by_local_DetectChange
5035
5035
Assert . Equal ( EntityState . Deleted , childEntry . State ) ;
5036
5036
}
5037
5037
5038
+ [ ConditionalTheory ] // Issue #35528
5039
+ [ InlineData ( false , false ) ]
5040
+ [ InlineData ( true , false ) ]
5041
+ [ InlineData ( false , true ) ]
5042
+ [ InlineData ( true , true ) ]
5043
+ public virtual async Task Lazy_loading_is_thread_safe( bool noTracking , bool async )
5044
+ {
5045
+ using var context = CreateContext( lazyLoadingEnabled : true) ;
5046
+
5047
+ //Creating another context to avoid caches
5048
+ using var context2 = CreateContext ( lazyLoadingEnabled : true) ;
5049
+
5050
+ IQueryable < Parent > query = context . Set < Parent > ( ) ;
5051
+ IQueryable < Parent > query2 = context2 . Set < Parent > ( ) ;
5052
+
5053
+ if ( noTracking )
5054
+ {
5055
+ query = query . AsNoTracking ( ) ;
5056
+ query2 = query2 . AsNoTracking ( ) ;
5057
+ }
5058
+
5059
+ var parent = query . Single ( ) ;
5060
+
5061
+ var children = ( await parent . LazyLoadChildren( async ) ) ? . Select ( x => x . Id ) . OrderBy( x => x) . ToList ( ) ;
5062
+ var singlePkToPk = ( await parent . LazyLoadSinglePkToPk( async ) ) ? . Id ;
5063
+ var single = ( await parent . LazyLoadSingle ( async ) ) ? . Id;
5064
+ var childrenAk = ( await parent . LazyLoadChildrenAk( async ) ) ? . Select ( x => x . Id ) . OrderBy( x => x) . ToList ( ) ;
5065
+ var singleAk = ( await parent . LazyLoadSingleAk( async ) ) ? . Id ;
5066
+ var childrenShadowFk = ( await parent . LazyLoadChildrenShadowFk ( async ) ) ? . Select( x => x . Id ) . OrderBy ( x => x ) . ToList ( ) ;
5067
+ var singleShadowFk = ( await parent . LazyLoadSingleShadowFk ( async ) ) ? . Id ;
5068
+ var childrenCompositeKey = ( await parent . LazyLoadChildrenCompositeKey ( async ) ) ? . Select ( x => x . Id ) . OrderBy ( x => x ) . ToList ( ) ;
5069
+ var singleCompositeKey = ( await parent . LazyLoadSingleCompositeKey ( async ) ) ? . Id ;
5070
+
5071
+ var parent2 = query2. Single ( ) ;
5072
+
5073
+ var parallelOptions = new ParallelOptions
5074
+ {
5075
+ MaxDegreeOfParallelism = Environment . ProcessorCount * 500
5076
+ } ;
5077
+
5078
+ await Parallel. ForAsync ( 0 , 50000 , parallelOptions , async ( i , ct ) =>
5079
+ {
5080
+ Assert . Equal ( children , ( await parent2 . LazyLoadChildren ( async ) ) ? . Select( x => x . Id ) . OrderBy ( x => x ) . ToList ( ) ) ;
5081
+ Assert . Equal ( singlePkToPk , ( await parent2 . LazyLoadSinglePkToPk ( async ) ) ? . Id) ;
5082
+ Assert. Equal( single, ( await parent2 . LazyLoadSingle( async ) ) ? . Id) ;
5083
+ Assert. Equal( childrenAk , ( await parent2 . LazyLoadChildrenAk( async ) ) ? . Select( x => x. Id) . OrderBy( x => x) . ToList( ) ) ;
5084
+ Assert . Equal ( singleAk , ( await parent2 . LazyLoadSingleAk( async ) ) ? . Id) ;
5085
+ Assert . Equal ( childrenShadowFk , ( await parent2 . LazyLoadChildrenShadowFk( async ) ) ? . Select( x => x. Id) . OrderBy( x => x) . ToList( ) ) ;
5086
+ Assert . Equal ( singleShadowFk , ( await parent2 . LazyLoadSingleShadowFk( async ) ) ? . Id) ;
5087
+ Assert . Equal ( childrenCompositeKey , ( await parent2 . LazyLoadChildrenCompositeKey( async ) ) ? . Select( x => x. Id) . OrderBy( x => x) . ToList( ) ) ;
5088
+ Assert . Equal ( singleCompositeKey , ( await parent2 . LazyLoadSingleCompositeKey( async ) ) ? . Id) ;
5089
+ } ) ;
5090
+ }
5091
+
5038
5092
private static void SetState (
5039
5093
DbContext context ,
5040
5094
object entity ,
@@ -5092,6 +5146,17 @@ public SinglePkToPk SinglePkToPk
5092
5146
set => _singlePkToPk = value ;
5093
5147
}
5094
5148
5149
+ public async Task < SinglePkToPk > LazyLoadSinglePkToPk ( bool async )
5150
+ {
5151
+ if ( async )
5152
+ {
5153
+ await Loader . LoadAsync ( this , default , nameof ( SinglePkToPk ) ) ;
5154
+ return _singlePkToPk ;
5155
+ }
5156
+
5157
+ return SinglePkToPk ;
5158
+ }
5159
+
5095
5160
public Single Single
5096
5161
{
5097
5162
get => Loader . Load ( this , ref _single ) ;
@@ -5121,35 +5186,101 @@ public IEnumerable<ChildAk> ChildrenAk
5121
5186
set => _childrenAk = value ;
5122
5187
}
5123
5188
5189
+ public async Task < IEnumerable < ChildAk >> LazyLoadChildrenAk ( bool async )
5190
+ {
5191
+ if ( async )
5192
+ {
5193
+ await Loader . LoadAsync ( this , default , nameof ( ChildrenAk ) ) ;
5194
+ return _childrenAk ;
5195
+ }
5196
+
5197
+ return ChildrenAk ;
5198
+ }
5199
+
5124
5200
public SingleAk SingleAk
5125
5201
{
5126
5202
get => Loader . Load ( this , ref _singleAk ) ;
5127
5203
set => _singleAk = value ;
5128
5204
}
5129
5205
5206
+ public async Task < SingleAk > LazyLoadSingleAk ( bool async )
5207
+ {
5208
+ if ( async )
5209
+ {
5210
+ await Loader . LoadAsync ( this , default , nameof ( SingleAk ) ) ;
5211
+ return _singleAk ;
5212
+ }
5213
+
5214
+ return SingleAk ;
5215
+ }
5216
+
5130
5217
public IEnumerable < ChildShadowFk > ChildrenShadowFk
5131
5218
{
5132
5219
get => Loader . Load ( this , ref _childrenShadowFk ) ;
5133
5220
set => _childrenShadowFk = value ;
5134
5221
}
5135
5222
5223
+ public async Task < IEnumerable < ChildShadowFk >> LazyLoadChildrenShadowFk ( bool async )
5224
+ {
5225
+ if ( async )
5226
+ {
5227
+ await Loader . LoadAsync ( this , default , nameof ( ChildrenShadowFk ) ) ;
5228
+ return _childrenShadowFk ;
5229
+ }
5230
+
5231
+ return ChildrenShadowFk ;
5232
+ }
5233
+
5136
5234
public SingleShadowFk SingleShadowFk
5137
5235
{
5138
5236
get => Loader . Load ( this , ref _singleShadowFk ) ;
5139
5237
set => _singleShadowFk = value ;
5140
5238
}
5141
5239
5240
+ public async Task < SingleShadowFk > LazyLoadSingleShadowFk ( bool async )
5241
+ {
5242
+ if ( async )
5243
+ {
5244
+ await Loader . LoadAsync ( this , default , nameof ( SingleShadowFk ) ) ;
5245
+ return _singleShadowFk ;
5246
+ }
5247
+
5248
+ return SingleShadowFk ;
5249
+ }
5250
+
5142
5251
public IEnumerable < ChildCompositeKey > ChildrenCompositeKey
5143
5252
{
5144
5253
get => Loader . Load ( this , ref _childrenCompositeKey ) ;
5145
5254
set => _childrenCompositeKey = value ;
5146
5255
}
5147
5256
5257
+ public async Task < IEnumerable < ChildCompositeKey >> LazyLoadChildrenCompositeKey ( bool async )
5258
+ {
5259
+ if ( async )
5260
+ {
5261
+ await Loader . LoadAsync ( this , default , nameof ( ChildrenCompositeKey ) ) ;
5262
+ return _childrenCompositeKey ;
5263
+ }
5264
+
5265
+ return ChildrenCompositeKey ;
5266
+ }
5267
+
5148
5268
public SingleCompositeKey SingleCompositeKey
5149
5269
{
5150
5270
get => Loader . Load ( this , ref _singleCompositeKey ) ;
5151
5271
set => _singleCompositeKey = value ;
5152
5272
}
5273
+
5274
+ public async Task < SingleCompositeKey > LazyLoadSingleCompositeKey ( bool async )
5275
+ {
5276
+ if ( async )
5277
+ {
5278
+ await Loader . LoadAsync ( this , default , nameof ( SingleCompositeKey ) ) ;
5279
+ return _singleCompositeKey ;
5280
+ }
5281
+
5282
+ return SingleCompositeKey ;
5283
+ }
5153
5284
}
5154
5285
5155
5286
protected class Child
0 commit comments