Определение объема цели MemberExpressions
Есть здесь кто-нибудь с опытом написания пользовательских провайдеров 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
что, конечно, не работает.
То, что я хотел бы сделать, это изучить выражение члена на c.CustomerID
и попытайтесь выяснить, является ли она локальной переменной или просто чем-то, что используется как часть выражения Linq.
Мне удалось сделать это как второй проход по запросу, отыскивая поля, которые SQL Server не сможет связать, и вместо этого вводя их значения, но, если возможно, я бы хотел, чтобы все это происходило одновременно, Я попытался посмотреть на выражение Type
собственность и IsAutoClass
, но это было только предположение, потому что оно содержало слово Auto. И это не сработало:)
3 ответа
Ну, я не знаю, можете ли вы сократить его до одного прохода, но вы можете получить информацию о члене, и если она совпадает с другой переменной, которую вы объявили как часть запроса (в данном случае "o"), вы используете его для генерации вашего запроса.
В противном случае вы предполагаете, что это константа, а затем подключаете это значение.
К сожалению, поскольку вы можете использовать операторы from (в дополнение к оператору let) в нескольких местах запроса, не похоже, что вы можете сделать это за один проход, так как вам нужно знать все переменные запроса заранее,
Хорошо, после некоторого быстрого статистического анализа (т. Е. Сравнения отдельных свойств вручную), запускаются параметры DeclaringType, ReflectedType и Namespace, когда параметр Lambda находится вне области действия.
Так что, если кто-то не придумает лучшего ответа, это все, что мне нужно сделать.
В выражении Где вы смотрите на 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;
}
}