Ejemplo n.º 1
0
        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();
            }
        }
Ejemplo n.º 2
0
            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);
            }
            }
        }