атм я пишу модуль общего фильтра в моем приложении.У меня проблема с созданием правильного выражения>.Общий запрос SQL выглядит так:
SELECT distinct ROW_NUMBER
FROM dbo.VIEW_ITEM item
WHERE CODE ='MyName'
AND EXISTS (SELECT *
FROM dbo.VIEW_ITEM
WHERE ROW_NUMBER = item.ROW_NUMBER
AND CODE='MyName'
AND (COL_NUMBER=1 AND DISPLAY='UserName'))
AND EXISTS (SELECT *
FROM VIEW_ITEM
WHERE ROW_NUMBER = item.ROW_NUMBER
AND CODE='MyName'
AND (COL_NUMBER=3 and DISPLAY='2261'))
ORDER BY ROW_NUMBER
Это (на мой взгляд) лучший способ получить все нужные мне записи.Я не могу использовать опцию соединения, потому что я проверяю в моей И СУЩЕСТВУЕТ ту же таблицу, что и в основном запросе.
Так что мой linq выглядит так:
dboMVI.Where(mvi => mvi.Code == "MyCode")
.Where(mvi => dboMVI.Where(innerMvi => innerMvi.RowNumber == mvi.RowNumber && innerMvi.Code == "MyCode" && innerMvi.ColNumber == 1 && innerMvi.Display == "UserName").Any())
.Where(mvi => dboMVI.Where(innerMvi => innerMvi.RowNumber == mvi.RowNumber && innerMvi.Code == "MyCode" && innerMvi.ColNumber == 3 && innerMvi.Display == "2261").Any())
.Select(mvi => mvi.RowNumber)
.OrderBy(rn => rn)
.Distinct();
Это должно вернуть мне все числа строк, которые прошли мою фильтрацию.Мне удалось создать Expression, но я уверен, что есть лучший способ сделать его более универсальным и поместить его в мой модуль фильтрации, который не используется, откуда я передаю его DbContext.
Expression<Func<ViewItem, bool>> filter =mvi =>
dboMVI.Where(innerMvi => innerMvi.RowNumber == mvi.RowNumber
&& innerMvi.Code == "MyCode" && innerMvi.ColNumber == 3 &&
innerMvi.Display == "2261").Any()
И для второго фильтра:
Expression<Func<ViewItem, bool>> filter =mvi =>
dboMVI.Where(innerMvi => innerMvi.RowNumber == mvi.RowNumber
&& innerMvi.Code == "MyCode" && innerMvi.ColNumber == 1 &&
innerMvi.Display == "UserName").Any()
Мой вопрос заключается в том, как создать общее дерево выражений для этого запроса LINQ?
Я не смог найти нигде пример создания такого дерева.Я нашел пример передачи аргументов в операторе соединения.Но в моем случае я перехожу с основного номера запроса текущей строки и в подзапросе проверяется, удовлетворяются ли какие-либо условия для этой конкретной строки .
РЕДАКТИРОВАТЬ: после некоторых комментариевя заметил, что сделал ошибку в переписывании запросов с реальных значений на демо.Надеюсь, что теперь это исправлено :) В общем, его рабочее решение просто ищет лучший путь.
EDIT2: В чем моя проблема:
im пытается сгенерировать из LINQ SQLзапрос, который может быть использован EF Core.в моем SQL с использованием AND EXISTS, где в теле я ссылаюсь на ту же таблицу, что и в основном запросе.Что еще в подзапросе я использую ROW_NUMBER из основного запроса.В чем моя проблема?Я не знаю, как создать выражение func (отвечающее за мой подзапрос), потому что я не знаю, как передать в него мой текущий ROW_NUMBER, который я проверяю.Я знаю, как сделать дерево выражений для простых примеров.Но там у меня есть константа в моем теле, например, int или string.Но в этом случае мой const каждый раз меняется на другое значение, поэтому его нельзя жестко запрограммировать.
Ответ
Мне удалось решить эту проблему.Прежде всего пришлось упростить запрос linq.
dboMVI.Where(mvi => mvi.Code == "MyCode")
.Where(mvi => dboMVI.Any(innerMvi => innerMvi.RowNumber == mvi.RowNumber && innerMvi.Code == "MyCode" && innerMvi.ColNumber == 1 && innerMvi.Display == "UserName"))
.Where(mvi => dboMVI.Any(innerMvi => innerMvi.RowNumber == mvi.RowNumber && innerMvi.Code == "MyCode" && innerMvi.ColNumber == 3 && innerMvi.Display == "2261"))
.Select(mvi => mvi.RowNumber)
.OrderBy(rn => rn)
.Distinct()
Чем нужно было создать дерево выражений для элементов внутри любого выражения:
IQueryable<MaterializedViewItem> MyDtoList = Enumerable.Empty<MaterializedViewItem>().AsQueryable();
var insideProperty = Expression.Parameter(typeof(MaterializedViewItem), "mviAny");
var baseProperty = Expression.Parameter(typeof(MaterializedViewItem), "mviBaseAny");
MemberExpression condition0Code = Expression.Property(baseProperty, "MvCode");
ConstantExpression condition0CodeValue = Expression.Constant("ARAPP");
var condition0 = Expression.Equal(condition0Code, condition0CodeValue);
var predicateFirstElement = Expression.Lambda<Func<T, bool>>(condition0, baseProperty);
MemberExpression conditionACode = Expression.Property(insideProperty, "MvCode");
ConstantExpression conditionACodeValue = Expression.Constant("MyCode");
var conditionA = Expression.Equal(conditionACode, conditionACodeValue);
MemberExpression conditionACol = Expression.Property(insideProperty, "ColNumber");
ConstantExpression conditionAColValue = Expression.Constant((byte)1);
var conditionB = Expression.Equal(conditionACol, conditionAColValue);
MemberExpression conditionDisplay = Expression.Property(insideProperty, "ValueDisplay");
ConstantExpression conditionDisplayValue = Expression.Constant("UserName");
var conditionC = Expression.Equal(conditionDisplay, conditionDisplayValue);
MemberExpression conditionRow = Expression.Property(insideProperty, "RowNumber");
var newValueToCompare = Expression.PropertyOrField(baseProperty, "RowNumber");
ConstantExpression conditionRowValue = Expression.Constant(0);
var conditionD = Expression.Equal(conditionRow, newValueToCompare);
var condition = Expression.AndAlso(conditionA, conditionB);
var condition2 = Expression.AndAlso(conditionC, conditionD);
var condition3 = Expression.AndAlso(condition, condition2);
var predicate = Expression.Lambda<Func<MaterializedViewItem, bool>>(condition3, insideProperty);
var callCondtions = BuildAny<MaterializedViewItem>(predicate, MyDtoList.Expression);
var myPredicate = Expression.Lambda<Func<T, bool>>(callCondtions, baseProperty);
MemberExpression conditionCol2 = Expression.Property(insideProperty, "ColNumber");
ConstantExpression conditionCol2Value = Expression.Constant((byte)3);
var conditionE = Expression.Equal(conditionCol2, conditionCol2Value);
MemberExpression conditionColDisplay2 = Expression.Property(insideProperty, "ValueDisplay");
ConstantExpression conditionColDisplay2Value = Expression.Constant("2261");
var conditionF = Expression.Equal(conditionColDisplay2, conditionColDisplay2Value);
var condition22 = Expression.AndAlso(conditionA, conditionD);
var condition23 = Expression.AndAlso(conditionE, conditionF);
var condition2Final = Expression.AndAlso(condition22, condition23);
var predicate2 = Expression.Lambda<Func<T, bool>>(condition2Final, insideProperty);
var callCondtions2 = BuildAny<T>(predicate2, MyDtoList.Expression);
Нужна дополнительная функция, чтобы построить для меня final Any со всемипараметры
public static Expression BuildAny<T>(Expression<Func<T, bool>> predicate, Expression expression)
{
var overload = typeof(Queryable).GetMethods().FirstOrDefault(method => method.Name == "Any" && method.GetParameters().Count() == 2);
var specificMethod = overload.MakeGenericMethod(typeof(T));
var call = Expression.Call(
specificMethod,
expression,
predicate);
return call;
}
Что важно помнить, мы строим IQueryable на основе временного объекта.Позже он должен быть заменен на реальную таблицу БД.Это можно сделать с помощью:
IQueryable<T> queryList = this.DbSet;
var filtersForDbSet = ExpressionTreeConstantReplacer.CopyAndReplace<DbSet<T>, T>(condition, typeof(EnumerableQuery<T>), this.DbSet);
class ExpressionTreeConstantReplacer
{
internal static Expression<Func<T2, bool>> CopyAndReplace<T, T2>(Expression<Func<T2, bool>> expression, Type originalType, T replacementConstant)
{
var modifier = new ExpressionTreeConstantReplacer<T>(originalType, replacementConstant);
var newLambda = modifier.Visit(expression) as LambdaExpression;
return Expression.Lambda<Func<T2, bool>>(newLambda.Body, newLambda.Parameters.FirstOrDefault());
}
}
и
class ExpressionTreeConstantReplacer<T> : ExpressionVisitor
{
Type originalType;
T replacementConstant;
internal ExpressionTreeConstantReplacer(Type originalType, T replacementConstant)
{
this.originalType = originalType;
this.replacementConstant = replacementConstant;
}
protected override Expression VisitConstant(ConstantExpression c)
{
return c.Type == originalType ? Expression.Constant(replacementConstant) : c;
}
}
Если у кого-то возникнет аналогичная проблема в дереве выражений.Запрос строится так же, как и в обычном запросе.Чтобы передать некоторую переменную из основного выражения во внутреннее выражение, вам нужно просто показать, что вы сравниваете их как:
MemberExpression conditionRow = Expression.Property(insideProperty, "RowNumber");
var newValueToCompare = Expression.PropertyOrField(baseProperty, "RowNumber");
var conditionD = Expression.Equal(conditionRow, newValueToCompare