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."); } } }
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)); }
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); }
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)); }
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)); }
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(); } }
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); }