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))); }
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))); }