Пример #1
0
            static Expression СonvertMemberExpression(Expression expr, Expression root, LambdaExpression l)
            {
                var body  = l.Body.Unwrap();
                var parms = l.Parameters.ToDictionary(p => p);
                var ex    = body.Transform(wpi =>
                {
                    if (wpi.NodeType == ExpressionType.Parameter && parms.ContainsKey((ParameterExpression)wpi))
                    {
                        if (wpi.Type.IsSameOrParentOf(root.Type))
                        {
                            return(root);
                        }

                        if (ExpressionBuilder.DataContextParam.Type.IsSameOrParentOf(wpi.Type))
                        {
                            if (ExpressionBuilder.DataContextParam.Type != wpi.Type)
                            {
                                return(Expression.Convert(ExpressionBuilder.DataContextParam, wpi.Type));
                            }
                            return(ExpressionBuilder.DataContextParam);
                        }

                        throw new LinqToDBException($"Can't convert {wpi} to expression.");
                    }

                    return(wpi);
                });

                if (ex.Type != expr.Type)
                {
                    ex = new ChangeTypeExpression(ex, expr.Type);
                }
                return(ex);
            }
Пример #2
0
        public Expression ExposeExpression(Expression expression)
        {
            if (_exposedCache.TryGetValue(expression, out var result))
            {
                return(result);
            }

            result = expression.Transform(expr =>
            {
                if (_exposedCache.TryGetValue(expr, out var aleradyExposed))
                {
                    return(new TransformInfo(aleradyExposed, true));
                }

                switch (expr.NodeType)
                {
                case ExpressionType.MemberAccess:
                    {
                        var me = (MemberExpression)expr;

                        if (me.Member.IsNullableHasValueMember())
                        {
                            return(new TransformInfo(Expression.NotEqual(me.Expression, Expression.Constant(null, me.Expression.Type)), false, true));
                        }

                        if (CanBeCompiled(expr))
                        {
                            break;
                        }

                        var l = ConvertMethodExpression(me.Expression?.Type ?? me.Member.ReflectedType !, me.Member, out var alias);

                        if (l != null)
                        {
                            var body  = l.Body.Unwrap();
                            var parms = l.Parameters.ToDictionary(p => p);
                            var ex    = body.Transform(wpi =>
                            {
                                if (wpi.NodeType == ExpressionType.Parameter && parms.ContainsKey((ParameterExpression)wpi))
                                {
                                    if (wpi.Type.IsSameOrParentOf(me.Expression !.Type))
                                    {
                                        return(me.Expression);
                                    }

                                    if (ExpressionBuilder.DataContextParam.Type.IsSameOrParentOf(wpi.Type))
                                    {
                                        if (ExpressionBuilder.DataContextParam.Type != wpi.Type)
                                        {
                                            return(Expression.Convert(ExpressionBuilder.DataContextParam, wpi.Type));
                                        }
                                        return(ExpressionBuilder.DataContextParam);
                                    }

                                    throw new LinqToDBException($"Can't convert {wpi} to expression.");
                                }

                                return(wpi);
                            });

                            if (ex.Type != expr.Type)
                            {
                                ex = new ChangeTypeExpression(ex, expr.Type);
                            }

                            return(new TransformInfo(AliasCall(ex, alias !), false, true));
                        }

                        break;
                    }

                case ExpressionType.Convert:
                    {
                        var ex = (UnaryExpression)expr;
                        if (ex.Method != null)
                        {
                            var l = ConvertMethodExpression(ex.Method.DeclaringType !, ex.Method, out var alias);
                            if (l != null)
                            {
                                var exposed = l.GetBody(ex.Operand);
                                return(new TransformInfo(exposed, false, true));
                            }
                        }
                        break;
                    }

                case ExpressionType.Constant:
                    {
                        var c = (ConstantExpression)expr;

                        // Fix Mono behaviour.
                        //
                        //if (c.Value is IExpressionQuery)
                        //	return ((IQueryable)c.Value).Expression;

                        if (c.Value is IQueryable queryable && !(queryable is ITable))
                        {
                            var e = queryable.Expression;

                            if (!_visitedExpressions !.Contains(e))
                            {
                                _visitedExpressions !.Add(e);
                                return(new TransformInfo(e, false, true));
                            }
                        }

                        break;
                    }

                case ExpressionType.Invoke:
                    {
                        var invocation = (InvocationExpression)expr;
                        if (invocation.Expression.NodeType == ExpressionType.Call)
                        {
                            var mc = (MethodCallExpression)invocation.Expression;
                            if (mc.Method.Name == "Compile" &&
                                typeof(LambdaExpression).IsSameOrParentOf(mc.Method.DeclaringType !))
                            {
                                if (mc.Object.EvaluateExpression() is LambdaExpression lambds)
                                {
                                    var map = new Dictionary <Expression, Expression>();
                                    for (int i = 0; i < invocation.Arguments.Count; i++)
                                    {
                                        map.Add(lambds.Parameters[i], invocation.Arguments[i]);
                                    }

                                    var newBody = lambds.Body.Transform(se =>
                                    {
                                        if (se.NodeType == ExpressionType.Parameter &&
                                            map.TryGetValue(se, out var newExpr))
                                        {
                                            return(newExpr);
                                        }
                                        return(se);
                                    });

                                    return(new TransformInfo(newBody, false, true));
                                }
                            }
                        }
                        break;
                    }
                }

                _exposedCache.Add(expr, expr);

                return(new TransformInfo(expr, false));
            });

            _exposedCache[expression] = result;

            return(result);
        }
