44using Microsoft . EntityFrameworkCore ;
55using MySql . EntityFrameworkCore . Extensions ;
66using System ;
7+ using System . Linq ;
78using System . Threading . Tasks ;
89using Xunit ;
910
@@ -21,7 +22,8 @@ protected DatabaseTests(DemoContext demoContext, SameNameIndexesContext sameName
2122 DemoContext = demoContext ;
2223 SameNameIndexesContext = sameNameIndexesContext ;
2324
24- isMySql = MySqlDatabaseFacadeExtensions . IsMySql ( DemoContext . Database ) || MySQLDatabaseFacadeExtensions . IsMySql ( DemoContext . Database ) ;
25+ isMySql = MySqlDatabaseFacadeExtensions . IsMySql ( DemoContext . Database ) ||
26+ MySQLDatabaseFacadeExtensions . IsMySql ( DemoContext . Database ) ;
2527 isSqlite = demoContext . Database . IsSqlite ( ) ;
2628 }
2729
@@ -43,6 +45,24 @@ public virtual async Task UniqueColumnViolationThrowsUniqueConstraintException()
4345 }
4446 }
4547
48+ #if BULK_OPERATIONS
49+ [ Fact ]
50+ public virtual async Task UniqueColumnViolationThrowsUniqueConstraintExceptionThroughExecuteUpdate ( )
51+ {
52+ DemoContext . Products . Add ( new Product { Name = "Bulk Update 1" } ) ;
53+ DemoContext . Products . Add ( new Product { Name = "Bulk Update 2" } ) ;
54+
55+ await DemoContext . SaveChangesAsync ( ) ;
56+ Assert . Throws < UniqueConstraintException > ( ( ) =>
57+ DemoContext . Products . ExecuteUpdate ( p => p . SetProperty ( pp => pp . Name , "Bulk Update 1" ) ) ) ;
58+ await Assert . ThrowsAsync < UniqueConstraintException > ( async ( ) =>
59+ await DemoContext . Products . ExecuteUpdateAsync ( p => p . SetProperty ( pp => pp . Name , "Bulk Update 1" ) ) ) ;
60+ await DemoContext . Products
61+ . Where ( p => p . Name == "Bulk Update 1" || p . Name == "Bulk Update 2" )
62+ . ExecuteDeleteAsync ( ) ;
63+ }
64+ #endif
65+
4666 [ Fact ]
4767 public virtual async Task UniqueColumnViolationSameNamesIndexesInDifferentSchemasSetsCorrectTableName ( )
4868 {
@@ -56,13 +76,14 @@ public virtual async Task UniqueColumnViolationSameNamesIndexesInDifferentSchema
5676 {
5777 Name = "Rope Access"
5878 } ) ;
59-
79+
6080 SameNameIndexesContext . IncidentCategories . Add ( new EFExceptionSchema . Entities . Incidents . Category
6181 {
6282 Name = "Rope Access"
6383 } ) ;
6484
65- var uniqueConstraintException = Assert . Throws < UniqueConstraintException > ( ( ) => SameNameIndexesContext . SaveChanges ( ) ) ;
85+ var uniqueConstraintException =
86+ Assert . Throws < UniqueConstraintException > ( ( ) => SameNameIndexesContext . SaveChanges ( ) ) ;
6687 await Assert . ThrowsAsync < UniqueConstraintException > ( ( ) => SameNameIndexesContext . SaveChangesAsync ( ) ) ;
6788
6889 if ( ! isSqlite )
@@ -94,7 +115,7 @@ public virtual async Task PrimaryKeyViolationThrowsUniqueConstraintException()
94115 Assert . False ( string . IsNullOrEmpty ( uniqueConstraintException . ConstraintName ) ) ;
95116 Assert . False ( string . IsNullOrEmpty ( uniqueConstraintException . SchemaQualifiedTableName ) ) ;
96117 Assert . NotEmpty ( uniqueConstraintException . ConstraintProperties ) ;
97- Assert . Contains < string > ( nameof ( Product . Id ) , uniqueConstraintException . ConstraintProperties ) ;
118+ Assert . Contains < string > ( nameof ( Product . Id ) , uniqueConstraintException . ConstraintProperties ) ;
98119 }
99120 }
100121
@@ -107,6 +128,21 @@ public virtual async Task RequiredColumnViolationThrowsCannotInsertNullException
107128 await Assert . ThrowsAsync < CannotInsertNullException > ( ( ) => DemoContext . SaveChangesAsync ( ) ) ;
108129 }
109130
131+ #if BULK_OPERATIONS
132+ [ Fact ]
133+ public virtual async Task RequiredColumnViolationThrowsCannotInsertNullExceptionThroughExecuteUpdate ( )
134+ {
135+ DemoContext . Products . Add ( new Product { Name = "Bulk Update 1" } ) ;
136+ await DemoContext . SaveChangesAsync ( ) ;
137+
138+ Assert . Throws < CannotInsertNullException > ( ( ) =>
139+ DemoContext . Products . ExecuteUpdate ( p => p . SetProperty ( pp => pp . Name , ( string ) null ) ) ) ;
140+ await Assert . ThrowsAsync < CannotInsertNullException > ( async ( ) =>
141+ await DemoContext . Products . ExecuteUpdateAsync ( p => p . SetProperty ( pp => pp . Name , ( string ) null ) ) ) ;
142+ await DemoContext . Products . Where ( p => p . Name == "Bulk Update 1" ) . ExecuteDeleteAsync ( ) ;
143+ }
144+ #endif
145+
110146 [ Fact ]
111147 public virtual async Task MaxLengthViolationThrowsMaxLengthExceededException ( )
112148 {
@@ -116,6 +152,42 @@ public virtual async Task MaxLengthViolationThrowsMaxLengthExceededException()
116152 await Assert . ThrowsAsync < MaxLengthExceededException > ( ( ) => DemoContext . SaveChangesAsync ( ) ) ;
117153 }
118154
155+ #if BULK_OPERATIONS
156+ [ Fact ]
157+ public virtual async Task MaxLengthViolationThrowsMaxLengthExceededExceptionThroughExecuteUpdate ( )
158+ {
159+ DemoContext . Products . Add ( new Product { Name = "Bulk Update 1" } ) ;
160+ await DemoContext . SaveChangesAsync ( ) ;
161+
162+ CleanupContext ( ) ;
163+
164+ Assert . Throws < MaxLengthExceededException > ( Query ) ;
165+ await Assert . ThrowsAsync < MaxLengthExceededException > ( QueryAsync ) ;
166+
167+ await DemoContext . Products
168+ . Where ( p => p . Name == "Bulk Update 1" )
169+ . ExecuteDeleteAsync ( ) ;
170+
171+ return ;
172+
173+ void Query ( )
174+ {
175+ DemoContext . Products
176+ . Where ( p => p . Name == "Bulk Update 1" )
177+ . ExecuteUpdate ( p =>
178+ p . SetProperty ( pp => pp . Name , new string ( 'G' , DemoContext . ProductNameMaxLength + 5 ) ) ) ;
179+ }
180+
181+ async Task QueryAsync ( )
182+ {
183+ await DemoContext . Products
184+ . Where ( p => p . Name == "Bulk Update 1" )
185+ . ExecuteUpdateAsync ( p =>
186+ p . SetProperty ( pp => pp . Name , new string ( 'G' , DemoContext . ProductNameMaxLength + 5 ) ) ) ;
187+ }
188+ }
189+ #endif
190+
119191 [ Fact ]
120192 public virtual async Task NumericOverflowViolationThrowsNumericOverflowException ( )
121193 {
@@ -127,6 +199,41 @@ public virtual async Task NumericOverflowViolationThrowsNumericOverflowException
127199 await Assert . ThrowsAsync < NumericOverflowException > ( ( ) => DemoContext . SaveChangesAsync ( ) ) ;
128200 }
129201
202+ #if BULK_OPERATIONS
203+ [ Fact ]
204+ public virtual async Task NumericOverflowViolationThrowsNumericOverflowExceptionThroughExecuteUpdate ( )
205+ {
206+ var product = new Product { Name = "Numeric Overflow Test 2" } ;
207+ DemoContext . Products . Add ( product ) ;
208+ var sale = new ProductSale { Price = 1m , Product = product } ;
209+ DemoContext . ProductSales . Add ( sale ) ;
210+ await DemoContext . SaveChangesAsync ( ) ;
211+
212+ Assert . Throws < NumericOverflowException > ( Query ) ;
213+ await Assert . ThrowsAsync < NumericOverflowException > ( QueryAsync ) ;
214+
215+ DemoContext . Remove ( sale ) ;
216+ DemoContext . Remove ( product ) ;
217+ await DemoContext . SaveChangesAsync ( ) ;
218+
219+ return ;
220+
221+ void Query ( )
222+ {
223+ DemoContext . ProductSales
224+ . Where ( s => s . Id == sale . Id )
225+ . ExecuteUpdate ( s => s . SetProperty ( ss => ss . Price , 3141.59265m ) ) ;
226+ }
227+
228+ async Task QueryAsync ( )
229+ {
230+ await DemoContext . ProductSales
231+ . Where ( s => s . Id == sale . Id )
232+ . ExecuteUpdateAsync ( s => s . SetProperty ( ss => ss . Price , 3141.59265m ) ) ;
233+ }
234+ }
235+ #endif
236+
130237 [ Fact ]
131238 public virtual async Task ReferenceViolationThrowsReferenceConstraintException ( )
132239 {
@@ -143,6 +250,48 @@ public virtual async Task ReferenceViolationThrowsReferenceConstraintException()
143250 }
144251 }
145252
253+ #if BULK_OPERATIONS
254+ [ Fact ]
255+ public virtual async Task ReferenceViolationThrowsReferenceConstraintExceptionThroughExecuteUpdate ( )
256+ {
257+ var product = new Product { Name = "RefConstraint Violation 1" } ;
258+ DemoContext . Products . Add ( product ) ;
259+ var sale = new ProductSale { Price = 1m , Product = product } ;
260+ DemoContext . ProductSales . Add ( sale ) ;
261+ await DemoContext . SaveChangesAsync ( ) ;
262+
263+ var exception = Assert . Throws < ReferenceConstraintException > ( Query ) ;
264+ var asyncException = await Assert . ThrowsAsync < ReferenceConstraintException > ( QueryAsync ) ;
265+
266+ if ( ! isSqlite )
267+ {
268+ Assert . False ( string . IsNullOrEmpty ( exception . ConstraintName ) ) ;
269+ Assert . NotEmpty ( exception . ConstraintProperties ) ;
270+ Assert . Contains < string > ( nameof ( ProductSale . ProductId ) , exception . ConstraintProperties ) ;
271+
272+ Assert . False ( string . IsNullOrEmpty ( asyncException . ConstraintName ) ) ;
273+ Assert . NotEmpty ( asyncException . ConstraintProperties ) ;
274+ Assert . Contains < string > ( nameof ( ProductSale . ProductId ) , asyncException . ConstraintProperties ) ;
275+ }
276+
277+ return ;
278+
279+ void Query ( )
280+ {
281+ DemoContext . ProductSales
282+ . Where ( s => s . Id == sale . Id )
283+ . ExecuteUpdate ( s => s . SetProperty ( ss => ss . ProductId , 0 ) ) ;
284+ }
285+
286+ async Task QueryAsync ( )
287+ {
288+ await DemoContext . ProductSales
289+ . Where ( s => s . Id == sale . Id )
290+ . ExecuteUpdateAsync ( s => s . SetProperty ( ss => ss . ProductId , 0 ) ) ;
291+ }
292+ }
293+ #endif
294+
146295 [ Fact ]
147296 public virtual async Task DatabaseUnrelatedExceptionThrowsOriginalException ( )
148297 {
@@ -163,7 +312,8 @@ public virtual async Task DatabaseUnrelatedExceptionThrowsOriginalException()
163312 public virtual async Task DeleteParentItemThrowsReferenceConstraintException ( )
164313 {
165314 var product = new Product { Name = "AN" } ;
166- var productPriceHistory = new ProductPriceHistory { Product = product , Price = 15.27m , EffectiveDate = DateTimeOffset . UtcNow } ;
315+ var productPriceHistory = new ProductPriceHistory
316+ { Product = product , Price = 15.27m , EffectiveDate = DateTimeOffset . UtcNow } ;
167317 DemoContext . ProductPriceHistories . Add ( productPriceHistory ) ;
168318 await DemoContext . SaveChangesAsync ( ) ;
169319
@@ -176,6 +326,39 @@ public virtual async Task DeleteParentItemThrowsReferenceConstraintException()
176326 await Assert . ThrowsAsync < ReferenceConstraintException > ( ( ) => DemoContext . SaveChangesAsync ( ) ) ;
177327 }
178328
329+ #if BULK_OPERATIONS
330+ [ Fact ]
331+ public virtual async Task DeleteParentItemThrowsReferenceConstraintExceptionThroughExecuteDelete ( )
332+ {
333+ var product = new Product { Name = "AN2" } ;
334+ var productPriceHistory = new ProductPriceHistory
335+ { Product = product , Price = 15.27m , EffectiveDate = DateTimeOffset . UtcNow } ;
336+ DemoContext . ProductPriceHistories . Add ( productPriceHistory ) ;
337+ await DemoContext . SaveChangesAsync ( ) ;
338+
339+ CleanupContext ( ) ;
340+
341+ Assert . Throws < ReferenceConstraintException > ( Query ) ;
342+ await Assert . ThrowsAsync < ReferenceConstraintException > ( QueryAsync ) ;
343+
344+ return ;
345+
346+ void Query ( )
347+ {
348+ DemoContext . Products
349+ . Where ( p => p . Name == "AN2" )
350+ . ExecuteDelete ( ) ;
351+ }
352+
353+ async Task QueryAsync ( )
354+ {
355+ await DemoContext . Products
356+ . Where ( p => p . Name == "AN2" )
357+ . ExecuteDeleteAsync ( ) ;
358+ }
359+ }
360+ #endif
361+
179362 [ Fact ]
180363 public async Task NotHandledViolationReThrowsOriginalException ( )
181364 {
0 commit comments