Exemplo n.º 1
0
        static Expression GetMultipleQueryExpression(IBuildContext context, MappingSchema mappingSchema,
                                                     Expression expression, HashSet <ParameterExpression> parameters, out bool isLazy)
        {
            var valueExpression = EagerLoading.GenerateDetailsExpression(context, mappingSchema, expression);

            if (valueExpression == null)
            {
                isLazy = true;
                return(GetMultipleQueryExpressionLazy(context, mappingSchema, expression, parameters));
            }

            valueExpression = EagerLoading.AdjustType(valueExpression, expression.Type, mappingSchema);

            isLazy = false;
            return(valueExpression);
        }
Exemplo n.º 2
0
        static void CheckFilterFunc(Type expectedType, Type filterType, MappingSchema mappingSchema)
        {
            var propType = expectedType;

            if (EagerLoading.IsEnumerableType(expectedType, mappingSchema))
            {
                propType = EagerLoading.GetEnumerableElementType(expectedType, mappingSchema);
            }
            var itemType = typeof(Expression <>).IsSameOrParentOf(filterType) ?
                           filterType.GetGenericArguments()[0].GetGenericArguments()[0].GetGenericArguments()[0] :
                           filterType.GetGenericArguments()[0].GetGenericArguments()[0];

            if (propType != itemType)
            {
                throw new LinqException("Invalid filter function usage.");
            }
        }
        static Expression GetMultipleQueryExpression(IBuildContext context, MappingSchema mappingSchema,
                                                     Expression expression, HashSet <ParameterExpression> parameters, out bool isLazy)
        {
            if (!Common.Configuration.Linq.AllowMultipleQuery)
            {
                throw new LinqException("Multiple queries are not allowed. Set the 'LinqToDB.Common.Configuration.Linq.AllowMultipleQuery' flag to 'true' to allow multiple queries.");
            }

            var valueExpression = EagerLoading.GenerateDetailsExpression(context, mappingSchema, expression, parameters);

            if (valueExpression == null)
            {
                isLazy = true;
                return(GetMultipleQueryExpressionLazy(context, mappingSchema, expression, parameters));
            }

            valueExpression = EagerLoading.EnsureDestinationType(valueExpression, expression.Type, mappingSchema);

            isLazy = false;
            return(valueExpression);
        }
Exemplo n.º 4
0
        public Type GetElementType(MappingSchema mappingSchema)
        {
            var type = MemberInfo.GetMemberType();

            return(EagerLoading.GetEnumerableElementType(type, mappingSchema));
        }
