Example #1
0
        private ExtendedNewExpression CreateNewExpression(IEntityType type, BaseTableExpression table, Func <IProperty, Expression> makeColumnExpression)
        {
            var constructorBinding = (ConstructorBinding)type[nameof(ConstructorBinding)];

            switch (constructorBinding)
            {
            case DirectConstructorBinding directConstructorBinding:
            {
                return(CreateNewExpression(type, directConstructorBinding, table, makeColumnExpression));
            }

            case FactoryMethodConstructorBinding factoryMethodConstructorBinding:
            {
                return(CreateNewExpression(type, factoryMethodConstructorBinding, table, makeColumnExpression));
            }

            case null:
            {
                return(new ExtendedNewExpression(type.ClrType));
            }

            default:
            {
                throw new NotSupportedException($"The {constructorBinding.GetType().Name} is not supported.");
            }
            }
        }
Example #2
0
        public static Expression CreateQueryExpression <TElement>()
        {
            var type = typeof(TElement);

            var annotation = type.GetTypeInfo().GetCustomAttribute <TableAttribute>();

            var table = new BaseTableExpression(
                annotation?.Schema ?? "dbo",
                annotation?.Name ?? type.Name,
                (annotation?.Name ?? type.Name).ToLower().First().ToString(),
                type);

            return(new EnumerableRelationalQueryExpression(
                       new SelectExpression(
                           new ServerProjectionExpression(
                               Expression.MemberInit(
                                   Expression.New(type),
                                   from property in type.GetTypeInfo().DeclaredProperties
                                   where property.PropertyType.IsScalarType()
                                   let nullable =
                                       (property.PropertyType.IsConstructedGenericType &&
                                        property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) ||
                                       (!property.PropertyType.GetTypeInfo().IsValueType &&
                                        property.GetCustomAttribute <RequiredAttribute>() == null)
                                       let column = new SqlColumnExpression(table, property.Name, property.PropertyType, nullable)
                                                    select Expression.Bind(property, column))),
                           table)));
        }
        private static IEnumerable <Expression> CreateTablePerTypeQueryExpressions(
            Type rootType,
            IEnumerable <ImpatientTableDescriptor> tableDescriptors)
        {
            var inheritancePaths
                = (from t in tableDescriptors
                   select GetInheritancePath(t.SourceType, rootType) into p
                   where !p.First().GetTypeInfo().IsAbstract
                   orderby p.Count()
                   select p.Reverse()).ToArray();

            return(from t in tableDescriptors
                   let p = (from p in inheritancePaths where p.Contains(t.SourceType) select p)
                           from r in TreeNode.Treeify(p)
                           select CreateTablePerTypeQueryExpression(t.SourceType, r.Transform(node =>
            {
                var descriptor = tableDescriptors.Single(d => d.SourceType == node.Value);

                var table
                    = new BaseTableExpression(
                          descriptor.SchemaName,
                          descriptor.TableName,
                          descriptor.TableName.ToLower().Substring(0, 1),
                          descriptor.SourceType);

                var columns
                    = from cd in descriptor.ColumnDescriptors
                      let nullable = cd.SourceType != rootType
                                     select new SqlColumnExpression(
                          table,
                          cd.ColumnName,
                          cd.Type.MakeNullableType(),
                          nullable || cd.IsNullable,
                          null);

                return new TablePerTypeInfo
                {
                    Type = node.Value,
                    Path = GetInheritancePath(node.Value, rootType),
                    TableDescriptor = descriptor,
                    Table = table,
                    Columns = columns.ToArray(),
                };
            })));
        }
        protected override Expression VisitExtension(Expression node)
        {
            if (alias != null &&
                !alias.StartsWith("<>") &&
                node is EnumerableRelationalQueryExpression enumerableRelationalQueryExpression &&
                enumerableRelationalQueryExpression.SelectExpression.Table is BaseTableExpression oldTableExpression)
            {
                var newTableExpression
                    = new BaseTableExpression(
                          oldTableExpression.SchemaName,
                          oldTableExpression.TableName,
                          alias,
                          oldTableExpression.Type);

                return(enumerableRelationalQueryExpression.Replace(oldTableExpression, newTableExpression));
            }

            return(base.VisitExtension(node));
        }
