@@ -36,38 +36,38 @@ await context
36
36
[ ConditionalTheory ]
37
37
[ InlineData ( true ) ]
38
38
[ InlineData ( false ) ]
39
- public Task Ulong_row_version_with_TPH_and_owned_types ( bool updateOwned )
40
- => Row_version_with_owned_types < SuperFan , ulong > ( updateOwned , Mapping . Tph , "ULongVersion" ) ;
39
+ public Task Ulong_row_version_with_TPH_and_owned_types ( bool updateOwnedFirst )
40
+ => Row_version_with_owned_types < SuperFan , ulong > ( updateOwnedFirst , Mapping . Tph , "ULongVersion" ) ;
41
41
42
42
[ ConditionalTheory ]
43
43
[ InlineData ( true ) ]
44
44
[ InlineData ( false ) ]
45
- public Task Ulong_row_version_with_TPT_and_owned_types ( bool updateOwned )
46
- => Row_version_with_owned_types < SuperFanTpt , ulong > ( updateOwned , Mapping . Tpt , "ULongVersion" ) ;
45
+ public Task Ulong_row_version_with_TPT_and_owned_types ( bool updateOwnedFirst )
46
+ => Row_version_with_owned_types < SuperFanTpt , ulong > ( updateOwnedFirst , Mapping . Tpt , "ULongVersion" ) ;
47
47
48
48
[ ConditionalTheory ]
49
49
[ InlineData ( true ) ]
50
50
[ InlineData ( false ) ]
51
- public Task Ulong_row_version_with_TPC_and_owned_types ( bool updateOwned )
52
- => Row_version_with_owned_types < SuperFanTpc , ulong > ( updateOwned , Mapping . Tpc , "ULongVersion" ) ;
51
+ public Task Ulong_row_version_with_TPC_and_owned_types ( bool updateOwnedFirst )
52
+ => Row_version_with_owned_types < SuperFanTpc , ulong > ( updateOwnedFirst , Mapping . Tpc , "ULongVersion" ) ;
53
53
54
54
[ ConditionalTheory ]
55
55
[ InlineData ( true ) ]
56
56
[ InlineData ( false ) ]
57
- public Task Ulong_row_version_with_TPH_and_table_splitting ( bool updateDependent )
58
- => Row_version_with_table_splitting < StreetCircuit , City , ulong > ( updateDependent , Mapping . Tph , "ULongVersion" ) ;
57
+ public Task Ulong_row_version_with_TPH_and_table_splitting ( bool updateDependentFirst )
58
+ => Row_version_with_table_splitting < StreetCircuit , City , ulong > ( updateDependentFirst , Mapping . Tph , "ULongVersion" ) ;
59
59
60
60
[ ConditionalTheory ]
61
61
[ InlineData ( true ) ]
62
62
[ InlineData ( false ) ]
63
- public Task Ulong_row_version_with_TPT_and_table_splitting ( bool updateDependent )
64
- => Row_version_with_table_splitting < StreetCircuitTpt , CityTpt , ulong > ( updateDependent , Mapping . Tpt , "ULongVersion" ) ;
63
+ public Task Ulong_row_version_with_TPT_and_table_splitting ( bool updateDependentFirst )
64
+ => Row_version_with_table_splitting < StreetCircuitTpt , CityTpt , ulong > ( updateDependentFirst , Mapping . Tpt , "ULongVersion" ) ;
65
65
66
66
[ ConditionalTheory ]
67
67
[ InlineData ( true ) ]
68
68
[ InlineData ( false ) ]
69
- public Task Ulong_row_version_with_TPC_and_table_splitting ( bool updateDependent )
70
- => Row_version_with_table_splitting < StreetCircuitTpc , CityTpc , ulong > ( updateDependent , Mapping . Tpc , "ULongVersion" ) ;
69
+ public Task Ulong_row_version_with_TPC_and_table_splitting ( bool updateDependentFirst )
70
+ => Row_version_with_table_splitting < StreetCircuitTpc , CityTpc , ulong > ( updateDependentFirst , Mapping . Tpc , "ULongVersion" ) ;
71
71
}
72
72
73
73
public class OptimisticConcurrencySqlServerTest : OptimisticConcurrencySqlServerTestBase < F1SqlServerFixture , byte [ ] >
@@ -80,38 +80,38 @@ public OptimisticConcurrencySqlServerTest(F1SqlServerFixture fixture)
80
80
[ ConditionalTheory ]
81
81
[ InlineData ( true ) ]
82
82
[ InlineData ( false ) ]
83
- public Task Row_version_with_TPH_and_owned_types ( bool updateOwned )
84
- => Row_version_with_owned_types < SuperFan , List < byte > > ( updateOwned , Mapping . Tph , "BinaryVersion" ) ;
83
+ public Task Row_version_with_TPH_and_owned_types ( bool updateOwnedFirst )
84
+ => Row_version_with_owned_types < SuperFan , List < byte > > ( updateOwnedFirst , Mapping . Tph , "BinaryVersion" ) ;
85
85
86
86
[ ConditionalTheory ]
87
87
[ InlineData ( true ) ]
88
88
[ InlineData ( false ) ]
89
- public Task Row_version_with_TPT_and_owned_types ( bool updateOwned )
90
- => Row_version_with_owned_types < SuperFanTpt , List < byte > > ( updateOwned , Mapping . Tpt , "BinaryVersion" ) ;
89
+ public Task Row_version_with_TPT_and_owned_types ( bool updateOwnedFirst )
90
+ => Row_version_with_owned_types < SuperFanTpt , List < byte > > ( updateOwnedFirst , Mapping . Tpt , "BinaryVersion" ) ;
91
91
92
92
[ ConditionalTheory ]
93
93
[ InlineData ( true ) ]
94
94
[ InlineData ( false ) ]
95
- public Task Row_version_with_TPC_and_owned_types ( bool updateOwned )
96
- => Row_version_with_owned_types < SuperFanTpc , List < byte > > ( updateOwned , Mapping . Tpc , "BinaryVersion" ) ;
95
+ public Task Row_version_with_TPC_and_owned_types ( bool updateOwnedFirst )
96
+ => Row_version_with_owned_types < SuperFanTpc , List < byte > > ( updateOwnedFirst , Mapping . Tpc , "BinaryVersion" ) ;
97
97
98
98
[ ConditionalTheory ]
99
99
[ InlineData ( true ) ]
100
100
[ InlineData ( false ) ]
101
- public Task Ulong_row_version_with_TPH_and_table_splitting ( bool updateDependent )
102
- => Row_version_with_table_splitting < StreetCircuit , City , List < byte > > ( updateDependent , Mapping . Tph , "BinaryVersion" ) ;
101
+ public Task Ulong_row_version_with_TPH_and_table_splitting ( bool updateDependentFirst )
102
+ => Row_version_with_table_splitting < StreetCircuit , City , List < byte > > ( updateDependentFirst , Mapping . Tph , "BinaryVersion" ) ;
103
103
104
104
[ ConditionalTheory ]
105
105
[ InlineData ( true ) ]
106
106
[ InlineData ( false ) ]
107
- public Task Ulong_row_version_with_TPT_and_table_splitting ( bool updateDependent )
108
- => Row_version_with_table_splitting < StreetCircuitTpt , CityTpt , List < byte > > ( updateDependent , Mapping . Tpt , "BinaryVersion" ) ;
107
+ public Task Ulong_row_version_with_TPT_and_table_splitting ( bool updateDependentFirst )
108
+ => Row_version_with_table_splitting < StreetCircuitTpt , CityTpt , List < byte > > ( updateDependentFirst , Mapping . Tpt , "BinaryVersion" ) ;
109
109
110
110
[ ConditionalTheory ]
111
111
[ InlineData ( true ) ]
112
112
[ InlineData ( false ) ]
113
- public Task Ulong_row_version_with_TPC_and_table_splitting ( bool updateDependent )
114
- => Row_version_with_table_splitting < StreetCircuitTpc , CityTpc , List < byte > > ( updateDependent , Mapping . Tpc , "BinaryVersion" ) ;
113
+ public Task Ulong_row_version_with_TPC_and_table_splitting ( bool updateDependentFirst )
114
+ => Row_version_with_table_splitting < StreetCircuitTpc , CityTpc , List < byte > > ( updateDependentFirst , Mapping . Tpc , "BinaryVersion" ) ;
115
115
}
116
116
117
117
public abstract class OptimisticConcurrencySqlServerTestBase < TFixture , TRowVersion >
@@ -130,7 +130,7 @@ protected enum Mapping
130
130
Tpc
131
131
}
132
132
133
- protected async Task Row_version_with_owned_types < TEntity , TVersion > ( bool updateOwned , Mapping mapping , string propertyName )
133
+ protected async Task Row_version_with_owned_types < TEntity , TVersion > ( bool updateOwnedFirst , Mapping mapping , string propertyName )
134
134
where TEntity : class , ISuperFan
135
135
{
136
136
await using var c = CreateF1Context ( ) ;
@@ -146,69 +146,100 @@ await c.Database.CreateExecutionStrategy().ExecuteAsync(
146
146
147
147
var fanEntry = c . Entry ( fan ) ;
148
148
var swagEntry = fanEntry . Reference ( s => s . Swag ) . TargetEntry ! ;
149
- var originalFanVersion = fanEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
150
- var originalSwagVersion = default ( TVersion ) ;
149
+ var fanVersion1 = fanEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
150
+ var swagVersion1 = default ( TVersion ) ;
151
151
152
- if ( mapping == Mapping . Tph )
152
+ if ( mapping == Mapping . Tph ) // Issue #29750
153
153
{
154
- originalSwagVersion
155
- = swagEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
154
+ swagVersion1 = swagEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
156
155
157
- Assert . Equal ( originalFanVersion , originalSwagVersion ) ;
158
- }
159
-
160
- if ( updateOwned )
161
- {
162
- fan . Swag . Stuff += "+" ;
163
- }
164
- else
165
- {
166
- fan . Name += "+" ;
156
+ Assert . Equal ( fanVersion1 , swagVersion1 ) ;
167
157
}
168
158
169
159
await using var innerContext = CreateF1Context ( ) ;
170
160
UseTransaction ( innerContext . Database , transaction ) ;
171
161
var fanInner = innerContext . Set < TEntity > ( ) . Single ( e => e . Name == "Alice" ) ;
172
162
173
- if ( updateOwned )
163
+ if ( updateOwnedFirst )
174
164
{
165
+ fan . Swag . Stuff += "+" ;
175
166
fanInner . Swag . Stuff += "-" ;
176
167
}
177
168
else
178
169
{
179
170
fanInner . Name += "-" ;
171
+ fan . Name += "+" ;
180
172
}
181
173
182
174
await innerContext . SaveChangesAsync ( ) ;
183
175
184
- if ( ! updateOwned || mapping ! = Mapping . Tpt ) // Issue #22060
176
+ if ( updateOwnedFirst && mapping = = Mapping . Tpt ) // Issue #22060
185
177
{
186
- await Assert . ThrowsAnyAsync < DbUpdateConcurrencyException > ( ( ) => context . SaveChangesAsync ( ) ) ;
178
+ await context . SaveChangesAsync ( ) ;
179
+ return ;
180
+ }
187
181
188
- await fanEntry . ReloadAsync ( ) ;
189
- await swagEntry . ReloadAsync ( ) ;
182
+ await Assert . ThrowsAnyAsync < DbUpdateConcurrencyException > ( ( ) => context . SaveChangesAsync ( ) ) ;
190
183
191
- await context . SaveChangesAsync ( ) ;
184
+ await fanEntry . ReloadAsync ( ) ;
185
+ await swagEntry . ReloadAsync ( ) ;
192
186
193
- var fanVersion = fanEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
194
- Assert . NotEqual ( originalFanVersion , fanVersion ) ;
187
+ await context . SaveChangesAsync ( ) ;
195
188
196
- if ( mapping == Mapping . Tph )
197
- {
198
- var swagVersion = swagEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
199
- Assert . Equal ( fanVersion , swagVersion ) ;
200
- Assert . NotEqual ( originalSwagVersion , swagVersion ) ;
201
- }
189
+ var fanVersion2 = fanEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
190
+ Assert . NotEqual ( fanVersion1 , fanVersion2 ) ;
191
+
192
+ var swagVersion2 = default ( TVersion ) ;
193
+ if ( mapping == Mapping . Tph ) // Issue #29750
194
+ {
195
+ swagVersion2 = swagEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
196
+ Assert . Equal ( fanVersion2 , swagVersion2 ) ;
197
+ Assert . NotEqual ( swagVersion1 , swagVersion2 ) ;
198
+ }
199
+
200
+ await innerContext . Entry ( fanInner ) . ReloadAsync ( ) ;
201
+ await innerContext . Entry ( fanInner . Swag ) . ReloadAsync ( ) ;
202
+
203
+ if ( updateOwnedFirst )
204
+ {
205
+ fanInner . Name += "-" ;
206
+ fan . Name += "+" ;
202
207
}
203
208
else
209
+ {
210
+ fan . Swag . Stuff += "+" ;
211
+ fanInner . Swag . Stuff += "-" ;
212
+ }
213
+
214
+ await innerContext . SaveChangesAsync ( ) ;
215
+
216
+ if ( ! updateOwnedFirst && mapping == Mapping . Tpt ) // Issue #22060
204
217
{
205
218
await context . SaveChangesAsync ( ) ;
219
+ return ;
220
+ }
221
+
222
+ await Assert . ThrowsAnyAsync < DbUpdateConcurrencyException > ( ( ) => context . SaveChangesAsync ( ) ) ;
223
+
224
+ await fanEntry . ReloadAsync ( ) ;
225
+ await swagEntry . ReloadAsync ( ) ;
226
+
227
+ await context . SaveChangesAsync ( ) ;
228
+
229
+ var fanVersion3 = fanEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
230
+ Assert . NotEqual ( fanVersion2 , fanVersion3 ) ;
231
+
232
+ if ( mapping == Mapping . Tph ) // Issue #29750
233
+ {
234
+ var swagVersion3 = swagEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
235
+ Assert . Equal ( fanVersion3 , swagVersion3 ) ;
236
+ Assert . NotEqual ( swagVersion2 , swagVersion3 ) ;
206
237
}
207
238
} ) ;
208
239
}
209
240
210
241
protected async Task Row_version_with_table_splitting < TEntity , TCity , TVersion > (
211
- bool updateDependent ,
242
+ bool updateDependentFirst ,
212
243
Mapping mapping ,
213
244
string propertyName )
214
245
where TEntity : class , IStreetCircuit < TCity >
@@ -227,71 +258,100 @@ await c.Database.CreateExecutionStrategy().ExecuteAsync(
227
258
228
259
var circuitEntry = c . Entry ( circuit ) ;
229
260
var cityEntry = circuitEntry . Reference ( s => s . City ) . TargetEntry ! ;
230
- var originalCircuitVersion = circuitEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
231
- var originalCityVersion = default ( TVersion ) ;
261
+ var circuitVersion1 = circuitEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
262
+ var cityVersion1 = default ( TVersion ) ;
232
263
233
- if ( mapping == Mapping . Tph )
264
+ if ( mapping == Mapping . Tph ) // Issue #29750
234
265
{
235
- originalCityVersion
236
- = cityEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
266
+ cityVersion1 = cityEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
237
267
238
- Assert . Equal ( originalCircuitVersion , originalCityVersion ) ;
268
+ Assert . Equal ( circuitVersion1 , cityVersion1 ) ;
239
269
}
240
270
241
- if ( updateDependent )
271
+ await using var innerContext = CreateF1Context ( ) ;
272
+ UseTransaction ( innerContext . Database , transaction ) ;
273
+ var circuitInner = innerContext . Set < TEntity > ( ) . Include ( e => e . City ) . Single ( e => e . Name == "Monaco" ) ;
274
+
275
+ if ( updateDependentFirst )
242
276
{
243
277
circuit . City . Name += "+" ;
278
+ circuitInner . City . Name += "-" ;
244
279
}
245
280
else
246
281
{
247
282
circuit . Name += "+" ;
283
+ circuitInner . Name += "-" ;
248
284
}
249
285
250
- await using var innerContext = CreateF1Context ( ) ;
251
- UseTransaction ( innerContext . Database , transaction ) ;
252
- var fanInner = innerContext . Set < TEntity > ( ) . Include ( e => e . City ) . Single ( e => e . Name == "Monaco" ) ;
253
-
254
- if ( updateDependent )
286
+ if ( mapping == Mapping . Tpc ) // Issue #29751.
255
287
{
256
- fanInner . City . Name += "-" ;
288
+ await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => innerContext . SaveChangesAsync ( ) ) ;
289
+ return ;
257
290
}
258
- else
291
+
292
+ await innerContext . SaveChangesAsync ( ) ;
293
+
294
+ if ( updateDependentFirst && mapping == Mapping . Tpt ) // Issue #22060
259
295
{
260
- fanInner . Name += "-" ;
296
+ await context . SaveChangesAsync ( ) ;
297
+ return ;
261
298
}
262
299
263
- if ( mapping == Mapping . Tpc )
300
+ await Assert . ThrowsAnyAsync < DbUpdateConcurrencyException > ( ( ) => context . SaveChangesAsync ( ) ) ;
301
+
302
+ await circuitEntry . ReloadAsync ( ) ;
303
+ await cityEntry . ReloadAsync ( ) ;
304
+
305
+ await context . SaveChangesAsync ( ) ;
306
+
307
+ var circuitVersion2 = circuitEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
308
+ Assert . NotEqual ( circuitVersion1 , circuitVersion2 ) ;
309
+
310
+ var cityVersion2 = default ( TVersion ) ;
311
+ if ( mapping == Mapping . Tph ) // Issue #29750
264
312
{
265
- await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => innerContext . SaveChangesAsync ( ) ) ;
313
+ cityVersion2 = cityEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
314
+ Assert . Equal ( circuitVersion2 , cityVersion2 ) ;
315
+ Assert . NotEqual ( cityVersion1 , cityVersion2 ) ;
316
+ }
317
+
318
+ await innerContext . Entry ( circuitInner ) . ReloadAsync ( ) ;
319
+ await innerContext . Entry ( circuitInner . City ) . ReloadAsync ( ) ;
266
320
321
+ if ( updateDependentFirst )
322
+ {
323
+ circuit . Name += "+" ;
324
+ circuitInner . Name += "-" ;
267
325
}
268
326
else
269
327
{
270
- await innerContext . SaveChangesAsync ( ) ;
328
+ circuit . City . Name += "+" ;
329
+ circuitInner . City . Name += "-" ;
330
+ }
271
331
272
- if ( ! updateDependent || mapping != Mapping . Tpt ) // Issue #22060
273
- {
274
- await Assert . ThrowsAnyAsync < DbUpdateConcurrencyException > ( ( ) => context . SaveChangesAsync ( ) ) ;
332
+ await innerContext . SaveChangesAsync ( ) ;
275
333
276
- await circuitEntry . ReloadAsync ( ) ;
277
- await cityEntry . ReloadAsync ( ) ;
334
+ if ( ! updateDependentFirst && mapping == Mapping . Tpt ) // Issue #22060
335
+ {
336
+ await context . SaveChangesAsync ( ) ;
337
+ return ;
338
+ }
278
339
279
- await context . SaveChangesAsync ( ) ;
340
+ await Assert . ThrowsAnyAsync < DbUpdateConcurrencyException > ( ( ) => context . SaveChangesAsync ( ) ) ;
280
341
281
- var circuitVersion = circuitEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
282
- Assert . NotEqual ( originalCircuitVersion , circuitVersion ) ;
342
+ await circuitEntry . ReloadAsync ( ) ;
343
+ await cityEntry . ReloadAsync ( ) ;
283
344
284
- if ( mapping == Mapping . Tph )
285
- {
286
- var cityVersion = cityEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
287
- Assert . Equal ( circuitVersion , cityVersion ) ;
288
- Assert . NotEqual ( originalCityVersion , cityVersion ) ;
289
- }
290
- }
291
- else
292
- {
293
- await context . SaveChangesAsync ( ) ;
294
- }
345
+ await context . SaveChangesAsync ( ) ;
346
+
347
+ var circuitVersion3 = circuitEntry . Property < TVersion > ( propertyName ) . CurrentValue ;
348
+ Assert . NotEqual ( circuitVersion2 , circuitVersion3 ) ;
349
+
350
+ if ( mapping == Mapping . Tph ) // Issue #29750
351
+ {
352
+ var cityVersion3 = cityEntry . Property < TVersion > ( synthesizedPropertyName ) . CurrentValue ;
353
+ Assert . Equal ( circuitVersion3 , cityVersion3 ) ;
354
+ Assert . NotEqual ( cityVersion2 , cityVersion3 ) ;
295
355
}
296
356
} ) ;
297
357
}
0 commit comments