private static Expression CreateTablePerTypeQueryExpression(
            Type targetType,
            TreeNode <TablePerTypeInfo> hierarchyRoot)
        {
            var columnDescriptors
                = hierarchyRoot
                  .Flatten()
                  .SelectMany(x => x.TableDescriptor.ColumnDescriptors)
                  .Select((c, i) => new { c, i });

            var columnExpressions
                = hierarchyRoot
                  .Flatten()
                  .SelectMany(x => x.Columns);

            var tupleType          = ValueTupleHelper.CreateTupleType(columnExpressions.Select(c => c.Type));
            var tupleNewExpression = ValueTupleHelper.CreateNewExpression(tupleType, columnExpressions);
            var tupleParameter     = Expression.Parameter(tupleType);

            var polymorphicTypeDescriptors
                = (from node in hierarchyRoot.Flatten()
                   where !node.Type.GetTypeInfo().IsAbstract
                   let testMember = node.TableDescriptor.PrimaryKeyMembers.First()
                                    select new PolymorphicTypeDescriptor(
                       node.Type,
                       Expression.Lambda(
                           Expression.NotEqual(
                               ValueTupleHelper.CreateMemberExpression(
                                   tupleType,
                                   tupleParameter,
                                   (from x in columnDescriptors
                                    where x.c.SourceMember == testMember
                                    select x.i).Single()),
                               Expression.Constant(null, testMember.GetMemberType().MakeNullableType())),
                           tupleParameter),
                       Expression.Lambda(
                           Expression.MemberInit(
                               Expression.New(node.Type),
                               (from x in columnDescriptors
                                where node.Path.Contains(x.c.SourceType)
                                group x by x.c.SourceMember.MetadataToken into xg
                                select xg.Last() into x
                                select Expression.Bind(
                                    x.c.SourceMember,
                                    Expression.Convert(
                                        ValueTupleHelper.CreateMemberExpression(tupleType, tupleParameter, x.i),
                                        x.c.SourceMember.GetMemberType())))),
                           tupleParameter))).ToArray();

            var tableExpression
                = hierarchyRoot
                  .Aggregate <TableExpression>(
                      hierarchyRoot.Value.Table,
                      (accumulate, parent, child) =>
            {
                var keyComparisons
                    = from k in hierarchyRoot.Value.TableDescriptor.PrimaryKeyMembers
                      join l in parent.TableDescriptor.ColumnDescriptors
                      on k.MetadataToken equals l.SourceMember.MetadataToken
                      join r in child.TableDescriptor.ColumnDescriptors
                      on k.MetadataToken equals r.SourceMember.MetadataToken
                      select Expression.Equal(
                          new SqlColumnExpression(parent.Table, l.ColumnName, l.Type, l.IsNullable, null),
                          new SqlColumnExpression(child.Table, r.ColumnName, r.Type, r.IsNullable, null));

                return(new LeftJoinTableExpression(
                           accumulate,
                           child.Table,
                           keyComparisons.Aggregate(Expression.AndAlso),
                           hierarchyRoot.Value.Type));
            });

            return(new EnumerableRelationalQueryExpression(
                       new SelectExpression(
                           new ServerProjectionExpression(
                               new PolymorphicExpression(
                                   targetType,
                                   tupleNewExpression,
                                   polymorphicTypeDescriptors)),
                           tableExpression)));
        }
Esempio n. 2
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();
            }
        }
        private static Expression CreateTablePerTypeQueryExpression(
            Type targetType,
            TreeNode <TablePerTypeInfo> hierarchyRoot)
        {
            var columnDescriptors
                = hierarchyRoot
                  .Flatten()
                  .SelectMany(x => x.TableDescriptor.ColumnDescriptors)
                  .Select((c, i) => new { c, i });

            var columnExpressions
                = hierarchyRoot
                  .Flatten()
                  .SelectMany(x => x.Columns);

            var tupleType          = ValueTupleHelper.CreateTupleType(columnExpressions.Select(c => c.Type));
            var tupleNewExpression = ValueTupleHelper.CreateNewExpression(tupleType, columnExpressions);
            var tupleParameter     = Expression.Parameter(tupleType);

            // TODO: Use ExpandParameters instead
            // Use ExpandParameters on a LambdaExpression representing
            // the materializer for the concrete type. Expand it the
            // LambdaExpression so that all of the bindings are bound
            // to the tuple's field accessors.

            var polymorphicTypeDescriptors
                = (from node in hierarchyRoot.Flatten()
                   where !node.Type.GetTypeInfo().IsAbstract
                   let testMember = node.TableDescriptor.PrimaryKeyMembers.First()
                                    select new PolymorphicExpression.TypeDescriptor(
                       node.Type,
                       Expression.Lambda(
                           Expression.NotEqual(
                               ValueTupleHelper.CreateMemberExpression(
                                   tupleType,
                                   tupleParameter,
                                   (from x in columnDescriptors
                                    where x.c.SourceMember == testMember
                                    select x.i).Single()),
                               Expression.Constant(null, MakeNullableType(GetMemberType(testMember)))),
                           tupleParameter),
                       Expression.Lambda(
                           Expression.MemberInit(
                               Expression.New(node.Type),
                               (from x in columnDescriptors
                                where node.Path.Contains(x.c.SourceType)
                                group x by x.c.SourceMember.MetadataToken into xg
                                select xg.Last() into x
                                select Expression.Bind(
                                    x.c.SourceMember,
                                    Expression.Convert(
                                        ValueTupleHelper.CreateMemberExpression(tupleType, tupleParameter, x.i),
                                        GetMemberType(x.c.SourceMember))))),
                           tupleParameter))).ToArray();

            // TODO: Use ExpandParameters instead
            // Use ExpandParameters on a LambdaExpression representing
            // the primary key accessor for the root type. Expand it
            // once for the parent table and once for the child table
            // and use an Equal expression to compare the two.

            var tableExpression
                = hierarchyRoot
                  .Aggregate <TableExpression>(
                      hierarchyRoot.Value.Table,
                      (accumulate, parent, child) =>
            {
                var keyComparisons
                    = from k in hierarchyRoot.Value.TableDescriptor.PrimaryKeyMembers
                      join l in parent.TableDescriptor.ColumnDescriptors
                      on k.MetadataToken equals l.SourceMember.MetadataToken
                      join r in child.TableDescriptor.ColumnDescriptors
                      on k.MetadataToken equals r.SourceMember.MetadataToken
                      select Expression.Equal(
                          new SqlColumnExpression(parent.Table, l.ColumnName, l.Type, l.IsNullable),
                          new SqlColumnExpression(child.Table, r.ColumnName, r.Type, r.IsNullable));

                return(new LeftJoinExpression(
                           accumulate,
                           child.Table,
                           keyComparisons.Aggregate(Expression.AndAlso),
                           hierarchyRoot.Value.Type));
            });

            return(new EnumerableRelationalQueryExpression(
                       new SelectExpression(
                           new ServerProjectionExpression(
                               new PolymorphicExpression(
                                   targetType,
                                   tupleNewExpression,
                                   polymorphicTypeDescriptors)),
                           tableExpression)));
        }