想将 c# 多个 Linq 转换为一个 Linq 查询?
Want to convert c# multiple Linq into one Linq query?
我编写了带有多个 if 条件的代码,并从每个 if 语句中获取 ID 列表。如果可能的话,我想将整个代码写入一个 linq 查询,然后让我知道。
问题是当我们 运行 这段代码可以看到我们的编译器在输出 window 上运行数百行查询。我只想优化。如果你们能帮助我。
详情如下代码:
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);
让我们从 this linked answer 中 Marc 的 AndAlso
方法开始,并将 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
假设您所指的 AgeList
是一个像这样的枚举:
public enum AgeList
{
_0to5 = 0,
_6to10 = 1,
_11to15 = 2,
_16to20 = 3,
_21to25 = 4,
_26to30 = 5,
_31to35 = 6,
_36to40 = 7,
}
我想你想将 BuildYear
映射到上面的其中一个,我们可以观察到它们成五个。
所以我们可以这样做:
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)
);
我不知道这是否对你有帮助,或者我是否正确理解了你的问题,但也许这会
给你一些想法。
另一种解决方案是创建一个转换器,将 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
}
}
我只是在每种情况下都添加了 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);
我编写了带有多个 if 条件的代码,并从每个 if 语句中获取 ID 列表。如果可能的话,我想将整个代码写入一个 linq 查询,然后让我知道。
问题是当我们 运行 这段代码可以看到我们的编译器在输出 window 上运行数百行查询。我只想优化。如果你们能帮助我。 详情如下代码:
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);
让我们从 this linked answer 中 Marc 的 AndAlso
方法开始,并将 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
假设您所指的 AgeList
是一个像这样的枚举:
public enum AgeList
{
_0to5 = 0,
_6to10 = 1,
_11to15 = 2,
_16to20 = 3,
_21to25 = 4,
_26to30 = 5,
_31to35 = 6,
_36to40 = 7,
}
我想你想将 BuildYear
映射到上面的其中一个,我们可以观察到它们成五个。
所以我们可以这样做:
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)
);
我不知道这是否对你有帮助,或者我是否正确理解了你的问题,但也许这会 给你一些想法。
另一种解决方案是创建一个转换器,将 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
}
}
我只是在每种情况下都添加了 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);