Определение объема цели MemberExpressions - PullRequest
2 голосов
/ 19 января 2009

Есть здесь кто-нибудь с опытом написания пользовательских провайдеров Linq?

То, что я пытаюсь сделать, это сказать, должно ли MemberExpression, являющееся свойством бизнес-объекта, быть включено в SQL или рассматриваться как константа, потому что это из локальной переменной, которая, как оказалось, является бизнес-объектом .

Так, например, если у вас есть это:

Customer c = LoadCustomerFromDatabase();

var orders = from o in db.Orders() where o.CustomerID == c.CustomerID select o;

В данный момент мой переводчик запросов попытается выполнить SELECT * FROM orders o where o.CustomerID = c.CustomerID, что, конечно, не работает.

То, что я хотел бы сделать, это изучить выражение MemberExpression на c.CustomerID и попытаться выяснить, является ли оно локальной переменной или просто тем, что используется как часть выражения Linq.

Мне удалось сделать это как второй проход по запросу, отыскивая поля, которые SQL Server не сможет связать, и вводя вместо них их значения, но, если возможно, я бы хотел, чтобы все это происходило при в то же время. Я попытался посмотреть на выражения Type property и IsAutoClass, но это было только предположение, потому что оно содержало слово Auto. И это не сработало:)

Ответы [ 3 ]

1 голос
/ 19 января 2009

Хорошо, после некоторого быстрого статистического анализа (т. Е. Сравнения отдельных свойств вручную) запускаются параметры DeclaringType, ReflectedType и Namespace, когда параметр Lambda находится вне области действия.

Так что, если кто-то не придет с лучшим ответом, это может быть все, что мне нужно.

1 голос
/ 19 января 2009

В выражении Where вы смотрите на Expression<Func<T,bool>> - это означает, что самая внешняя лямбда должна иметь один ParameterExpression с типом T.

Если сравнение относится к строке, оно будет иметь (как предок) это ParameterExpression; если это локальная переменная, она будет иметь (как предок) ConstantExpression - однако тип этого константного выражения будет сгенерирован компилятором для обработки всех захваченных переменных, используемых в выражении.

Вот так:

using System;
using System.Linq.Expressions;
class Foo
{
    public string Name { get; set; }
    static void Main()
    {
        var exp = (LambdaExpression) GetExpression();
        WalkTree(0, exp.Body, exp.Parameters[0]);

    }
    static void WriteLine(int offset, string message)
    {
        Console.WriteLine(new string('>',offset) + message);
    }
    static void WalkTree(int offset, Expression current,
        ParameterExpression param)
    {
        WriteLine(offset, "Node: " + current.NodeType.ToString());
        switch (current.NodeType)
        {
            case ExpressionType.Constant:
                WriteLine(offset, "Constant (non-db)"
                    + current.Type.FullName);
                break;
            case ExpressionType.Parameter:
                if (!ReferenceEquals(param, current))
                {
                    throw new InvalidOperationException(
                        "Unexpected parameter: " + param.Name);
                }
                WriteLine(offset, "db row: " + param.Name);
                break;
            case ExpressionType.Equal:
                BinaryExpression be = (BinaryExpression)current;
                WriteLine(offset, "Left:");
                WalkTree(offset + 1, be.Left, param);
                WriteLine(offset, "Right:");
                WalkTree(offset + 1, be.Right, param);
                break;
            case ExpressionType.MemberAccess:
                MemberExpression me = (MemberExpression)current;
                WriteLine(offset, "Member: " + me.Member.Name);
                WalkTree(offset + 1, me.Expression, param);
                break;
            default:
                throw new NotSupportedException(
                    current.NodeType.ToString());
        }
    }

    static Expression<Func<Foo, bool>> GetExpression()
    {
        Foo foo = new Foo { Name = "abc" };

        return row => row.Name == foo.Name;
    }    
}
1 голос
/ 19 января 2009

Ну, я не знаю, можете ли вы сократить его до одного прохода, но вы можете получить информацию о члене, и если она совпадает с другой переменной, объявленной вами как часть запроса (в данном случае " o "), вы используете его для генерации запроса.

В противном случае вы предполагаете, что это константа, а затем вставляете это значение.

К сожалению, поскольку вы можете иметь операторы from (в дополнение к оператору let) в нескольких местах в запросе, не похоже, что вы можете сделать это за один проход, так как вам нужно знать все запросы переменные впереди.

...