Example #5
0
        private Expression CreateMaterializationExpression(IEntityType type, BaseTableExpression table, Func <IProperty, Expression> makeColumnExpression)
        {
            var properties
                = (from p in type.GetProperties()
                   where !p.IsShadowProperty
                   select p).ToList();

            var navigations
                = (from n in type.GetNavigations()
                   where n.ForeignKey.IsOwnership && !n.IsDependentToPrincipal()
                   select n).ToList();

            var services
                = (from s in type.GetServiceProperties()
                   select s).ToList();

            var newExpression = CreateNewExpression(type, table, makeColumnExpression);

            properties.RemoveAll(p => newExpression.WritableMembers.Contains(p.GetWritableMemberInfo()));
            navigations.RemoveAll(p => newExpression.WritableMembers.Contains(p.GetWritableMemberInfo()));
            services.RemoveAll(p => newExpression.WritableMembers.Contains(p.GetWritableMemberInfo()));

            var arguments       = new Expression[properties.Count + navigations.Count + services.Count];
            var readableMembers = new MemberInfo[arguments.Length];
            var writableMembers = new MemberInfo[arguments.Length];

            var c = properties.Count;
            var d = 0;

            for (var i = 0; i < c; i++)
            {
                var property = properties[i - d];

                arguments[i]       = makeColumnExpression(property);
                readableMembers[i] = property.GetReadableMemberInfo();
                writableMembers[i] = property.GetWritableMemberInfo();
            }

            c += navigations.Count;
            d += properties.Count;

            for (var i = d; i < c; i++)
            {
                var navigation = navigations[i - d];

                arguments[i]       = CreateMaterializationExpression(navigation.GetTargetType(), table, makeColumnExpression);
                readableMembers[i] = navigation.GetReadableMemberInfo();
                writableMembers[i] = navigation.GetWritableMemberInfo();
            }

            c += services.Count;
            d += navigations.Count;

            for (var i = d; i < c; i++)
            {
                var service = services[i - d];

                arguments[i]       = GetBindingExpression(type, service.GetParameterBinding(), makeColumnExpression);
                readableMembers[i] = service.GetReadableMemberInfo();
                writableMembers[i] = service.GetWritableMemberInfo();
            }

            var materializer
                = new ExtendedMemberInitExpression(
                      type.ClrType,
                      newExpression,
                      arguments,
                      readableMembers,
                      writableMembers);

            var keySelector
                = CreateMaterializationKeySelector(type);

            if (keySelector != null)
            {
                var shadowProperties
                    = from p in type.GetProperties()
                      where p.IsShadowProperty
                      select(property : p, expression : makeColumnExpression(p));

                return(new EntityMaterializationExpression(
                           type,
                           IdentityMapMode.IdentityMap,
                           keySelector,
                           shadowProperties.Select(s => s.property),
                           shadowProperties.Select(s => s.expression),
                           materializer));
            }

            return(materializer);
        }
Example #6
0
        private ExtendedNewExpression CreateNewExpression(IEntityType type, FactoryMethodConstructorBinding factoryMethodConstructorBinding, BaseTableExpression table, Func <IProperty, Expression> makeColumnExpression)
        {
            var factoryInstance
                = typeof(FactoryMethodConstructorBinding)
                  .GetField("_factoryInstance", BindingFlags.NonPublic | BindingFlags.Instance)
                  ?.GetValue(factoryMethodConstructorBinding);

            var factoryMethod
                = typeof(FactoryMethodConstructorBinding)
                  .GetField("_factoryMethod", BindingFlags.NonPublic | BindingFlags.Instance)
                  ?.GetValue(factoryMethodConstructorBinding) as MethodInfo;

            if (factoryInstance is null && (factoryMethod is null || !factoryMethod.IsStatic))
            {
                throw new NotSupportedException();
            }

            var bindings = factoryMethodConstructorBinding.ParameterBindings;

            if (!(bindings.Count == 3 &&
                  bindings[0] is EntityTypeParameterBinding entityTypeParameterBinding &&
                  bindings[1] is DefaultServiceParameterBinding defaultServiceParameterBinding &&
                  bindings[2] is ObjectArrayParameterBinding objectArrayParameterBinding))
            {
                throw new NotSupportedException();
            }

            var innerBindings
                = typeof(ObjectArrayParameterBinding)
                  .GetField("_bindings", BindingFlags.NonPublic | BindingFlags.Instance)
                  ?.GetValue(objectArrayParameterBinding) as IReadOnlyList <ParameterBinding>;

            if (innerBindings is null)
            {
                throw new NotSupportedException();
            }

            var constructor
                = factoryMethodConstructorBinding.RuntimeType
                  .GetConstructor(innerBindings.Select(b => b.ParameterType).ToArray());

            var arguments       = new Expression[innerBindings.Count];
            var readableMembers = new MemberInfo[arguments.Length];
            var writableMembers = new MemberInfo[arguments.Length];

            for (var i = 0; i < arguments.Length; i++)
            {
                var binding = innerBindings[i];

                arguments[i] = GetBindingExpression(type, binding, makeColumnExpression);

                if (binding.ConsumedProperties.ElementAtOrDefault(0) is IPropertyBase property)
                {
                    readableMembers[i] = property.GetReadableMemberInfo();
                    writableMembers[i] = property.GetWritableMemberInfo();
                }
            }

            return(new EFCoreProxyNewExpression(
                       factoryInstance is null ? null : Expression.Constant(factoryInstance),
                       factoryMethod,
                       new[]
            {
                GetBindingExpression(type, entityTypeParameterBinding, makeColumnExpression),
                GetBindingExpression(type, defaultServiceParameterBinding, makeColumnExpression)
            },
                       constructor,
                       arguments,
                       readableMembers,
                       writableMembers));
        }
