@@ -23,49 +23,49 @@ namespace Microsoft.EntityFrameworkCore.Query
2323 /// </summary>
2424 public class EntityProjectionExpression : Expression
2525 {
26- private readonly IDictionary < IProperty , ColumnExpression > _propertyExpressionsCache
27- = new Dictionary < IProperty , ColumnExpression > ( ) ;
28-
29- private readonly IDictionary < INavigation , EntityShaperExpression > _navigationExpressionsCache
26+ private readonly IDictionary < IProperty , ColumnExpression > _propertyExpressionMap = new Dictionary < IProperty , ColumnExpression > ( ) ;
27+ private readonly IDictionary < INavigation , EntityShaperExpression > _ownedNavigationMap
3028 = new Dictionary < INavigation , EntityShaperExpression > ( ) ;
3129
32- private readonly TableExpressionBase _innerTable ;
33- private readonly bool _nullable ;
34-
3530 /// <summary>
3631 /// Creates a new instance of the <see cref="EntityProjectionExpression" /> class.
3732 /// </summary>
3833 /// <param name="entityType"> The entity type to shape. </param>
3934 /// <param name="innerTable"> The table from which entity columns are being projected out. </param>
4035 /// <param name="nullable"> A bool value indicating whether this entity instance can be null. </param>
36+ [ Obsolete ( "Use the constructor which takes populated column expressions map." , error : true ) ]
4137 public EntityProjectionExpression ( [ NotNull ] IEntityType entityType , [ NotNull ] TableExpressionBase innerTable , bool nullable )
4238 {
43- Check . NotNull ( entityType , nameof ( entityType ) ) ;
44- Check . NotNull ( innerTable , nameof ( innerTable ) ) ;
45-
46- EntityType = entityType ;
47- _innerTable = innerTable ;
48- _nullable = nullable ;
39+ throw new NotSupportedException ( ) ;
4940 }
5041
5142 /// <summary>
5243 /// Creates a new instance of the <see cref="EntityProjectionExpression" /> class.
5344 /// </summary>
5445 /// <param name="entityType"> The entity type to shape. </param>
55- /// <param name="propertyExpressions"> A dictionary of column expressions corresponding to properties of the entity type. </param>
56- public EntityProjectionExpression ( [ NotNull ] IEntityType entityType , [ NotNull ] IDictionary < IProperty , ColumnExpression > propertyExpressions )
46+ /// <param name="propertyExpressionMap"> A dictionary of column expressions corresponding to properties of the entity type. </param>
47+ /// <param name="entityTypeIdentifyingExpressionMap"> A dictionary of <see cref="SqlExpression"/> to identify each entity type in hierarchy. </param>
48+ public EntityProjectionExpression (
49+ [ NotNull ] IEntityType entityType ,
50+ [ NotNull ] IDictionary < IProperty , ColumnExpression > propertyExpressionMap ,
51+ [ CanBeNull ] IReadOnlyDictionary < IEntityType , SqlExpression > entityTypeIdentifyingExpressionMap = null )
5752 {
5853 Check . NotNull ( entityType , nameof ( entityType ) ) ;
59- Check . NotNull ( propertyExpressions , nameof ( propertyExpressions ) ) ;
54+ Check . NotNull ( propertyExpressionMap , nameof ( propertyExpressionMap ) ) ;
6055
6156 EntityType = entityType ;
62- _propertyExpressionsCache = propertyExpressions ;
57+ _propertyExpressionMap = propertyExpressionMap ;
58+ EntityTypeIdentifyingExpressionMap = entityTypeIdentifyingExpressionMap ;
6359 }
6460
6561 /// <summary>
6662 /// The entity type being projected out.
6763 /// </summary>
6864 public virtual IEntityType EntityType { get ; }
65+ /// <summary>
66+ /// Dictionary of entity type identifying expressions.
67+ /// </summary>
68+ public virtual IReadOnlyDictionary < IEntityType , SqlExpression > EntityTypeIdentifyingExpressionMap { get ; }
6969 /// <inheritdoc />
7070 public sealed override ExpressionType NodeType => ExpressionType . Extension ;
7171 /// <inheritdoc />
@@ -76,27 +76,31 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
7676 {
7777 Check . NotNull ( visitor , nameof ( visitor ) ) ;
7878
79- if ( _innerTable != null )
79+ var changed = false ;
80+ var propertyExpressionMap = new Dictionary < IProperty , ColumnExpression > ( ) ;
81+ foreach ( var expression in _propertyExpressionMap )
8082 {
81- var table = ( TableExpressionBase ) visitor . Visit ( _innerTable ) ;
83+ var newExpression = ( ColumnExpression ) visitor . Visit ( expression . Value ) ;
84+ changed |= newExpression != expression . Value ;
8285
83- return table != _innerTable
84- ? new EntityProjectionExpression ( EntityType , table , _nullable )
85- : this ;
86+ propertyExpressionMap [ expression . Key ] = newExpression ;
8687 }
8788
88- var changed = false ;
89- var newCache = new Dictionary < IProperty , ColumnExpression > ( ) ;
90- foreach ( var expression in _propertyExpressionsCache )
89+ Dictionary < IEntityType , SqlExpression > entityTypeIdentifyingExpressionMap = null ;
90+ if ( EntityTypeIdentifyingExpressionMap != null )
9191 {
92- var newExpression = ( ColumnExpression ) visitor . Visit ( expression . Value ) ;
93- changed |= newExpression != expression . Value ;
92+ entityTypeIdentifyingExpressionMap = new Dictionary < IEntityType , SqlExpression > ( ) ;
93+ foreach ( var expression in EntityTypeIdentifyingExpressionMap )
94+ {
95+ var newExpression = ( SqlExpression ) visitor . Visit ( expression . Value ) ;
96+ changed |= newExpression != expression . Value ;
9497
95- newCache [ expression . Key ] = newExpression ;
98+ entityTypeIdentifyingExpressionMap [ expression . Key ] = newExpression ;
99+ }
96100 }
97101
98102 return changed
99- ? new EntityProjectionExpression ( EntityType , newCache )
103+ ? new EntityProjectionExpression ( EntityType , propertyExpressionMap , entityTypeIdentifyingExpressionMap )
100104 : this ;
101105 }
102106
@@ -106,18 +110,14 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
106110 /// <returns> A new entity projection expression which can project nullable entity. </returns>
107111 public virtual EntityProjectionExpression MakeNullable ( )
108112 {
109- if ( _innerTable != null )
113+ var propertyExpressionMap = new Dictionary < IProperty , ColumnExpression > ( ) ;
114+ foreach ( var expression in _propertyExpressionMap )
110115 {
111- return new EntityProjectionExpression ( EntityType , _innerTable , nullable : true ) ;
116+ propertyExpressionMap [ expression . Key ] = expression . Value . MakeNullable ( ) ;
112117 }
113118
114- var newCache = new Dictionary < IProperty , ColumnExpression > ( ) ;
115- foreach ( var expression in _propertyExpressionsCache )
116- {
117- newCache [ expression . Key ] = expression . Value . MakeNullable ( ) ;
118- }
119-
120- return new EntityProjectionExpression ( EntityType , newCache ) ;
119+ // We don't need to process EntityTypeIdentifyingExpressionMap because they are already nullable
120+ return new EntityProjectionExpression ( EntityType , propertyExpressionMap , EntityTypeIdentifyingExpressionMap ) ;
121121 }
122122
123123 /// <summary>
@@ -129,23 +129,32 @@ public virtual EntityProjectionExpression UpdateEntityType([NotNull] IEntityType
129129 {
130130 Check . NotNull ( derivedType , nameof ( derivedType ) ) ;
131131
132- if ( _innerTable != null )
133- {
134- return new EntityProjectionExpression ( derivedType , _innerTable , _nullable ) ;
135- }
136-
137- var propertyExpressionCache = new Dictionary < IProperty , ColumnExpression > ( ) ;
138- foreach ( var kvp in _propertyExpressionsCache )
132+ var propertyExpressionMap = new Dictionary < IProperty , ColumnExpression > ( ) ;
133+ foreach ( var kvp in _propertyExpressionMap )
139134 {
140135 var property = kvp . Key ;
141136 if ( derivedType . IsAssignableFrom ( property . DeclaringEntityType )
142137 || property . DeclaringEntityType . IsAssignableFrom ( derivedType ) )
143138 {
144- propertyExpressionCache [ property ] = kvp . Value ;
139+ propertyExpressionMap [ property ] = kvp . Value ;
140+ }
141+ }
142+
143+ Dictionary < IEntityType , SqlExpression > entityTypeIdentifyingExpressionMap = null ;
144+ if ( EntityTypeIdentifyingExpressionMap != null )
145+ {
146+ entityTypeIdentifyingExpressionMap = new Dictionary < IEntityType , SqlExpression > ( ) ;
147+ foreach ( var kvp in EntityTypeIdentifyingExpressionMap )
148+ {
149+ var entityType = kvp . Key ;
150+ if ( entityType . IsStrictlyDerivedFrom ( derivedType ) )
151+ {
152+ entityTypeIdentifyingExpressionMap [ entityType ] = kvp . Value ;
153+ }
145154 }
146155 }
147156
148- return new EntityProjectionExpression ( derivedType , propertyExpressionCache ) ;
157+ return new EntityProjectionExpression ( derivedType , propertyExpressionMap , entityTypeIdentifyingExpressionMap ) ;
149158 }
150159
151160 /// <summary>
@@ -168,13 +177,7 @@ public virtual ColumnExpression BindProperty([NotNull] IProperty property)
168177 property . Name ) ) ;
169178 }
170179
171- if ( ! _propertyExpressionsCache . TryGetValue ( property , out var expression ) )
172- {
173- expression = new ColumnExpression ( property , _innerTable , _nullable ) ;
174- _propertyExpressionsCache [ property ] = expression ;
175- }
176-
177- return expression ;
180+ return _propertyExpressionMap [ property ] ;
178181 }
179182
180183 /// <summary>
@@ -198,7 +201,7 @@ public virtual void AddNavigationBinding([NotNull] INavigation navigation, [NotN
198201 navigation . Name ) ) ;
199202 }
200203
201- _navigationExpressionsCache [ navigation ] = entityShaper ;
204+ _ownedNavigationMap [ navigation ] = entityShaper ;
202205 }
203206
204207 /// <summary>
@@ -222,7 +225,7 @@ public virtual EntityShaperExpression BindNavigation([NotNull] INavigation navig
222225 navigation . Name ) ) ;
223226 }
224227
225- return _navigationExpressionsCache . TryGetValue ( navigation , out var expression )
228+ return _ownedNavigationMap . TryGetValue ( navigation , out var expression )
226229 ? expression
227230 : null ;
228231 }
0 commit comments