Пример #3
0
        //Creates a cached generator for the converter to use for all data tables with a matching set of data columns. Due to our assumption that data types are loose in
        //data tables, this will generate an extremely generic converter that doesn't statically use the data table's data typing.
        private static Func <DataRow, TEntity> GetConverter(IDictionary <DataColumn, string> columnToMemberMap)
        {
            List <string> columnNames = columnToMemberMap.Select(k => k.Key.ColumnName).ToList();

            if (!converters.TryGetValue(columnNames, out Func <DataRow, TEntity> value))
            {
                NewExpression       instantiate = Expression.New(type);
                ParameterExpression dataRow     = Expression.Parameter(typeof(DataRow), "dataRow");
                //Declare a dictionary to prevent creating more than 1 instance of a temporary parser type.
                ConcurrentDictionary <Type, ParameterExpression> parameters = new ConcurrentDictionary <Type, ParameterExpression>();
                List <MemberBinding> memberBindings = new List <MemberBinding>();
                //Iterate over the column map and generate the expressions needed.
                foreach (KeyValuePair <DataColumn, string> columnToMember in columnToMemberMap)
                {
                    if (TryGetMemberInfo(columnToMember.Value, columnToMember.Key.Table.CaseSensitive, out MemberInfo memberInfo))
                    {
                        //Get a member expression. This is used for type resolution.
                        Type memberType = Expression.MakeMemberAccess(instantiate, memberInfo).Type,
                                                           realType = Nullable.GetUnderlyingType(memberType) ?? memberType;
                        //Get the data column expression used to access the data row field.
                        DataColumnExpression dataColumn = DataExpression.DataColumn(DataExpression.DataTable(dataRow), columnToMember.Key.ColumnName);
                        //Get the is null expression used to determine if the data field is null.
                        DataFieldIsNullExpression callDataFieldIsNull = DataExpression.DataFieldIsNull(dataRow, dataColumn);
                        ConditionalExpression     dataFieldIsNull     = Expression.Condition(callDataFieldIsNull, Expression.Default(memberType),
                                                                                             Expression.Default(memberType));
                        //Get the is assignable from expression used to determine if the data column can be converted to the member type.
                        IsAssignableFromExpression callIsAssignableFrom = DataExpression.IsAssignableFrom(DataExpression.DataType(dataColumn), memberType);
                        //Get the is assignable from true result. If member type is string, then call the trim method on the string.
                        Expression isAssignableFromTrue = Expression.Convert(DataExpression.DataField(dataRow, dataColumn), memberType);
                        if (typeof(string).IsAssignableFrom(memberType))
                        {
                            isAssignableFromTrue = Expression.Call(isAssignableFromTrue, "Trim", Type.EmptyTypes);
                        }
                        //Get the conditional expression used to process the data column type conversion.
                        ConditionalExpression isAssignableFrom = Expression.Condition(callIsAssignableFrom,
                                                                                      isAssignableFromTrue, Expression.Default(memberType));
                        if (realType.GetMethods().Any(m => m.Name == "TryParse"))
                        {
                            //Get the is string parameter expression used for try parsing.
                            ParameterExpression stringLocal = parameters.GetOrAdd(typeof(string), Expression.Variable(typeof(string))),
                                                outLocal    = parameters.GetOrAdd(realType, Expression.Variable(realType));
                            //Get the type is assign and try parse expressions used for try parsing.
                            TypeIsAssignExpression typeIsAssign = DataExpression.TypeIsAssign(
                                DataExpression.DataField(dataRow, dataColumn), stringLocal);
                            TryParseExpression callTryParse = DataExpression.TryParse(stringLocal, outLocal);
                            //Get the conditional expression used to process the try parse conversion.
                            ConditionalExpression tryParse = Expression.Condition(
                                Expression.AndAlso(typeIsAssign, callTryParse),
                                Expression.Convert(outLocal, memberType), Expression.Default(memberType));
                            //Update is assignable from with the try parse method.
                            isAssignableFrom = isAssignableFrom.Update(isAssignableFrom.Test, isAssignableFrom.IfTrue, tryParse);
                        }
                        if (typeof(IConvertible).IsAssignableFrom(realType))
                        {
                            //Get the change type expression.
                            ChangeTypeExpression callChangeType = DataExpression.ChangeType(
                                DataExpression.DataField(dataRow, dataColumn), realType);
                            //Get the conversion expression for the change type. If member type is string, then call the trim method on the string.
                            Expression changeType = Expression.Convert(callChangeType, memberType);
                            if (typeof(string).IsAssignableFrom(memberType))
                            {
                                changeType = Expression.Call(changeType, "Trim", Type.EmptyTypes);
                            }
                            //Determine if try parse is set. If so, update it and is assignable from. Otherwise, add change type to is assignable from.
                            if (isAssignableFrom.IfFalse is ConditionalExpression tryParse)
                            {
                                tryParse         = tryParse.Update(tryParse.Test, tryParse.IfTrue, changeType);
                                isAssignableFrom = isAssignableFrom.Update(isAssignableFrom.Test, isAssignableFrom.IfTrue, tryParse);
                            }
                            else
                            {
                                isAssignableFrom = isAssignableFrom.Update(isAssignableFrom.Test, isAssignableFrom.IfTrue, changeType);
                            }
                        }
                        dataFieldIsNull = dataFieldIsNull.Update(dataFieldIsNull.Test, dataFieldIsNull.IfTrue, isAssignableFrom);
                        //Silently ignore conversion errors with the try catch expression. This is mostly for testing purposes and might be removed
                        //in the future.
                        memberBindings.Add(Expression.Bind(memberInfo, Expression.TryCatch(dataFieldIsNull,
                                                                                           Expression.Catch(typeof(Exception), Expression.Default(memberType)))));
                    }
                }
                //Get the converter lambda expression, creating the initialization block with its parameters.
                Expression <Func <DataRow, TEntity> > converter = Expression.Lambda <Func <DataRow, TEntity> >(
                    Expression.Block(parameters.Values, Expression.MemberInit(instantiate, memberBindings)), dataRow);
                //Use expression reducer to reduce the conversion lambda expression, then compile and return the delegate.
                converter = (Expression <Func <DataRow, TEntity> >) new ExpressionReducer().Visit(converter);
                value     = converters.GetOrAdd(new HashSet <string>(columnNames), converter.Compile());
            }
            return(value);
        }