Example #7
0
        private ExtendedNewExpression CreateNewExpression(IEntityType type, DirectConstructorBinding directConstructorBinding, BaseTableExpression table, Func <IProperty, Expression> makeColumnExpression)
        {
            var constructor     = directConstructorBinding.Constructor;
            var arguments       = new Expression[constructor.GetParameters().Length];
            var readableMembers = new MemberInfo[arguments.Length];
            var writableMembers = new MemberInfo[arguments.Length];

            for (var i = 0; i < arguments.Length; i++)
            {
                var binding = directConstructorBinding.ParameterBindings[i];

                arguments[i] = GetBindingExpression(type, binding, makeColumnExpression);

                if (binding.ConsumedProperties.ElementAtOrDefault(0) is IPropertyBase property)
                {
                    readableMembers[i] = property.GetReadableMemberInfo();
                    writableMembers[i] = property.GetWritableMemberInfo();
                }
            }

            return(new ExtendedNewExpression(constructor, arguments, readableMembers, writableMembers));
        }
Example #8
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();
            }
        }
Example #9
0
        public Expression CreateQueryExpression(Type elementType, DbContext context)
        {
            var targetType = context.Model.GetEntityTypes().SingleOrDefault(t => t.ClrType == elementType);

            Expression queryExpression;

            if (targetType.DefiningQuery != null)
            {
                queryExpression = targetType.DefiningQuery.Body;

                goto ApplyQueryFilters;
            }

            var rootType = targetType.RootType();

            var schemaName = rootType.Relational().Schema ?? context.Model.Relational().DefaultSchema;
            var tableName  = rootType.Relational().TableName;

            var table
                = new BaseTableExpression(
                      schemaName,
                      tableName,
                      tableName.Substring(0, 1).ToLower(),
                      rootType.ClrType);

            var materializer = CreateMaterializer(targetType, table);

            var projection = new ServerProjectionExpression(materializer);

            var selectExpression = new SelectExpression(projection, table);

            var discriminatingType = targetType;

            while (discriminatingType != null)
            {
                var discriminatorProperty = discriminatingType.Relational().DiscriminatorProperty;

                if (discriminatorProperty != null)
                {
                    selectExpression
                        = selectExpression.AddToPredicate(
                              new SqlInExpression(
                                  MakeColumnExpression(
                                      table,
                                      discriminatorProperty),
                                  Expression.NewArrayInit(
                                      discriminatorProperty.ClrType,
                                      from t in discriminatingType.GetDerivedTypesInclusive()
                                      where !t.IsAbstract()
                                      select Expression.Constant(
                                          t.Relational().DiscriminatorValue,
                                          discriminatorProperty.ClrType))));
                }

                discriminatingType = FindSameTabledPrincipalType(discriminatingType);
            }

            queryExpression = new EnumerableRelationalQueryExpression(selectExpression);

ApplyQueryFilters:

            var currentType = targetType;
            var recast = false;

            while (currentType != null)
            {
                if (currentType.QueryFilter != null)
                {
                    var filterBody = currentType.QueryFilter.Body;

                    var repointer
                        = new QueryFilterRepointingExpressionVisitor(
                              DbContextParameter.GetInstance(context.GetType()));

                    filterBody = repointer.Visit(filterBody);

                    // Use a method call instead of adding to the SelectExpression
                    // so the rewriting visitors are guaranteed to get their hands on the
                    // filter.
                    queryExpression
                        = Expression.Call(
                              queryableWhereMethodInfo.MakeGenericMethod(currentType.ClrType),
                              queryExpression,
                              Expression.Quote(
                                  Expression.Lambda(
                                      new QueryFilterExpression(filterBody),
                                      currentType.QueryFilter.Parameters)));

                    recast |= currentType != targetType;
                }

                currentType = currentType.BaseType;
            }

            if (recast)
            {
                queryExpression
                    = Expression.Call(
                          queryableCastMethodInfo.MakeGenericMethod(targetType.ClrType),
                          queryExpression);
            }

            return(queryExpression);
        }