11using System ;
2- using System . Buffers ;
32using System . Collections . Generic ;
43using System . Diagnostics . CodeAnalysis ;
54using System . Linq ;
65using System . Linq . Expressions ;
76using System . Reflection ;
8- using System . Text ;
9- using System . Threading . Tasks ;
10- using System . Xml . Linq ;
117using EntityFrameworkCore . Projectables . Extensions ;
8+ using Microsoft . EntityFrameworkCore . Query ;
129
1310namespace EntityFrameworkCore . Projectables . Services
1411{
@@ -28,10 +25,10 @@ bool TryGetReflectedExpression(MemberInfo memberInfo, [NotNullWhen(true)] out La
2825 if ( ! _projectableMemberCache . TryGetValue ( memberInfo , out reflectedExpression ) )
2926 {
3027 var projectableAttribute = memberInfo . GetCustomAttribute < ProjectableAttribute > ( false ) ;
31-
32- reflectedExpression = projectableAttribute is not null
28+
29+ reflectedExpression = projectableAttribute is not null
3330 ? _resolver . FindGeneratedExpression ( memberInfo )
34- : ( LambdaExpression ? ) null ;
31+ : null ;
3532
3633 _projectableMemberCache . Add ( memberInfo , reflectedExpression ) ;
3734 }
@@ -50,7 +47,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
5047 {
5148 var parameterExpession = reflectedExpression . Parameters [ parameterIndex ] ;
5249 var mappedArgumentExpression = ( parameterIndex , node . Object ) switch {
53- ( 0 , not null ) => node . Object ,
50+ ( 0 , not null ) => node . Object ,
5451 ( _, not null ) => node . Arguments [ parameterIndex - 1 ] ,
5552 ( _, null ) => node . Arguments . Count > parameterIndex ? node . Arguments [ parameterIndex ] : null
5653 } ;
@@ -60,7 +57,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
6057 _expressionArgumentReplacer . ParameterArgumentMapping . Add ( parameterExpession , mappedArgumentExpression ) ;
6158 }
6259 }
63-
60+
6461 var updatedBody = _expressionArgumentReplacer . Visit ( reflectedExpression . Body ) ;
6562 _expressionArgumentReplacer . ParameterArgumentMapping . Clear ( ) ;
6663
@@ -110,5 +107,64 @@ PropertyInfo property when nodeExpression is not null
110107
111108 return base . VisitMember ( node ) ;
112109 }
110+
111+ protected override Expression VisitExtension ( Expression node )
112+ {
113+ if ( node is not QueryRootExpression root )
114+ {
115+ return node ;
116+ }
117+
118+ var projectableProperties = root . EntityType . ClrType . GetProperties ( )
119+ . Where ( x => x . IsDefined ( typeof ( ProjectableAttribute ) , false ) )
120+ . Where ( x => x . CanWrite )
121+ . ToList ( ) ;
122+
123+ if ( ! projectableProperties . Any ( ) )
124+ {
125+ return node ;
126+ }
127+
128+ var properties = root . EntityType . GetProperties ( )
129+ . Where ( x => ! x . IsShadowProperty ( ) )
130+ . Select ( x => x . GetMemberInfo ( false , false ) )
131+ // Remove projectable properties from the ef properties. Since properties returned here for auto
132+ // properties (like `public string Test {get;set;}`) are generated fields, we also need to take them into account.
133+ . Where ( x => projectableProperties . All ( y => x . Name != y . Name && x . Name != $ "<{ y . Name } >k__BackingField") ) ;
134+
135+ // Replace db.Entities to db.Entities.Select(x => new Entity { Property1 = x.Property1, Rewritted = rewrittedProperty })
136+ var select = typeof ( Queryable ) . GetMethods ( BindingFlags . Static | BindingFlags . Public )
137+ . Where ( x => x . Name == nameof ( Queryable . Select ) )
138+ . First ( x =>
139+ x . GetParameters ( ) . Last ( ) . ParameterType // Expression<Func<T, Ret>>
140+ . GetGenericArguments ( ) . First ( ) // Func<T, Ret>
141+ . GetGenericArguments ( ) . Length == 2 // Separate between Func<T, Ret> and Func<T, int, Ret>
142+ )
143+ . MakeGenericMethod ( root . EntityType . ClrType , root . EntityType . ClrType ) ;
144+ var xParam = Expression . Parameter ( root . EntityType . ClrType ) ;
145+ return Expression . Call (
146+ null ,
147+ select ,
148+ node ,
149+ Expression . Lambda (
150+ Expression . MemberInit (
151+ Expression . New ( root . EntityType . ClrType ) ,
152+ properties . Select ( x => Expression . Bind ( x , Expression . MakeMemberAccess ( xParam , x ) ) )
153+ . Concat ( projectableProperties
154+ . Select ( x => Expression . Bind ( x , _ReplaceParam ( _resolver . FindGeneratedExpression ( x ) , xParam ) ) )
155+ )
156+ ) ,
157+ xParam
158+ )
159+ ) ;
160+ }
161+
162+ private Expression _ReplaceParam ( LambdaExpression lambda , ParameterExpression para )
163+ {
164+ _expressionArgumentReplacer . ParameterArgumentMapping . Add ( lambda . Parameters [ 0 ] , para ) ;
165+ var updatedBody = _expressionArgumentReplacer . Visit ( lambda . Body ) ;
166+ _expressionArgumentReplacer . ParameterArgumentMapping . Clear ( ) ;
167+ return updatedBody ;
168+ }
113169 }
114170}
0 commit comments