1

В настоящее время я работаю с решением .net core (vnext), которое использует ядро ​​linq и entity framework.Linq Expression Tree - Concatenate Массив значений

Мы используем Microsoft.Linq.Translations для хранения вычисляемых определений столбцов в виде лямбда-выражения, чтобы затем их можно было вводить в наши инструкции, когда это необходимо. Пример вычисленного определения заключается в следующем:

private static readonly CompiledExpression<Model, string> fullNameExpression = 
     aTranslation<Model>.Expression(e => e.FullName, e => $"{e.FirstName} {e.LastName} ({e.Code})"); 

и потреблять/выполнить его:

var list = context.Set<Model>().Where(e => e.FullName.Contains("Bob")).WithTranslations(); 

Это все «Технически,» отлично работает .... как в правильном результате получает связанное назад для моделей сущностей, однако работа выполняется в памяти вместо того, чтобы строить запрос в SQL. Это проблема, так как в наших моделях данных могут появиться миллионы строк данных, и сделать это в памяти будет непросто.

Это то, что мы в конечном итоге на данный момент:

.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) { 
.Call (.Call System.String.Format(
    "{0} {1} ({2})", 
    $e.FirstName, 
    $e.LastName, 
    $e.Code)).Contains("Bob") 

}

Как я уже сказал, это работает, но результаты в задаче выполняется в памяти, а не SQL. Запрос получает генерируется что-то вроде:

SELECT FirstName, LastName, Code FROM Model 

Так что, если я изменил вычисляемое определение следующим образом (обратите внимание, я просто строю строку вручную вместо использования string.format:

private static readonly CompiledExpression<Model, string> fullNameExpression = 
     aTranslation<Model>.Expression(e => e.FullName, e => e.FirstName + " " + e.LastName + "(" + e.Code + ")"); 

Это производит лямбда-выражение, как следующее:

.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) { 
.Call ($e.FirstName + " " + $e.LastName + " (" + $e.Code + ")").Contains("Bob")} 

Это будет работать и правильно построить запрос SQL из

.
SELECT * FROM Model WHERE (FirstName + ' ' + LastName + '(' + Code + ')') LIKE '%bob%' 

Таким образом, чтобы изменить способ вычисления или определения вычисленных определений, я пытаюсь написать слой, который берет определение, написанное с использованием выражения linq в строковом формате, и заменяет это выражение на второй тип (например, строку конкатенация)

Я начал писать этот слой перехватывает выражение форматирования строки и построение списка/массива элемента и постоянных выражений, т.е. ПгвЬЫата, «», LastName, «(» и т.д ....

Я попробовал u петь струну Concat подхода обернув через значение и оценку левое и правое выражение, но это заканчивается тем, что что-то другое, что не работает:

.Call System.String.Concat(
.Call System.String.Concat(
    .Call System.String.Concat(
     .Call System.String.Concat(
      $e.FirstName, 
      " "), 
     $e.LastName), 
    " ("), 
$e.Code) 

Теперь я застрял о том, как преобразовать этот список выражений для получения такого рода выражений:

.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) { 
.Call ($e.FirstName + " " + $e.LastName + " (" + $e.Code + ")").Contains("Bob")} 

Любая помощь была бы принята с благодарностью.

----------------- Edit -------------

Кодекс пытался до сих пор.Для краткости я оставил строение переменной «concatArgs». Это просто список, содержащий типы MemberExpression и ConstantExpression.

Expression expr = GenerateStringConcat(concatArgs[0], concatArgs[1]); 
for(int i = 2; i < concatArgs.Count; i++) 
    expr = GenerateStringConcat(expr, concatArgs[i]); 

и вызываемый метод является показать ниже

private Expression GenerateStringConcat(Expression left, Expression right) { 
     return Expression.Call(
      null, 
      typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), 
      new[] { left, right }); 
} 
+1

Написать выражение вручную в запросе, который работает. Затем декомпилируйте сборку (или используйте Roslyn онлайн), чтобы увидеть, какое дерево выражений генерируется компилятором C#. Это шаблон, который вы можете использовать. – usr

+0

Отправьте код, который вы попробовали, чтобы мы могли его отрегулировать. –

ответ

0

Ok, так что я понял это!

Комментарий от @usr побудил меня углубиться в дерево синтаксиса еще немного и посмотреть, как скомпилирован код. Я использовал визуализатор синтаксиса, встроенный в VS2015, в «Вид -> Другие окна -> Синтаксис визуализатора»

При разрыве стандартного выражения linq желаемого результата это показало, что за кулисами это составляло до BinaryOperator, который казался странно ...

Изначально я пытался заменить мой String.Concat звонки с:

BinaryExpression.Add(concatArgs[0], concatArgs[1]) 

Однако это привело к следующей ошибке:

The binary operator Add is not defined for the types 'System.String' and 'System.String'

Который ведет меня к этой статье/должность:

"The binary operator Add is not defined for the types 'System.String' and 'System.String'." -- Really?

с этим предложил перегрузки:

https://msdn.microsoft.com/en-us/library/bb339231%28v=vs.110%29.aspx

В основном, за кулисами все это заканчивается тем, что были преобразованы в String.Concat вызов в любом случае, однако использование метода BinaryExpression.Add позволяет linq работать с этим во время выполнения и использовать только один вызов для string.concat? возможно, "params object []"?

Во всяком случае, изменяя мой метод GenerateStringConcat к следующему сделал трюк:

private Expression GenerateStringConcat(Expression left, Expression right) 
{ 
    return BinaryExpression.Add(left, right, typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) })); 
} 
Смежные вопросы