@@ -127,6 +127,17 @@ public BatchUpdate()
127127 /// <returns>The number of rows affected.</returns>
128128 public int Execute < T > ( IQueryable < T > query , Expression < Func < T , T > > updateFactory ) where T : class
129129 {
130+ // FIX query with visitor
131+ {
132+ var visitor = new BatchUpdateVisitor ( ) ;
133+ visitor . Visit ( query . Expression ) ;
134+
135+ if ( visitor . HasOrderBy )
136+ {
137+ query = query . Take ( int . MaxValue ) ;
138+ }
139+ }
140+
130141 string expression = query . Expression . ToString ( ) ;
131142
132143 if ( Regex . IsMatch ( expression , @"\.Where\(\w+ => False\)" ) )
@@ -181,8 +192,8 @@ public int Execute<T>(IQueryable<T> query, Expression<Func<T, T>> updateFactory)
181192 }
182193#elif EFCORE
183194 var dbContext = query . GetDbContext ( ) ;
184- var entity = dbContext . Model . FindEntityType ( typeof ( T ) ) ;
185-
195+ var entity = dbContext . Model . FindEntityType ( typeof ( T ) ) ;
196+
186197 // TODO: Select only key + lambda columns
187198 // var keys = entity.GetKeys().ToList()[0].Properties;
188199 //var queryKeys = query.SelectByName(keys.Select(x => x.Name).ToList());
@@ -285,7 +296,7 @@ internal DbCommand CreateCommand<T>(ObjectQuery query, SchemaEntityType<T> entit
285296
286297 // GET updateSetValues
287298 var setValues = string . Join ( "," + Environment . NewLine , values . Select ( ( x , i ) => x . Item2 is ConstantExpression ?
288- string . Concat ( "A." , EscapeName ( x . Item1 , isMySql ) , " = " , ( ( ConstantExpression ) x . Item2 ) . Value ) :
299+ string . Concat ( "A." , EscapeName ( x . Item1 , isMySql ) , " = " , ( ( ConstantExpression ) x . Item2 ) . Value ) :
289300 string . Concat ( "A." , EscapeName ( x . Item1 , isMySql ) , " = @zzz_BatchUpdate_" , i ) ) ) ;
290301
291302 // REPLACE template
@@ -367,9 +378,9 @@ public DbCommand CreateCommand(IQueryable query, IEntityType entity, List<Tuple<
367378 if ( assembly != null )
368379 {
369380 var type = assembly . GetType ( "Microsoft.EntityFrameworkCore.SqlServerMetadataExtensions" ) ;
370- var sqlServerEntityTypeMethod = type . GetMethod ( "SqlServer" , BindingFlags . Public | BindingFlags . Static , null , new [ ] { typeof ( IEntityType ) } , null ) ;
371- var sqlServerPropertyMethod = type . GetMethod ( "SqlServer" , BindingFlags . Public | BindingFlags . Static , null , new [ ] { typeof ( IProperty ) } , null ) ;
372- var sqlServer = ( IRelationalEntityTypeAnnotations ) sqlServerEntityTypeMethod . Invoke ( null , new [ ] { entity } ) ;
381+ var sqlServerEntityTypeMethod = type . GetMethod ( "SqlServer" , BindingFlags . Public | BindingFlags . Static , null , new [ ] { typeof ( IEntityType ) } , null ) ;
382+ var sqlServerPropertyMethod = type . GetMethod ( "SqlServer" , BindingFlags . Public | BindingFlags . Static , null , new [ ] { typeof ( IProperty ) } , null ) ;
383+ var sqlServer = ( IRelationalEntityTypeAnnotations ) sqlServerEntityTypeMethod . Invoke ( null , new [ ] { entity } ) ;
373384
374385 // GET mapping
375386 var tableName = string . IsNullOrEmpty ( sqlServer . Schema ) ?
@@ -380,10 +391,10 @@ public DbCommand CreateCommand(IQueryable query, IEntityType entity, List<Tuple<
380391 var columnKeys = new List < string > ( ) ;
381392 foreach ( var propertyKey in entity . GetKeys ( ) . ToList ( ) [ 0 ] . Properties )
382393 {
383- var mappingProperty = sqlServerPropertyMethod . Invoke ( null , new [ ] { propertyKey } ) ;
394+ var mappingProperty = sqlServerPropertyMethod . Invoke ( null , new [ ] { propertyKey } ) ;
384395
385396 var columnNameProperty = mappingProperty . GetType ( ) . GetProperty ( "ColumnName" , BindingFlags . Public | BindingFlags . Instance ) ;
386- columnKeys . Add ( ( string ) columnNameProperty . GetValue ( mappingProperty ) ) ;
397+ columnKeys . Add ( ( string ) columnNameProperty . GetValue ( mappingProperty ) ) ;
387398 }
388399#endif
389400 // GET command text template
@@ -410,7 +421,7 @@ public DbCommand CreateCommand(IQueryable query, IEntityType entity, List<Tuple<
410421
411422 // GET updateSetValues
412423 var setValues = string . Join ( "," + Environment . NewLine , values . Select ( ( x , i ) => x . Item2 is ConstantExpression ?
413- string . Concat ( "A.[" , x . Item1 , "] = " , ( ( ConstantExpression ) x . Item2 ) . Value ) :
424+ string . Concat ( "A.[" , x . Item1 , "] = " , ( ( ConstantExpression ) x . Item2 ) . Value ) :
414425 string . Concat ( "A.[" , x . Item1 , "] = @zzz_BatchUpdate_" , i ) ) ) ;
415426
416427 // REPLACE template
@@ -506,7 +517,7 @@ public List<Tuple<string, object>> GetInnerValues<T>(IQueryable<T> query, Expres
506517 }
507518
508519 var type = assembly . GetType ( "Microsoft.EntityFrameworkCore.SqlServerMetadataExtensions" ) ;
509- var sqlServerPropertyMethod = type . GetMethod ( "SqlServer" , BindingFlags . Public | BindingFlags . Static , null , new [ ] { typeof ( IProperty ) } , null ) ;
520+ var sqlServerPropertyMethod = type . GetMethod ( "SqlServer" , BindingFlags . Public | BindingFlags . Static , null , new [ ] { typeof ( IProperty ) } , null ) ;
510521
511522#endif
512523#endif
@@ -529,61 +540,79 @@ public List<Tuple<string, object>> GetInnerValues<T>(IQueryable<T> query, Expres
529540#elif EFCORE
530541
531542 var property = entity . FindProperty ( value . Key ) ;
532- var mappingProperty = sqlServerPropertyMethod . Invoke ( null , new [ ] { property } ) ;
543+ var mappingProperty = sqlServerPropertyMethod . Invoke ( null , new [ ] { property } ) ;
533544
534545 var columnNameProperty = mappingProperty . GetType ( ) . GetProperty ( "ColumnName" , BindingFlags . Public | BindingFlags . Instance ) ;
535- var columnName = ( string ) columnNameProperty . GetValue ( mappingProperty ) ;
546+ var columnName = ( string ) columnNameProperty . GetValue ( mappingProperty ) ;
536547#endif
537548
538549 if ( value . Value is Expression )
539550 {
540551 // Oops! the value is not resolved yet!
541552 ParameterExpression parameterExpression = null ;
542- ( ( Expression ) value . Value ) . Visit ( ( ParameterExpression p ) =>
553+ ( ( Expression ) value . Value ) . Visit ( ( ParameterExpression p ) =>
543554 {
544- if ( p . Type == typeof ( T ) )
555+ if ( p . Type == typeof ( T ) )
545556 parameterExpression = p ;
546557
547558 return p ;
548559 } ) ;
549560
550561 // GET the update value by creating a new select command
551- Type [ ] typeArguments = { typeof ( T ) , ( ( Expression ) value . Value ) . Type } ;
552- var lambdaExpression = Expression . Lambda ( ( ( Expression ) value . Value ) , parameterExpression ) ;
562+ Type [ ] typeArguments = { typeof ( T ) , ( ( Expression ) value . Value ) . Type } ;
563+ var lambdaExpression = Expression . Lambda ( ( ( Expression ) value . Value ) , parameterExpression ) ;
553564 var selectExpression = Expression . Call (
554- typeof ( Queryable ) ,
565+ typeof ( Queryable ) ,
555566 "Select" ,
556567 typeArguments ,
557568 Expression . Constant ( query ) ,
558569 lambdaExpression ) ;
559570 var result = Expression . Lambda ( selectExpression ) . Compile ( ) . DynamicInvoke ( ) ;
560571#if EF5 || EF6
561572 // GET the select command text
562- var commandText = ( ( IQueryable ) result ) . ToString ( ) ;
573+ var commandText = ( ( IQueryable ) result ) . ToString ( ) ;
563574
564575 // GET the 'value' part
565- var valueSql = commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
566- commandText . Substring ( 6 , commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 ) :
567- commandText . Substring ( 6 , commandText . IndexOf ( "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 ) ;
576+ var pos = commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
577+ commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 :
578+ commandText . IndexOf ( Environment . NewLine + " FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
579+ commandText . IndexOf ( Environment . NewLine + " FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 :
580+ commandText . IndexOf ( Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
581+ commandText . IndexOf ( Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 :
582+ commandText . IndexOf ( "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 ;
583+
584+ var valueSql = commandText . Substring ( 6 , pos ) ;
568585
569586 // Add the destination name
570587 valueSql = valueSql . Replace ( "AS [C1]" , "" ) ;
571588 valueSql = valueSql . Replace ( "[Extent1]" , "B" ) ;
572589#elif EFCORE
573590 RelationalQueryContext queryContext ;
574- var command = ( ( IQueryable ) result ) . CreateCommand ( out queryContext ) ;
591+ var command = ( ( IQueryable ) result ) . CreateCommand ( out queryContext ) ;
575592 var commandText = command . CommandText ;
576593
577594#if NETSTANDARD1_3
578595 // GET the 'value' part
579- var valueSql = commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . CurrentCultureIgnoreCase ) != - 1 ?
580- commandText . Substring ( 6 , commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . CurrentCultureIgnoreCase ) - 6 ) :
581- commandText . Substring ( 6 , commandText . IndexOf ( "FROM" , StringComparison . CurrentCultureIgnoreCase ) - 6 ) ;
596+ var pos = commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . CurrentCultureIgnoreCase ) != - 1 ?
597+ commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . CurrentCultureIgnoreCase ) - 6 :
598+ commandText . IndexOf ( Environment . NewLine + " FROM" , StringComparison . CurrentCultureIgnoreCase ) != - 1 ?
599+ commandText . IndexOf ( Environment . NewLine + " FROM" , StringComparison . CurrentCultureIgnoreCase ) - 6 :
600+ commandText . IndexOf ( Environment . NewLine + "FROM" , StringComparison . CurrentCultureIgnoreCase ) != - 1 ?
601+ commandText . IndexOf ( Environment . NewLine + "FROM" , StringComparison . CurrentCultureIgnoreCase ) - 6 :
602+ commandText . IndexOf ( "FROM" , StringComparison . CurrentCultureIgnoreCase ) - 6 ;
603+
604+ var valueSql = commandText . Substring ( 6 , pos ) ;
582605#else
583- // GET the 'value' part
584- var valueSql = commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
585- commandText . Substring ( 6 , commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 ) :
586- commandText . Substring ( 6 , commandText . IndexOf ( "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 ) ;
606+ // GET the 'value' part
607+ var pos = commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
608+ commandText . IndexOf ( "AS [value]" + Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 :
609+ commandText . IndexOf ( Environment . NewLine + " FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
610+ commandText . IndexOf ( Environment . NewLine + " FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 :
611+ commandText . IndexOf ( Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) != - 1 ?
612+ commandText . IndexOf ( Environment . NewLine + "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 :
613+ commandText . IndexOf ( "FROM" , StringComparison . InvariantCultureIgnoreCase ) - 6 ;
614+
615+ var valueSql = commandText . Substring ( 6 , pos ) ;
587616#endif
588617
589618 // Add the destination name
@@ -609,7 +638,7 @@ public Dictionary<string, object> ResolveUpdateFromQueryDictValues<T>(Expression
609638 {
610639 var dictValues = new Dictionary < string , object > ( ) ;
611640 var updateExpressionBody = updateFactory . Body ;
612- var entityType = typeof ( T ) ;
641+ var entityType = typeof ( T ) ;
613642
614643 // ENSURE: new T() { MemberInitExpression }
615644 var memberInitExpression = updateExpressionBody as MemberInitExpression ;
0 commit comments