Exemplo n.º 5
0
        public static Expression EnrichLoadWith(IDataContext dataContext, Expression table, Type entityType, List <LoadWithInfo[]> loadWith, MappingSchema mappingSchema)
        {
            var args       = new List <Expression>(2);
            var currentObj = table;

            foreach (var members in loadWith)
            {
                var  currentEntityType = entityType;
                var  isPrevEnumerable  = false;
                Type?prevMemberType    = null;

                foreach (var member in members)
                {
                    args.Clear();
                    args.Add(currentObj);

                    var memberType         = member.MemberInfo.GetMemberType();
                    var isEnumerableMember =
                        EagerLoading.IsEnumerableType(memberType, mappingSchema);

                    var desiredType = member.MemberInfo.IsMethodEx() ? currentEntityType : member.MemberInfo.DeclaringType;

                    var entityParam = Expression.Parameter(currentEntityType, "e");
                    var loadBody    = desiredType == currentEntityType
                                                ? (Expression)entityParam
                                                : Expression.Convert(entityParam, desiredType);

                    loadBody = Expression.MakeMemberAccess(loadBody, member.MemberInfo);
                    if (member.MemberFilter != null)
                    {
                        loadBody = member.MemberFilter.GetBody(loadBody);
                    }

                    var hasFilterFunc = member.FilterFunc != null;

                    if (isEnumerableMember && hasFilterFunc)
                    {
                        var propType       = EagerLoading.GetEnumerableElementType(memberType, mappingSchema);
                        var enumerableType = typeof(IEnumerable <>).MakeGenericType(propType);
                        if (loadBody.Type != enumerableType)
                        {
                            loadBody = Expression.Convert(loadBody, enumerableType);
                        }
                    }

                    args.Add(Expression.Quote(Expression.Lambda(loadBody, entityParam)));

                    if (hasFilterFunc)
                    {
                        args.Add(member.FilterFunc !);
                    }

                    MethodInfo method;
                    if (prevMemberType == null)
                    {
                        method = !hasFilterFunc
                                                        ? Methods.LinqToDB.LoadWith
                                                        : isEnumerableMember
                                                                ? Methods.LinqToDB.LoadWithManyFilter
                                                                : Methods.LinqToDB.LoadWithSingleFilter;


                        var propType = memberType;
                        if (hasFilterFunc && isEnumerableMember)
                        {
                            propType = EagerLoading.GetEnumerableElementType(propType, mappingSchema);
                        }
                        method = method.MakeGenericMethod(entityType, propType);
                    }
                    else
                    {
                        if (isPrevEnumerable)
                        {
                            if (!hasFilterFunc)
                            {
                                method = Methods.LinqToDB.ThenLoadFromMany;
                            }
                            else if (isEnumerableMember)
                            {
                                method = Methods.LinqToDB.ThenLoadFromManyManyFilter;
                            }
                            else
                            {
                                method = Methods.LinqToDB.ThenLoadFromManySingleFilter;
                            }
                        }
                        else
                        {
                            if (!hasFilterFunc)
                            {
                                method = Methods.LinqToDB.ThenLoadFromSingle;
                            }
                            else if (isEnumerableMember)
                            {
                                method = Methods.LinqToDB.ThenLoadFromSingleManyFilter;
                            }
                            else
                            {
                                method = Methods.LinqToDB.ThenLoadFromSingleSingleFilter;
                            }
                        }

                        var propType = memberType;
                        if (hasFilterFunc && isEnumerableMember)
                        {
                            propType = EagerLoading.GetEnumerableElementType(propType, mappingSchema);
                        }
                        method = method.MakeGenericMethod(entityType, prevMemberType, propType);
                    }

                    currentObj = Expression.Call(method, args);

                    isPrevEnumerable = isEnumerableMember && !hasFilterFunc;

                    if (isEnumerableMember)
                    {
                        memberType = EagerLoading.GetEnumerableElementType(memberType, mappingSchema);
                    }

                    prevMemberType    = memberType;
                    currentEntityType = memberType;
                }
            }

            return(currentObj);
        }
