Хотите конвертировать c# несколько Linq в один запрос Linq? - PullRequest
1 голос
/ 22 января 2020

Я написал код с несколькими условиями if и получил список идентификаторов из каждого оператора if. Я хочу написать весь этот код в один запрос linq, если это возможно, а затем дайте мне знать.

Проблема в том, что когда мы запускаем этот код, мы видим, что наш компилятор запускает строку запроса сотен в окне вывода. Я просто хочу оптимизировать. Если вы, ребята, можете мне помочь. Следующий код для деталей:

if (queryObj.AgeCategory.Contains((int)AgeList._0to5))
{
    var list = queryRecord.Where(v => v.BuildYear <= yearNow && v.BuildYear > year5).Select(s => s.Id);
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._6to10))
{
    var list = queryRecord.Where(v => v.BuildYear <= year5 && v.BuildYear > year10).Select(s => s.Id);
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._11to15))
{
    var list = queryRecord.Where(v => v.BuildYear <= year10 && v.BuildYear > year15).Select(s => s.Id);
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._16to20))
{
    var list = Ids.Concat(queryRecord.Where(v => v.BuildYear <= year15 && v.BuildYear > year20).Select(s => s.Id));
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._21to25))
{
    var list = Ids.Concat(queryRecord.Where(v => v.BuildYear <= year20 && v.BuildYear > year25).Select(s => s.Id));
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._26to30))
{
    var list = Ids.Concat(queryRecord.Where(v => v.BuildYear <= year25 && v.BuildYear > year30).Select(s => s.Id));
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._31to35))
{
    var list = Ids.Concat(queryRecord.Where(v => v.BuildYear <= year30 && v.BuildYear > year35).Select(s => s.Id));
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._36to40))
{
    var list = Ids.Concat(queryRecord.Where(v => v.BuildYear <= year35 && v.BuildYear > year40).Select(s => s.Id));
    Ids = Ids.Concat(list);
}
if (queryObj.AgeCategory.Contains((int)AgeList._Over41Years))
{
    var list = Ids.Concat(queryRecord.Where(v => v.BuildYear <= year40).Select(s => s.Id));
    Ids = Ids.Concat(list);
}
queryRecord = queryRecord.Where(v => Ids.Contains(v.Id));

Я протестировал следующий код, но из-за того же столбца Это соответствует первому условию.

                    Ids = queryRecord.Where(st => ((queryObj.AgeCategory.Contains((int)AgeList._11to15)) ? st.BuildYear <= year10 && st.BuildYear > year15 : true) &&
                        ((queryObj.AgeCategory.Contains((int)AgeList._6to10)) ? st.BuildYear <= year5 && st.BuildYear > year10 : true))
                        .Select(s => s.Id);

Ответы [ 4 ]

1 голос
/ 22 января 2020

Давайте начнем с метода AndAlso Мар c из этого связанного ответа и заменим AndAlso на OrElse:

public static Expression<Func<T, bool>> OrElse<T>(
    this Expression<Func<T, bool>> expr1,
    Expression<Func<T, bool>> expr2)
{
    var parameter = Expression.Parameter(typeof(T));

    var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
    var left = leftVisitor.Visit(expr1.Body);

    var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
    var right = rightVisitor.Visit(expr2.Body);

    return Expression.Lambda<Func<T, bool>>(
        Expression.OrElse(left, right), parameter);
}

private class ReplaceExpressionVisitor
    : ExpressionVisitor
{
    private readonly Expression _oldValue;
    private readonly Expression _newValue;

    public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
    {
        _oldValue = oldValue;
        _newValue = newValue;
    }

    public override Expression Visit(Expression node)
    {
        if (node == _oldValue)
            return _newValue;
        return base.Visit(node);
    }
}

Это позволяет нам писать :

Expression<Func<Whatever, bool>> query = t => false;

if (queryObj.AgeCategory.Contains((int)AgeList._0to5))
{
    query = query.OrElse(v => v.BuildYear <= yearNow && v.BuildYear > year5);
}
if (queryObj.AgeCategory.Contains((int)AgeList._6to10))
{
    query = query.OrElse(v => v.BuildYear <= year5 && v.BuildYear > year10);
}
... and so on...

queryRecord = whatever.Where(query);

Это генерирует один запрос, который будет выглядеть так:

WHERE (BuildYear <= yearNow AND BuildYear > year5) OR (BuildYear < year5 AND BuildYear > year10) etc
0 голосов
/ 07 февраля 2020

Я просто добавил false в каждом случае и добавил || между каждым утверждением и исправил это.

Ids = queryRecord.Where(st => 
                    ((queryObj.AgeCategory.Contains((int)AgeList._11to15)) ? st.BuildYear <= year10 && st.BuildYear > year15 : false) ||
                    ((queryObj.AgeCategory.Contains((int)AgeList._6to10)) ? st.BuildYear <= year5 && st.BuildYear > year10 : false)
                    ).Select(s => s.Id);
0 голосов
/ 22 января 2020

Другое решение - создать конвертер, который преобразует BuildAge в возрастную категорию. Таким образом, вы можете просто позволить LINQ выполнять фильтрацию:

private void FilterQueryRecord(IEnumerable<Record> queryRecord, ? queryObj)
{
  this.Ids = queryRecord
    .Where(item => queryObj.AgeCategory.Contains(ConvertBuildYearToAgeCategory(item.BuildYear)))
    .Select(item => item.Id)
    .ToList();
}

private int ConvertBuildYearToAgeCategory(int buildYear) 
{
  switch (buildYear)
  {
    case int value when value <= yearNow && value > year5: return (int) AgeList._0to5;
    case int value when value <= year5 && value > year10: return (int) AgeList._6to10;
    case int value when value <= year10 && value > year15: return (int) AgeList._11to15;
    case int value when value <= year15 && value > year20: return (int) AgeList._16to20;
    case int value when value <= year20 && value > year25: return (int) AgeList._21to25;
    case int value when value <= year25 && value > year30: return (int) AgeList._26to30;
    case int value when value <= year30 && value > year35: return (int) AgeList._31to35;
    case int value when value <= year35 && value > year40: return (int) AgeList._36to40;
    case int value when value < year40: return (int) AgeList._Over41Years;
    default: return (int) AgeList.Undefined
  }
}
0 голосов
/ 22 января 2020

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

public enum AgeList
{
    _0to5   = 0,
    _6to10  = 1,
    _11to15 = 2,
    _16to20 = 3,
    _21to25 = 4,
    _26to30 = 5,
    _31to35 = 6,
    _36to40 = 7,
}

Я думаю, что вы хотите сопоставить BuildYear с одним из перечисленных выше, и мы можем наблюдать, как они go в пятерках. Итак, мы могли бы сделать:

var yearNow = 2020;
var buildYear = 2019;

var ageGroupNumber = (yearNow - buildYear) / 5; // 0
var ageGroup = (AgeList) ageGroupNumber;        // _0to5 

Так могли бы мы тогда сделать что-то вроде следующего?

Ids = queryObj.AgeCategory
    .SelectMany(category => // For every category :
        queryRecord.Where(v => // Select the items in this category :
            (DateTime.Now.Year - v.BuildYear) / 5 == (int) category 
        )
        .Select(s => s.Id)
    );

Я не знаю, поможет ли это вам, или я правильно понял вашу проблему, но, возможно, это даст вам некоторые идеи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...