55// Created: 2009.02.27
66
77using System ;
8+ using System . Collections . Concurrent ;
89using System . Collections . Generic ;
910using System . Linq ;
1011using System . Linq . Expressions ;
@@ -33,39 +34,44 @@ namespace Xtensive.Orm.Linq
3334{
3435 internal sealed partial class Translator
3536 {
37+ private static readonly Type OrmCollectionExtensionsType = typeof ( CollectionExtensionsEx ) ;
38+ private static readonly Type OrmQueryableExtensionsType = typeof ( QueryableExtensions ) ;
39+
3640 protected override Expression VisitTypeIs ( TypeBinaryExpression tb )
3741 {
3842 var expression = tb . Expression ;
3943 Type expressionType = expression . Type ;
4044 Type operandType = tb . TypeOperand ;
41- if ( operandType . IsAssignableFrom ( expressionType ) )
45+ if ( operandType . IsAssignableFrom ( expressionType ) ) {
4246 return Expression . Constant ( true ) ;
47+ }
4348
4449 // Structure
4550 var memberType = expression . GetMemberType ( ) ;
46- if ( memberType == MemberType . Structure
47- && WellKnownOrmTypes . Structure . IsAssignableFrom ( operandType ) )
51+ if ( memberType == MemberType . Structure && WellKnownOrmTypes . Structure . IsAssignableFrom ( operandType ) ) {
4852 return Expression . Constant ( false ) ;
53+ }
4954
5055 // Entity
51- if ( memberType == MemberType . Entity
52- && WellKnownOrmInterfaces . Entity . IsAssignableFrom ( operandType ) ) {
53- TypeInfo type = context . Model . Types [ operandType ] ;
54- IEnumerable < int > typeIds = type . GetDescendants ( true )
55- . Union ( type . GetImplementors ( true ) )
56- . Union ( Enumerable . Repeat ( type , 1 ) )
56+ if ( memberType == MemberType . Entity && WellKnownOrmInterfaces . Entity . IsAssignableFrom ( operandType ) ) {
57+ var entityTypeInfo = context . Model . Types [ operandType ] ;
58+ var typeIds = entityTypeInfo . GetDescendants ( true )
59+ . Union ( entityTypeInfo . GetImplementors ( true ) )
60+ . Union ( Enumerable . Repeat ( entityTypeInfo , 1 ) )
5761 . Select ( t => context . TypeIdRegistry . GetTypeId ( t ) ) ;
58- MemberExpression memberExpression = Expression . MakeMemberAccess ( expression , WellKnownMembers . TypeId ) ;
59- Expression boolExpression = null ;
60- foreach ( int typeId in typeIds )
61- boolExpression = MakeBooleanExpression (
62- boolExpression ,
63- memberExpression ,
62+
63+ var typeIdPropAccessor = Expression . MakeMemberAccess ( expression , WellKnownMembers . TypeId ) ;
64+ Expression filterByTypeIdExpression = null ;
65+ foreach ( var typeId in typeIds ) {
66+ filterByTypeIdExpression = MakeBooleanExpression (
67+ filterByTypeIdExpression ,
68+ typeIdPropAccessor ,
6469 Expression . Constant ( typeId ) ,
6570 ExpressionType . Equal ,
6671 ExpressionType . OrElse ) ;
72+ }
6773
68- return Visit ( boolExpression ) ;
74+ return Visit ( filterByTypeIdExpression ) ;
6975 }
7076
7177 throw new NotSupportedException ( Strings . ExTypeIsMethodSupportsOnlyEntitiesAndStructures ) ;
@@ -101,12 +107,12 @@ protected override Expression VisitUnary(UnaryExpression u)
101107 {
102108 switch ( u . NodeType ) {
103109 case ExpressionType . TypeAs :
104- if ( u . GetMemberType ( ) == MemberType . Entity )
110+ if ( u . GetMemberType ( ) is MemberType . Entity )
105111 return VisitTypeAs ( u . Operand , u . Type ) ;
106112 break ;
107113 case ExpressionType . Convert :
108114 case ExpressionType . ConvertChecked :
109- if ( u . GetMemberType ( ) == MemberType . Entity ) {
115+ if ( u . GetMemberType ( ) is MemberType . Entity ) {
110116 if ( u . Type == u . Operand . Type
111117 || u . Type . IsAssignableFrom ( u . Operand . Type )
112118 || ! WellKnownOrmInterfaces . Entity . IsAssignableFrom ( u . Operand . Type ) )
@@ -134,9 +140,9 @@ protected override Expression VisitLambda(LambdaExpression le)
134140 body = Visit ( body ) ;
135141 ParameterExpression parameter = le . Parameters [ 0 ] ;
136142 var shouldTranslate =
137- ( body . NodeType != ExpressionType . New || body . IsNewExpressionSupportedByStorage ( ) )
138- && body . NodeType != ExpressionType . MemberInit
139- && ! ( body . NodeType == ExpressionType . Constant && state . BuildingProjection ) ;
143+ ( body . NodeType is not ExpressionType . New || body . IsNewExpressionSupportedByStorage ( ) )
144+ && body . NodeType is not ExpressionType . MemberInit
145+ && ! ( body . NodeType is ExpressionType . Constant && state . BuildingProjection ) ;
140146 if ( shouldTranslate )
141147 body = body . IsProjection ( )
142148 ? BuildSubqueryResult ( ( ProjectionExpression ) body , le . Body . Type )
@@ -177,22 +183,22 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
177183 left = ExpressionEvaluator . Evaluate ( binaryExpression . Left ) ;
178184 }
179185 else {
180- var leftMemberAccess = binaryExpression . Left as MemberExpression ;
181- left = leftMemberAccess != null && leftMemberAccess . Member . ReflectedType . IsClosure ( )
186+ left = ( binaryExpression . Left is MemberExpression leftMemberAccess
187+ && leftMemberAccess . Member . ReflectedType . IsClosure ( ) )
182188 ? ExpressionEvaluator . Evaluate ( leftMemberAccess )
183189 : Visit ( binaryExpression . Left ) ;
184190 }
185191 if ( context . Evaluator . CanBeEvaluated ( binaryExpression . Right ) ) {
186192 right = ExpressionEvaluator . Evaluate ( binaryExpression . Right ) ;
187193 }
188194 else {
189- var rightMemberAccess = binaryExpression . Right as MemberExpression ;
190- right = rightMemberAccess != null && rightMemberAccess . Member . ReflectedType . IsClosure ( )
195+ right = ( binaryExpression . Right is MemberExpression rightMemberAccess
196+ && rightMemberAccess . Member . ReflectedType . IsClosure ( ) )
191197 ? ExpressionEvaluator . Evaluate ( rightMemberAccess )
192198 : Visit ( binaryExpression . Right ) ;
193199 }
194200 }
195- else if ( memberType == MemberType . Entity || memberType == MemberType . Structure ) {
201+ else if ( memberType is MemberType . Entity or MemberType . Structure ) {
196202 if ( binaryExpression . NodeType == ExpressionType . Coalesce ) {
197203 if ( ( context . Evaluator . CanBeEvaluated ( binaryExpression . Right ) && ! ( binaryExpression . Right is ConstantExpression ) )
198204 || ( context . Evaluator . CanBeEvaluated ( binaryExpression . Left ) && ! ( binaryExpression . Left is ConstantExpression ) ) )
@@ -219,14 +225,14 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
219225 var rightNoCastsType = rightNoCasts . Type ;
220226 var bareRightType = rightNoCastsType . StripNullable ( ) ;
221227
222- if ( bareLeftType . IsEnum && rightNoCasts . NodeType == ExpressionType . Constant ) {
228+ if ( bareLeftType . IsEnum && rightNoCasts . NodeType is ExpressionType . Constant ) {
223229 var typeToCast = leftNoCastsType . IsNullable ( )
224230 ? bareLeftType . GetEnumUnderlyingType ( ) . ToNullable ( )
225231 : leftNoCastsType . GetEnumUnderlyingType ( ) ;
226232 left = Visit ( Expression . Convert ( leftNoCasts , typeToCast ) ) ;
227233 right = Visit ( Expression . Convert ( binaryExpression . Right , typeToCast ) ) ;
228234 }
229- else if ( bareRightType . IsEnum && leftNoCasts . NodeType == ExpressionType . Constant ) {
235+ else if ( bareRightType . IsEnum && leftNoCasts . NodeType is ExpressionType . Constant ) {
230236 var typeToCast = rightNoCastsType . IsNullable ( )
231237 ? bareRightType . GetEnumUnderlyingType ( ) . ToNullable ( )
232238 : rightNoCastsType . GetEnumUnderlyingType ( ) ;
@@ -253,15 +259,15 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
253259 binaryExpression . IsLiftedToNull ,
254260 binaryExpression . Method ) ;
255261
256- if ( binaryExpression . NodeType == ExpressionType . Equal
257- || binaryExpression . NodeType == ExpressionType . NotEqual )
262+ if ( binaryExpression . NodeType is ExpressionType . Equal or ExpressionType . NotEqual )
258263 return VisitBinaryRecursive ( resultBinaryExpression , binaryExpression ) ;
259264
260- if ( binaryExpression . NodeType == ExpressionType . ArrayIndex ) {
261- var newArrayExpression = left . StripCasts ( ) as NewArrayExpression ;
262- var indexExpression = right . StripCasts ( ) as ConstantExpression ;
263- if ( newArrayExpression != null && indexExpression != null && indexExpression . Type == WellKnownTypes . Int32 )
265+ if ( binaryExpression . NodeType is ExpressionType . ArrayIndex ) {
266+ if ( left . StripCasts ( ) is NewArrayExpression newArrayExpression
267+ && right . StripCasts ( ) is ConstantExpression indexExpression
268+ && indexExpression . Type == WellKnownTypes . Int32 ) {
264269 return newArrayExpression . Expressions [ ( int ) indexExpression . Value ] ;
270+ }
265271
266272 throw new NotSupportedException ( String . Format ( Strings . ExBinaryExpressionXOfTypeXIsNotSupported , binaryExpression . ToString ( true ) , binaryExpression . NodeType ) ) ;
267273 }
@@ -271,9 +277,9 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
271277 static bool EnumRewritableOperations ( BinaryExpression b )
272278 {
273279 var nt = b . NodeType ;
274- return nt == ExpressionType . Equal || nt == ExpressionType . NotEqual
275- || nt == ExpressionType . GreaterThan || nt == ExpressionType . GreaterThanOrEqual
276- || nt == ExpressionType . LessThan || nt == ExpressionType . LessThanOrEqual ;
280+ return nt is ExpressionType . Equal or ExpressionType . NotEqual
281+ or ExpressionType . GreaterThan or ExpressionType . GreaterThanOrEqual
282+ or ExpressionType . LessThan or ExpressionType . LessThanOrEqual ;
277283 }
278284 }
279285
@@ -283,19 +289,20 @@ protected override Expression VisitConditional(ConditionalExpression c)
283289 ? c . IfFalse . GetMemberType ( )
284290 : c . IfTrue . GetMemberType ( ) ;
285291 if ( memberType == MemberType . Entity || memberType == MemberType . Structure ) {
286- if ( ( context . Evaluator . CanBeEvaluated ( c . IfFalse ) && ! ( c . IfFalse is ConstantExpression ) )
287- || ( context . Evaluator . CanBeEvaluated ( c . IfTrue ) && ! ( c . IfTrue is ConstantExpression ) ) )
292+ if ( ( context . Evaluator . CanBeEvaluated ( c . IfFalse ) && ( c . IfFalse is not ConstantExpression ) )
293+ || ( context . Evaluator . CanBeEvaluated ( c . IfTrue ) && ( c . IfTrue is not ConstantExpression ) ) )
288294 throw new NotSupportedException ( string . Format ( Strings . ExXExpressionsWithConstantValuesOfYTypeNotSupported , "Conditional" , memberType . ToString ( ) ) ) ;
289295 }
290296 return base . VisitConditional ( c ) ;
291297 }
292298
293299 private Expression ConvertEnum ( Expression left )
294300 {
295- var underlyingType = Enum . GetUnderlyingType ( left . Type . StripNullable ( ) ) ;
296- if ( left . Type . IsNullable ( ) )
297- underlyingType = underlyingType . ToNullable ( ) ;
298- left = left . NodeType == ExpressionType . Convert
301+ var underlyingType = left . Type . IsNullable ( )
302+ ? Enum . GetUnderlyingType ( left . Type . GetGenericArguments ( ) [ 0 ] ) . ToNullable ( )
303+ : Enum . GetUnderlyingType ( left . Type ) ;
304+
305+ left = left . NodeType is ExpressionType . Convert
299306 ? Expression . Convert ( ( ( UnaryExpression ) left ) . Operand , underlyingType )
300307 : Expression . Convert ( left , underlyingType ) ;
301308 return left ;
@@ -450,7 +457,7 @@ protected override Expression VisitMethodCall(MethodCallExpression mc)
450457 throw new InvalidOperationException ( String . Format ( Strings . ExMethodCallExpressionXIsNotSupported , mc . ToString ( true ) ) ) ;
451458 }
452459 // Visit QueryEndpoint.
453- if ( method . DeclaringType == typeof ( QueryEndpoint ) ) {
460+ if ( method . DeclaringType == WellKnownOrmTypes . QueryEndpoint ) {
454461 // Query.All<T>
455462 if ( method . IsGenericMethodSpecificationOf ( WellKnownMembers . QueryEndpoint . All ) ) {
456463 return ConstructQueryable ( mc ) ;
@@ -1208,22 +1215,25 @@ private ColumnExpression AddCalculatedColumn(ParameterExpression sourceParameter
12081215 var dataSource = oldResult . ItemProjector . DataSource ;
12091216
12101217 SortProvider sortProvider = null ;
1211- if ( dataSource is SortProvider ) {
1212- sortProvider = ( SortProvider ) dataSource ;
1213- dataSource = sortProvider . Source ;
1218+ if ( dataSource is SortProvider sortProvider1 ) {
1219+ sortProvider = sortProvider1 ;
1220+ dataSource = sortProvider1 . Source ;
12141221 }
12151222
1216- var columns = new List < CalculatedColumnDescriptor > ( ) ;
1217- if ( state . AllowCalculableColumnCombine && dataSource is CalculateProvider && isInlined == ( ( CalculateProvider ) dataSource ) . IsInlined ) {
1218- var calculateProvider = ( ( CalculateProvider ) dataSource ) ;
1219- var presentColumns = calculateProvider
1223+ CalculatedColumnDescriptor [ ] columns ;
1224+ if ( state . AllowCalculableColumnCombine && dataSource is CalculateProvider calculateProvider && isInlined == calculateProvider . IsInlined ) {
1225+ columns = calculateProvider
12201226 . CalculatedColumns
1221- . Select ( cc => new CalculatedColumnDescriptor ( cc . Name , cc . Type , cc . Expression ) ) ;
1222- columns . AddRange ( presentColumns ) ;
1227+ . Select ( cc => new CalculatedColumnDescriptor ( cc . Name , cc . Type , cc . Expression ) )
1228+ . ToArray ( calculateProvider . CalculatedColumns . Length + 1 ) ;
1229+ columns [ ^ 1 ] = descriptor ;
12231230 dataSource = calculateProvider . Source ;
12241231 }
1225- columns . Add ( descriptor ) ;
1226- dataSource = dataSource . Calculate ( isInlined , columns . ToArray ( ) ) ;
1232+ else {
1233+ columns = new [ ] { descriptor } ;
1234+ }
1235+
1236+ dataSource = dataSource . Calculate ( isInlined , columns ) ;
12271237
12281238 if ( sortProvider != null )
12291239 dataSource = dataSource . OrderBy ( sortProvider . Order ) ;
@@ -1291,37 +1301,44 @@ private static IList<Expression> GetAnonymousArguments(Expression expression, Ty
12911301 . Select ( ( methodInfo , index ) => new { methodInfo . Name , Argument = newExpression . Arguments [ index ] } )
12921302 . OrderBy ( a => a . Name )
12931303 . Select ( a => a . Argument ) ;
1294- return arguments . ToList ( ) ;
1304+ return arguments . ToList ( newExpression . Members . Count ) ;
12951305 }
12961306
12971307 if ( expression . NodeType == ExpressionType . Constant ) {
12981308 var constantExpression = expression as ConstantExpression ;
12991309 if ( constantExpression . Value == null && constantExpression . Type == WellKnownTypes . Object ) {
13001310 var newConstantExpressionType = anonymousTypeForNullValues ?? constantExpression . Type ;
13011311 constantExpression = Expression . Constant ( null , newConstantExpressionType ) ;
1302- return constantExpression
1312+ var orderedProps1 = constantExpression
13031313 . Type
1304- . GetProperties ( )
1305- . OrderBy ( property => property . Name )
1306- . Select ( p => Expression . MakeMemberAccess ( constantExpression , p ) )
1307- . Cast < Expression > ( )
1308- . ToList ( ) ;
1314+ . GetProperties ( ) ;
1315+ Array . Sort ( orderedProps1 , CompareProps ) ;
1316+
1317+ return orderedProps1
1318+ . Select ( p => ( Expression ) Expression . MakeMemberAccess ( constantExpression , p ) )
1319+ . ToList ( orderedProps1 . Length ) ;
13091320 }
13101321 }
13111322
1312- return expression
1323+ var orderedProps = expression
13131324 . Type
1314- . GetProperties ( )
1315- . OrderBy ( property => property . Name )
1316- . Select ( p => Expression . MakeMemberAccess ( expression , p ) )
1317- . Select ( e => ( Expression ) e )
1318- . ToList ( ) ;
1325+ . GetProperties ( ) ;
1326+ Array . Sort ( orderedProps , CompareProps ) ;
1327+
1328+ return orderedProps
1329+ . Select ( p => ( Expression ) Expression . MakeMemberAccess ( expression , p ) )
1330+ . ToList ( orderedProps . Length ) ;
1331+
1332+ static int CompareProps ( PropertyInfo p1 , PropertyInfo p2 )
1333+ {
1334+ return StringComparer . Ordinal . Compare ( p1 . Name , p2 . Name ) ;
1335+ }
13191336 }
13201337
13211338 protected override Expression VisitMemberInit ( MemberInitExpression mi )
13221339 {
13231340 var newExpression = mi . NewExpression ;
1324- var arguments = VisitNewExpressionArguments ( newExpression ) ;
1341+ VisitNewExpressionArgumentsSkipResults ( newExpression ) ;
13251342 var bindings = VisitBindingList ( mi . Bindings ) . Cast < MemberAssignment > ( ) ;
13261343 var constructorExpression = ( ConstructorExpression ) VisitNew ( mi . NewExpression ) ;
13271344 foreach ( var binding in bindings ) {
@@ -1529,13 +1546,12 @@ private Expression GetMemberWithRemap(Expression expression, MemberInfo memberIn
15291546
15301547 private Expression GetConditionalMember ( Expression expression , MemberInfo member , Expression sourceExpression )
15311548 {
1532- var ce = expression as ConditionalExpression ;
1533- if ( ce != null ) {
1549+ if ( expression is ConditionalExpression ce ) {
15341550 var ifTrue = GetConditionalMember ( ce . IfTrue , member , sourceExpression ) ;
15351551 var ifFalse = GetConditionalMember ( ce . IfFalse , member , sourceExpression ) ;
15361552 if ( ifTrue == null || ifFalse == null )
15371553 return null ;
1538- return Expression . Condition ( ce . Test , ifTrue , ifFalse ) ;
1554+ return Expression . Condition ( ce . Test , ifTrue , ifFalse ) ;
15391555 }
15401556 if ( expression . IsNull ( ) ) {
15411557 var mt = member . MemberType ;
@@ -1648,8 +1664,7 @@ private void EnsureEntityFieldsAreJoined(EntityExpression entityExpression)
16481664 public void EnsureEntityFieldsAreJoined ( EntityExpression entityExpression , ItemProjectorExpression itemProjector )
16491665 {
16501666 TypeInfo typeInfo = entityExpression . PersistentType ;
1651- if (
1652- typeInfo . Fields . All ( fieldInfo => entityExpression . Fields . Any ( entityField => entityField . Name == fieldInfo . Name ) ) )
1667+ if ( typeInfo . Fields . All ( fieldInfo => entityExpression . Fields . Any ( entityField => entityField . Name == fieldInfo . Name ) ) )
16531668 return ; // All fields are already joined
16541669 IndexInfo joinedIndex = typeInfo . Indexes . PrimaryIndex ;
16551670 var joinedRs = joinedIndex . GetQuery ( ) . Alias ( itemProjector . Context . GetNextAlias ( ) ) ;
@@ -1807,7 +1822,7 @@ private MemberInfo TryGetActualPropertyInfo(PropertyInfo propertyInfo, Type init
18071822 private IList < ColumnInfo > GetColumnsToSearch ( ConstantExpression arrayOfColumns , Type elementType , TypeInfo domainType )
18081823 {
18091824 var columnAccessLambdas = ( LambdaExpression [ ] ) arrayOfColumns . Value ;
1810- var fulltextFields = new List < ColumnInfo > ( ) ;
1825+ var fulltextFields = new List < ColumnInfo > ( columnAccessLambdas . Length ) ;
18111826 foreach ( var lambda in columnAccessLambdas ) {
18121827 var field = FieldExtractor . Extract ( lambda , elementType , domainType ) ;
18131828 if ( field . Column == null )
0 commit comments