Exemplo n.º 6
0
        // Returns
        // (ParentType p) => dc.GetTable<ObjectType>().Where(...)
        // (ParentType p) => dc.GetTable<ObjectType>().Where(...).DefaultIfEmpty
        public static LambdaExpression CreateAssociationQueryLambda(ExpressionBuilder builder, AccessorMember onMember, AssociationDescriptor association,
                                                                    Type parentOriginalType,
                                                                    Type parentType,
                                                                    Type objectType, bool inline, bool enforceDefault,
                                                                    List <LoadWithInfo[]>?loadWith, out bool isLeft)
        {
            var dataContextConstant = Expression.Constant(builder.DataContext, builder.DataContext.GetType());

            // We are trying to keep fast cache hit behaviour, so cache check should be added only if needed
            //
            bool shouldAddCacheCheck = false;

            bool cacheCheckAdded = false;

            LambdaExpression?definedQueryMethod = null;

            if (association.HasQueryMethod())
            {
                // here we tell for Expression Comparer to compare optimized Association expressions
                //
                definedQueryMethod = (LambdaExpression)builder.AddQueryableMemberAccessors(onMember, builder.DataContext, (mi, dc) =>
                {
                    var queryLambda         = association.GetQueryMethod(parentType, objectType) ?? throw new InvalidOperationException();
                    var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                    var optimizedExpr       = optimizationContext.ExposeExpression(queryLambda);
                    optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                    optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                    return(optimizedExpr);
                });

                cacheCheckAdded = true;

                var parameterMatch = new Dictionary <ParameterExpression, Expression>();
                if (onMember.Arguments == null)
                {
                    if (definedQueryMethod.Parameters.Count > 1 && typeof(IDataContext).IsSameOrParentOf(definedQueryMethod.Parameters[1].Type))
                    {
                        parameterMatch.Add(definedQueryMethod.Parameters[1], dataContextConstant);
                    }
                }
                else
                {
                    var definedCount   = definedQueryMethod.Parameters.Count;
                    var argumentsCount = onMember.Arguments.Count;
                    var diff           = definedCount - argumentsCount;
                    for (int i = definedCount - 1; i >= diff; i--)
                    {
                        parameterMatch.Add(definedQueryMethod.Parameters[i], onMember.Arguments[i - diff]);
                    }
                }

                var body = definedQueryMethod.Body.Transform(e =>
                {
                    if (e.NodeType == ExpressionType.Parameter &&
                        parameterMatch.TryGetValue((ParameterExpression)e, out var newExpression))
                    {
                        return(newExpression);
                    }

                    return(e);
                });

                definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters[0]);
            }

            var shouldAddDefaultIfEmpty = enforceDefault;

            if (definedQueryMethod == null)
            {
                var parentParam = Expression.Parameter(parentType, "parent");
                var childParam  = Expression.Parameter(objectType, association.AliasName);

                var parentAccessor = TypeAccessor.GetAccessor(parentType);
                var childAccessor  = TypeAccessor.GetAccessor(objectType);

                Expression?predicate = null;
                for (var i = 0; i < association.ThisKey.Length; i++)
                {
                    var parentName   = association.ThisKey[i];
                    var parentMember = parentAccessor.Members.Find(m => m.MemberInfo.Name == parentName);

                    if (parentMember == null)
                    {
                        throw new LinqException("Association key '{0}' not found for type '{1}.", parentName,
                                                parentType);
                    }

                    var childName   = association.OtherKey[i];
                    var childMember = childAccessor.Members.Find(m => m.MemberInfo.Name == childName);

                    if (childMember == null)
                    {
                        throw new LinqException("Association key '{0}' not found for type '{1}.", childName,
                                                objectType);
                    }

                    var current = ExpressionBuilder.Equal(builder.MappingSchema,
                                                          Expression.MakeMemberAccess(parentParam, parentMember.MemberInfo),
                                                          Expression.MakeMemberAccess(childParam, childMember.MemberInfo));

                    predicate = predicate == null ? current : Expression.AndAlso(predicate, current);
                }

                var expressionPredicate = association.GetPredicate(parentType, objectType);

                if (expressionPredicate != null)
                {
                    shouldAddDefaultIfEmpty = true;
                    shouldAddCacheCheck     = true;

                    var replacedBody = expressionPredicate.GetBody(parentParam, childParam);

                    predicate = predicate == null ? replacedBody : Expression.AndAlso(predicate, replacedBody);
                }

                if (predicate == null)
                {
                    throw new LinqException("Can not generate Association predicate");
                }

                if (inline && !shouldAddDefaultIfEmpty)
                {
                    var ed = builder.MappingSchema.GetEntityDescriptor(objectType);
                    if (ed.QueryFilterFunc != null)
                    {
                        shouldAddDefaultIfEmpty = true;
                        shouldAddCacheCheck     = true;
                    }
                }

                var queryParam = Expression.Call(Methods.LinqToDB.GetTable.MakeGenericMethod(objectType), dataContextConstant);

                var        filterLambda = Expression.Lambda(predicate, childParam);
                Expression body         = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(objectType), queryParam,
                                                          filterLambda);

                definedQueryMethod = Expression.Lambda(body, parentParam);
            }
            else
            {
                shouldAddDefaultIfEmpty = true;
                var bodyExpression = definedQueryMethod.Body.Unwrap();
                if (bodyExpression.NodeType == ExpressionType.Call)
                {
                    var mc = (MethodCallExpression)bodyExpression;
                    if (mc.IsSameGenericMethod(Methods.Queryable.DefaultIfEmpty, Methods.Queryable.DefaultIfEmptyValue))
                    {
                        shouldAddDefaultIfEmpty = false;
                    }
                }
            }

            if (!cacheCheckAdded && shouldAddCacheCheck)
            {
                // here we tell for Expression Comparer to compare optimized Association expressions
                //
                var closureExpr = definedQueryMethod;
                definedQueryMethod = (LambdaExpression)builder.AddQueryableMemberAccessors(onMember, builder.DataContext, (mi, dc) =>
                {
                    var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                    var optimizedExpr       = optimizationContext.ExposeExpression(closureExpr);
                    optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                    optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                    return(optimizedExpr);
                });
            }

            if (loadWith != null)
            {
                var associationLoadWith = GetLoadWith(loadWith)?
                                          .FirstOrDefault(li => li.Info.MemberInfo == association.MemberInfo);

                if (associationLoadWith != null &&
                    (associationLoadWith.Info.MemberFilter != null || associationLoadWith.Info.FilterFunc != null))
                {
                    var body = definedQueryMethod.Body.Unwrap();

                    var memberFilter = associationLoadWith.Info.MemberFilter;
                    if (memberFilter != null)
                    {
                        var elementType = EagerLoading.GetEnumerableElementType(memberFilter.Parameters[0].Type,
                                                                                builder.MappingSchema);
                        var filtered   = Expression.Convert(body, typeof(IEnumerable <>).MakeGenericType(elementType));
                        var filterBody = memberFilter.GetBody(filtered);
                        body = Expression.Call(
                            Methods.Enumerable.AsQueryable.MakeGenericMethod(objectType), filterBody);
                    }

                    var loadWithFunc = associationLoadWith.Info.FilterFunc;

                    if (loadWithFunc != null)
                    {
                        loadWithFunc = loadWithFunc.Unwrap();
                        if (loadWithFunc is LambdaExpression lambda)
                        {
                            body = lambda.GetBody(body);
                        }
                        else
                        {
                            var filterDelegate = loadWithFunc.EvaluateExpression <Delegate>() ??
                                                 throw new LinqException("Cannot convert filter function '{loadWithFunc}' to Delegate.");

                            var arumentType = filterDelegate.GetType().GetGenericArguments()[0].GetGenericArguments()[0];
                            // check for fake argument q => q
                            if (arumentType.IsSameOrParentOf(objectType))
                            {
                                var query    = ExpressionQueryImpl.CreateQuery(objectType, builder.DataContext, body);
                                var filtered = (IQueryable)filterDelegate.DynamicInvoke(query) !;
                                body = filtered.Expression;
                            }
                        }
                    }

                    definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);
                }

                if (associationLoadWith?.NextLoadWith != null && associationLoadWith.NextLoadWith.Count > 0)
                {
                    definedQueryMethod = (LambdaExpression)EnrichTablesWithLoadWith(builder.DataContext, definedQueryMethod, objectType,
                                                                                    associationLoadWith.NextLoadWith, builder.MappingSchema);
                }
            }

            if (parentOriginalType != parentType)
            {
                // add discriminator filter
                var ed = builder.MappingSchema.GetEntityDescriptor(parentOriginalType);
                foreach (var inheritanceMapping in ed.InheritanceMapping)
                {
                    if (inheritanceMapping.Type == parentType)
                    {
                        var objParam     = Expression.Parameter(objectType, "o");
                        var filterLambda = Expression.Lambda(ExpressionBuilder.Equal(builder.MappingSchema,
                                                                                     Expression.MakeMemberAccess(definedQueryMethod.Parameters[0], inheritanceMapping.Discriminator.MemberInfo),
                                                                                     Expression.Constant(inheritanceMapping.Code)), objParam);

                        var body = definedQueryMethod.Body.Unwrap();
                        body = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(objectType),
                                               body, filterLambda);
                        definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);

                        shouldAddDefaultIfEmpty = true;
                        break;
                    }
                }
            }

            if (inline && shouldAddDefaultIfEmpty)
            {
                var body = definedQueryMethod.Body.Unwrap();
                body = Expression.Call(Methods.Queryable.DefaultIfEmpty.MakeGenericMethod(objectType), body);
                definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);
                isLeft             = true;
            }
            else
            {
                isLeft = false;
            }

            definedQueryMethod = (LambdaExpression)builder.ConvertExpressionTree(definedQueryMethod);
            definedQueryMethod = (LambdaExpression)builder.ConvertExpression(definedQueryMethod);
            definedQueryMethod = (LambdaExpression)definedQueryMethod.OptimizeExpression() !;

            return(definedQueryMethod);
        }
