private Expression CreateMaterializer(IEntityType targetType, BaseTableExpression table) { var rootType = targetType.RootType(); var hierarchy = rootType.GetDerivedTypes().Prepend(rootType).ToArray(); var keyExpression = CreateMaterializationKeySelector(targetType); if (hierarchy.Length == 1) { // No inheritance return(CreateMaterializationExpression(targetType, table, p => MakeColumnExpression(table, p))); } else if (IsTablePerHierarchy(rootType, hierarchy)) { // Table-per-hierarchy inheritance var properties = (from t in hierarchy from p in IterateAllProperties(t) group p by GetRelationalId(p) into g select(id: g.Key, property: g.First())).ToArray(); var columns = (from p in properties.Select(p => p.property) select MakeColumnExpression(table, p)).ToArray(); var tupleType = ValueTupleHelper.CreateTupleType(columns.Select(c => c.Type)); var tupleParameter = Expression.Parameter(tupleType); Expression MakeTupleColumnExpression(IProperty property) { return(Expression.Convert( ValueTupleHelper.CreateMemberExpression( tupleType, tupleParameter, Array.FindIndex(properties, q => q.id.Equals(GetRelationalId(property)))), property.ClrType)); }; var concreteTypes = hierarchy.Where(t => !t.ClrType.IsAbstract).ToArray(); var descriptors = new PolymorphicTypeDescriptor[concreteTypes.Length]; for (var i = 0; i < concreteTypes.Length; i++) { var type = concreteTypes[i]; var relational = type.Relational(); var test = Expression.Lambda( Expression.Equal( ValueTupleHelper.CreateMemberExpression( tupleType, tupleParameter, Array.FindIndex(properties, p => p.property == relational.DiscriminatorProperty)), Expression.Constant(relational.DiscriminatorValue)), tupleParameter); var descriptorMaterializer = Expression.Lambda( CreateMaterializationExpression(type, table, MakeTupleColumnExpression), tupleParameter); descriptors[i] = new PolymorphicTypeDescriptor(type.ClrType, test, descriptorMaterializer); } return(new PolymorphicExpression( targetType.ClrType, ValueTupleHelper.CreateNewExpression(tupleType, columns), descriptors).Filter(targetType.ClrType)); } else { // Waiting on EF Core: // TODO: (EF Core ?.?) Table-per-type polymorphism // TODO: (EF Core ?.?) Table-per-concrete polymorphism throw new NotSupportedException(); } }
protected override Expression VisitExtension(Expression node) { switch (node) { case EntityMaterializationExpression entityMaterializationExpression: { var currentNavigation = path.Peek(); var visited = (EntityMaterializationExpression)base.VisitExtension(node); if (Finished) { visited = visited.IncludeNavigation(currentNavigation); } return(visited); } case ExtendedNewExpression extendedNewExpression: { return(VisitExtendedNew(extendedNewExpression)); } case ExtendedMemberInitExpression extendedMemberInitExpression: { return(VisitExtendedMemberInit(extendedMemberInitExpression)); } case SimpleExtraPropertiesExpression extraPropertiesExpression: { var navigation = path.Pop(); try { for (var i = 0; i < extraPropertiesExpression.Names.Count; i++) { var name = extraPropertiesExpression.Names[i]; if (name == navigation.Name || name == $"<{navigation.DeclaringType.ClrType.Name}>{navigation.Name}") { var expression = Visit(extraPropertiesExpression.Properties[i]); if (Finished) { return(extraPropertiesExpression.SetProperty(name, expression)); } else { return(extraPropertiesExpression); } } } } finally { path.Push(navigation); } return(base.VisitExtension(node)); } case PolymorphicExpression polymorphicExpression: { var row = Visit(polymorphicExpression.Row); if (Finished) { return(polymorphicExpression.Update(row, polymorphicExpression.Descriptors)); } if (!(row is SimpleExtraPropertiesExpression extraProperties)) { extraProperties = new SimpleExtraPropertiesExpression( polymorphicExpression.Row, Array.Empty <string>(), Array.Empty <Expression>()); } var navigation = path.Peek(); var member = navigation.GetReadableMemberInfo(); var hasCompatibleDescriptor = polymorphicExpression.Descriptors .Any(d => d.Type.IsAssignableFrom(member.DeclaringType)); if (!hasCompatibleDescriptor) { return(polymorphicExpression.Update(row, polymorphicExpression.Descriptors)); } var propertyName = member.Name; if (member.DeclaringType.IsSubclassOf(node.Type)) { propertyName = $"<{member.DeclaringType.Name}>{member.Name}"; } extraProperties = extraProperties.SetProperty(propertyName, includedExpression); var descriptors = polymorphicExpression.Descriptors.ToArray(); var finishedAny = false; for (var i = 0; i < descriptors.Length; i++) { Finished = false; var descriptor = descriptors[i]; var parameter = descriptor.Materializer.Parameters.Single(); includedExpression = new ExtraPropertyAccessExpression( parameter, propertyName, member.GetMemberType()); var materializer = Expression.Lambda( Visit(descriptor.Materializer.Body), descriptor.Materializer.Parameters); descriptors[i] = new PolymorphicTypeDescriptor( descriptor.Type, descriptor.Test, materializer); finishedAny |= Finished; } if (finishedAny) { Finished = true; return(polymorphicExpression.Update(extraProperties, descriptors)); } return(polymorphicExpression.Update(row, polymorphicExpression.Descriptors)); } default: { return(base.VisitExtension(node)); } } }
public override Expression Visit(Expression node) { if (InLeaf) { return(base.Visit(node)); } switch (node) { case NewExpression newExpression when IsNotLeaf(newExpression): { var arguments = new Expression[newExpression.Arguments.Count]; for (var i = 0; i < newExpression.Arguments.Count; i++) { memberStack.Push(newExpression.Members[i]); nameStack.Push(newExpression.Members[i].GetPathSegmentName()); arguments[i] = Visit(newExpression.Arguments[i]); memberStack.Pop(); nameStack.Pop(); } return(newExpression.Update(arguments)); } case MemberInitExpression memberInitExpression when IsNotLeaf(memberInitExpression): { var newExpression = memberInitExpression.NewExpression; var arguments = new Expression[newExpression.Arguments.Count]; for (var i = 0; i < newExpression.Arguments.Count; i++) { memberStack.Push(newExpression.Members[i]); nameStack.Push(newExpression.Members[i].GetPathSegmentName()); arguments[i] = Visit(newExpression.Arguments[i]); memberStack.Pop(); nameStack.Pop(); } newExpression = newExpression.Update(arguments); var bindings = new MemberBinding[memberInitExpression.Bindings.Count]; for (var i = 0; i < memberInitExpression.Bindings.Count; i++) { memberStack.Push(memberInitExpression.Bindings[i].Member); nameStack.Push(memberInitExpression.Bindings[i].Member.GetPathSegmentName()); bindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]); memberStack.Pop(); nameStack.Pop(); } return(memberInitExpression.Update(newExpression, bindings)); } case ExtendedNewExpression newExpression: { var arguments = new Expression[newExpression.Arguments.Count]; for (var i = 0; i < newExpression.Arguments.Count; i++) { memberStack.Push(newExpression.ReadableMembers[i]); nameStack.Push(newExpression.ReadableMembers[i].GetPathSegmentName()); arguments[i] = Visit(newExpression.Arguments[i]); memberStack.Pop(); nameStack.Pop(); } return(newExpression.Update(arguments)); } case ExtendedMemberInitExpression memberInitExpression: { var newExpression = memberInitExpression.NewExpression; var arguments = new Expression[newExpression.Arguments.Count]; for (var i = 0; i < newExpression.Arguments.Count; i++) { memberStack.Push(newExpression.ReadableMembers[i]); nameStack.Push(newExpression.ReadableMembers[i].GetPathSegmentName()); arguments[i] = Visit(newExpression.Arguments[i]); memberStack.Pop(); nameStack.Pop(); } newExpression = newExpression.Update(arguments); arguments = new Expression[memberInitExpression.Arguments.Count]; for (var i = 0; i < memberInitExpression.Arguments.Count; i++) { memberStack.Push(memberInitExpression.ReadableMembers[i]); nameStack.Push(memberInitExpression.ReadableMembers[i].GetPathSegmentName()); arguments[i] = Visit(memberInitExpression.Arguments[i]); memberStack.Pop(); nameStack.Pop(); } return(memberInitExpression.Update(newExpression, arguments)); } case NewArrayExpression newArrayExpression: { var expressions = new Expression[newArrayExpression.Expressions.Count]; for (var i = 0; i < newArrayExpression.Expressions.Count; i++) { nameStack.Push($"${i}"); expressions[i] = Visit(newArrayExpression.Expressions[i]); nameStack.Pop(); } return(newArrayExpression.Update(expressions)); } case PolymorphicExpression polymorphicExpression: { var row = Visit(polymorphicExpression.Row); var descriptors = polymorphicExpression.Descriptors.ToArray(); for (var i = 0; i < descriptors.Length; i++) { var descriptor = descriptors[i]; descriptors[i] = new PolymorphicTypeDescriptor( descriptor.Type, descriptor.Test, Expression.Lambda( descriptor.Materializer.Body, descriptor.Materializer.Parameters)); } return(new PolymorphicExpression( polymorphicExpression.Type, row, descriptors)); } case UnaryExpression unaryExpression when unaryExpression.NodeType == ExpressionType.Convert: { return(unaryExpression.Update(Visit(unaryExpression.Operand))); } case ExtraPropertiesExpression extraPropertiesExpression: { var properties = new Expression[extraPropertiesExpression.Properties.Count]; for (var i = 0; i < extraPropertiesExpression.Properties.Count; i++) { var path = extraPropertiesExpression.GetMemberPath(i).ToArray(); if (path.Length == 0) { nameStack.Push(extraPropertiesExpression.Names[i]); properties[i] = Visit(extraPropertiesExpression.Properties[i]); nameStack.Pop(); } else { for (var j = 0; j < path.Length; j++) { memberStack.Push(path[j]); nameStack.Push(path[j].GetPathSegmentName()); } properties[i] = Visit(extraPropertiesExpression.Properties[i]); for (var j = 0; j < path.Length; j++) { memberStack.Pop(); nameStack.Pop(); } } } var expression = Visit(extraPropertiesExpression.Expression); return(extraPropertiesExpression.Update(expression, properties)); } case LateBoundProjectionLeafExpression _: { return(node); } case AnnotationExpression annotationExpression: { return(base.Visit(annotationExpression)); } default: { InLeaf = true; node = VisitLeaf(node); InLeaf = false; return(node); } } }