Exemplo n.º 7
0
		SqlInfo[] ConvertToIndexInternal(Expression? expression, int level, ConvertFlags flags)
		{
			if (IsScalar)
			{
				if (Body.NodeType == ExpressionType.Parameter)
					for (var i = 0; i < Sequence.Length; i++)
						if (Body == Lambda.Parameters[i])
							return Sequence[i].ConvertToIndex(expression, level, flags);

				if (expression == null)
				{
					var key = Tuple.Create((MemberInfo?)null, flags);

					if (!_memberIndex.TryGetValue(key, out var idx))
					{
						idx = ConvertToSql(null, 0, flags);

						foreach (var info in idx)
							SetInfo(info, null);

						_memberIndex.Add(key, idx);
					}

					return idx;
				}

				switch (flags)
				{
					case ConvertFlags.Field :
					case ConvertFlags.Key   :
					case ConvertFlags.All   :
						return ProcessScalar(
							expression,
							level,
							(ctx, ex, l) => ctx.ConvertToIndex(ex, l, flags),
							() => GetSequence(expression, level)!.ConvertToIndex(expression, level + 1, flags));
				}
			}
			else
			{
				if (expression == null)
				{
					switch (flags)
					{
						case ConvertFlags.Field :
						case ConvertFlags.Key   :
						case ConvertFlags.All   :
							{
								var p = Expression.Parameter(Body.Type, "p");
								var q =
									from m in Members.Keys
									where !(m is MethodInfo || EagerLoading.IsDetailsMember(m))
									select new SqlData
									{
										Sql    = ConvertToIndex(Expression.MakeMemberAccess(p, m), 1, flags),
										Member = m
									} into mm
									from m in mm.Sql.Select(s => s.Clone(mm.Member))
									select m;

								return q.ToArray();
							}
					}
				}

				switch (flags)
				{
					case ConvertFlags.All   :
					case ConvertFlags.Key   :
					case ConvertFlags.Field :
						{
							if (level == 0)
							{
								var idx = Builder.ConvertExpressions(this, expression!, flags);

								foreach (var info in idx)
									SetInfo(info, null);

								return idx;
							}

							var levelExpression = expression!.GetLevelExpression(Builder.MappingSchema, level);

							switch (levelExpression.NodeType)
							{
								case ExpressionType.MemberAccess :
									{
										if (levelExpression == expression)
										{
											var member = Tuple.Create((MemberInfo?)((MemberExpression)levelExpression).Member, flags);

											if (!_memberIndex.TryGetValue(member, out var idx))
											{
												idx = ConvertToSql(expression, level, flags);

												if (flags == ConvertFlags.Field && idx.Length != 1)
													throw new InvalidOperationException();

												foreach (var info in idx)
													SetInfo(info, member.Item1);

												_memberIndex.Add(member, idx);
											}

											return idx;
										}

										return ProcessMemberAccess(
											expression!,
											(MemberExpression)levelExpression,
											level,
											(n, ctx, ex, l, _) => n == 0 ?
												GetSequence(expression!, level)!.ConvertToIndex(expression, level + 1, flags) :
												ctx.ConvertToIndex(ex, l, flags));
									}

								case ExpressionType.Parameter:
								case ExpressionType.Extension:

									if (levelExpression != expression)
										return GetSequence(expression!, level)!.ConvertToIndex(expression, level + 1, flags);
									break;
							}

							break;
						}
				}
			}

			throw new NotImplementedException();
		}
Exemplo n.º 8
0
		public virtual SqlInfo[] ConvertToSql(Expression? expression, int level, ConvertFlags flags)
		{
			if (expression != null && level > 0 && expression.NodeType == ExpressionType.Call)
			{
				var e = (MethodCallExpression)expression;

				if (e.Method.DeclaringType == typeof(Enumerable) && !typeof(IGrouping<,>).IsSameOrParentOf(e.Arguments[0].Type))
				{
					return new[] { new SqlInfo { Sql = Builder.SubQueryToSql(this, e) } };
				}
			}

			if (IsScalar)
			{
				if (expression == null)
					return Builder.ConvertExpressions(this, Body, flags);

				switch (flags)
				{
					case ConvertFlags.Field :
					case ConvertFlags.Key   :
					case ConvertFlags.All   :
						{
							if (Body.NodeType != ExpressionType.Parameter && level == 0)
							{
								var levelExpression = expression.GetLevelExpression(Builder.MappingSchema, level);

								if (levelExpression != expression)
									if (flags != ConvertFlags.Field && IsExpression(expression, level, RequestFor.Field).Result)
										flags = ConvertFlags.Field;
							}

							return ProcessScalar(
								expression,
								level,
								(ctx, ex, l) => ctx.ConvertToSql(ex, l, flags),
								() => new[] { new SqlInfo { Sql = Builder.ConvertToSql(this, expression) } });
						}
				}
			}
			else
			{
				if (expression == null)
				{
					if (flags != ConvertFlags.Field)
					{
						var q =
							from m in Members
							where !(m.Key is MethodInfo || EagerLoading.IsDetailsMember(m.Key))
							select ConvertMember(m.Key, m.Value, flags) into mm
							from m in mm
							select m;

						return q.ToArray();
					}

					throw new NotImplementedException();
				}

				switch (flags)
				{
					case ConvertFlags.All   :
					case ConvertFlags.Key   :
					case ConvertFlags.Field :
						{
							var levelExpression = expression.GetLevelExpression(Builder.MappingSchema, level);
							levelExpression = levelExpression.Unwrap();

							switch (levelExpression.NodeType)
							{
								case ExpressionType.MemberAccess :
									{
										if (level != 0 && levelExpression == expression)
										{
											var member = ((MemberExpression)levelExpression).Member;

											if (!_sql.TryGetValue(member, out var sql))
											{
												var memberExpression = GetMemberExpression(
													member, levelExpression == expression, levelExpression.Type, expression);

												sql = ConvertExpressions(memberExpression, flags)
													.Select(si => si.Clone(member)).ToArray();

												_sql.Add(member, sql);
											}

											return sql;
										}

										return ProcessMemberAccess(
											expression, (MemberExpression)levelExpression, level,
											(n,ctx,ex,l,mex) =>
											{
												switch (n)
												{
													case 0 :
														var buildExpression = GetExpression(expression, levelExpression, mex);
														return ConvertExpressions(buildExpression, flags);
													default:
														return ctx.ConvertToSql(ex, l, flags);
												}
											});
									}

								case ExpressionType.Parameter:
									if (levelExpression != expression)
										return GetSequence(expression, level)!.ConvertToSql(expression, level + 1, flags);

									if (level == 0)
										return GetSequence(expression, level)!.ConvertToSql(null, 0, flags);

									break;

								case ExpressionType.Extension:
									{
										if (levelExpression is ContextRefExpression)
										{
											if (levelExpression != expression)
												return GetSequence(expression, level)!.ConvertToSql(expression,
													level + 1,
													flags);

											if (level == 0)
												return GetSequence(expression, level)!.ConvertToSql(null, 0, flags);
										}

										goto default;
									}

								default:
									if (level == 0)
										return Builder.ConvertExpressions(this, expression, flags);
									break;
							}

							break;
						}
				}
			}

			throw new NotImplementedException();
		}
Exemplo n.º 9
0
 public GetItemExpression(Expression expression, MappingSchema mappingSchema)
 {
     Expression = expression;
     _type      = EagerLoading.GetEnumerableElementType(expression.Type, mappingSchema);
 }