LINQ 查询中的 C# 多个 OR 条件
C# multiple OR conditions in LINQ query
我正在尝试 link 多个值或在 LINQ 循环中。
情况
平台:.net 5
C# 9
我们正在为列表构建过滤逻辑。在当前情况下,它涉及要过滤的字符串值。
用户可以搜索一个或多个值。他可以决定是否对单个搜索词进行 AND/OR link 编辑,以及是否对某个值取反。
我看到了这个条目。但是由于我的值在循环中,所以我不能使用 ||
.
示例:
- 所有名字中带有“A”的动物
SELECT * FROM "Animal" WHERE "Name" = 'A';
- 所有名称中带有“A”或“B”的动物
SELECT * FROM "Animal" WHERE "Name" = 'A' OR "Name" = 'B';
- 名称中带有“A”或“B”或非“C”的所有动物(这将是无意义的搜索)
SELECT * FROM "Animal" WHERE "Name" = 'A' OR "Name" = 'B' OR "Name" != 'C' ;
- 名称中包含“A”和“B”的所有动物
SELECT * FROM "Animal" WHERE "Name" = 'A' AND "Name" = 'B';
- 所有名字中有“A”和“B”但没有“C”的动物
SELECT * FROM "Animal" WHERE "Name" = 'A' AND "Name" = 'B' AND "Name" != 'C';
问题
用LINQ AND link是没有问题的。但是这些值如何用 OR link 运算?
代码示例
using System.Collections.Generic;
using System.Linq;
namespace SampleProject
{
public class Program
{
public static void Main(string[] args)
{
// Or Condtion
Condition condition = Condition.Or;
var animalsQuery = Animals.AsQueryable();
// Loop over all search values to extend the query
foreach (FilterValue filterValue in FilterValues)
{
switch (filterValue.LikeType)
{
case LikeType.Left: // LIKE '%value'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => !animal.Name.EndsWith(filterValue.Value))
: animalsQuery.Where(animal => animal.Name.EndsWith(filterValue.Value));
break;
case LikeType.Right: // LIKE 'value%'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => !animal.Name.StartsWith(filterValue.Value))
: animalsQuery.Where(animal => animal.Name.StartsWith(filterValue.Value));
break;
case LikeType.LeftAndRight: // LIKE '%value%'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => !animal.Name.Contains(filterValue.Value))
: animalsQuery.Where(animal => animal.Name.Contains(filterValue.Value));
break;
case LikeType.Equals: // Like 'value'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => animal.Name != filterValue.Value)
: animalsQuery.Where(animal => animal.Name == filterValue.Value);
break;
}
}
var result = animalsQuery.ToList();
}
/// Values to filter
public static List<Animal> Animals = new()
{
new() {Name = "Lenny"},
new() {Name = "Gideon"},
new() {Name = "Shania"},
new() {Name = "Jada"},
new() {Name = "Kamil"},
new() {Name = "Fariha"},
};
/// Search Values
public static List<FilterValue> FilterValues = new()
{
new() {Value = "a", LikeType = LikeType.Left},
new() {Value = "n", LikeType = LikeType.Right},
new() {Value = "f", LikeType = LikeType.LeftAndRight},
new() {Value = "k", LikeType = LikeType.Equals},
};
}
public class Animal
{
public string Name { get; set; }
}
public class FilterValue
{
public string Value { get; set; }
public bool IsNegated { get; set; }
public LikeType LikeType { get; set; }
}
public enum LikeType
{
Left = 1,
Right = 2,
LeftAndRight = 3,
Equals = 4,
}
public enum Condition
{
And = 1,
Or = 2,
}
}
这进入了表达式树重写领域。好消息是:它并不复杂并且您可以同时执行取反步骤:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
namespace SampleProject
{
public class Program
{
static Expression<Func<T, bool>> Combine<T>(Condition condition, Expression<Func<T, bool>> left, Expression<Func<T, bool>> right, bool negateRight)
{
if (right is null) return left;
if (left is null)
{
return negateRight ?
Expression.Lambda<Func<T, bool>>(
Expression.Not(right.Body), right.Parameters)
: right;
}
var leftP = left.Parameters.Single();
var rightP = right.Parameters.Single();
var rightBody = right.Body;
if (!ReferenceEquals(leftP, rightP))
{
// swap all uses of rightP on rightBody to leftP
// i.e. normalize on the parameter
rightBody = new SwapVisitor(rightP, leftP).Visit(rightBody);
}
if (negateRight)
{
rightBody = Expression.Not(rightBody);
}
return Expression.Lambda<Func<T, bool>>(condition switch
{
Condition.And => Expression.AndAlso(left.Body, rightBody),
Condition.Or => Expression.OrElse(left.Body, rightBody),
_ => throw new ArgumentOutOfRangeException(nameof(condition)),
}, left.Parameters);
}
class SwapVisitor : ExpressionVisitor
{
private readonly Expression _from, _to;
public SwapVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node)
=> ReferenceEquals(node, _from) ? _to : base.Visit(node);
}
public static void Main(string[] args)
{
// Or Condtion
Condition condition = Condition.Or;
var animalsQuery = Animals.AsQueryable();
// Loop over all search values to extend the query
Expression<Func<Animal, bool>> predicate = null;
foreach (FilterValue filterValue in FilterValues)
{
switch (filterValue.LikeType)
{
case LikeType.Left: // LIKE '%value'
predicate = Combine(condition, predicate, animal => animal.Name.EndsWith(filterValue.Value), filterValue.IsNegated);
break;
case LikeType.Right: // LIKE 'value%'
predicate = Combine(condition, predicate, animal => animal.Name.StartsWith(filterValue.Value), filterValue.IsNegated);
break;
case LikeType.LeftAndRight: // LIKE '%value%'
predicate = Combine(condition, predicate, animal => animal.Name.Contains(filterValue.Value), filterValue.IsNegated);
break;
case LikeType.Equals: // Like 'value'
predicate = Combine(condition, predicate, animal => animal.Name == filterValue.Value, filterValue.IsNegated);
break;
}
}
if (predicate is not null)
{
animalsQuery = animalsQuery.Where(predicate);
}
var result = animalsQuery.ToList();
}
/// Values to filter
public static List<Animal> Animals = new()
{
new() { Name = "Lenny" },
new() { Name = "Gideon" },
new() { Name = "Shania" },
new() { Name = "Jada" },
new() { Name = "Kamil" },
new() { Name = "Fariha" },
};
/// Search Values
public static List<FilterValue> FilterValues = new()
{
new() { Value = "a", LikeType = LikeType.Left },
new() { Value = "n", LikeType = LikeType.Right },
new() { Value = "f", LikeType = LikeType.LeftAndRight },
new() { Value = "k", LikeType = LikeType.Equals },
};
}
public class Animal
{
public string Name { get; set; }
}
public class FilterValue
{
public string Value { get; set; }
public bool IsNegated { get; set; }
public LikeType LikeType { get; set; }
}
public enum LikeType
{
Left = 1,
Right = 2,
LeftAndRight = 3,
Equals = 4,
}
public enum Condition
{
And = 1,
Or = 2,
}
}
我正在尝试 link 多个值或在 LINQ 循环中。
情况
平台:.net 5
C# 9
我们正在为列表构建过滤逻辑。在当前情况下,它涉及要过滤的字符串值。
用户可以搜索一个或多个值。他可以决定是否对单个搜索词进行 AND/OR link 编辑,以及是否对某个值取反。
我看到了这个条目。但是由于我的值在循环中,所以我不能使用 ||
.
示例:
- 所有名字中带有“A”的动物
SELECT * FROM "Animal" WHERE "Name" = 'A';
- 所有名称中带有“A”或“B”的动物
SELECT * FROM "Animal" WHERE "Name" = 'A' OR "Name" = 'B';
- 名称中带有“A”或“B”或非“C”的所有动物(这将是无意义的搜索)
SELECT * FROM "Animal" WHERE "Name" = 'A' OR "Name" = 'B' OR "Name" != 'C' ;
- 名称中包含“A”和“B”的所有动物
SELECT * FROM "Animal" WHERE "Name" = 'A' AND "Name" = 'B';
- 所有名字中有“A”和“B”但没有“C”的动物
SELECT * FROM "Animal" WHERE "Name" = 'A' AND "Name" = 'B' AND "Name" != 'C';
问题
用LINQ AND link是没有问题的。但是这些值如何用 OR link 运算?
代码示例
using System.Collections.Generic;
using System.Linq;
namespace SampleProject
{
public class Program
{
public static void Main(string[] args)
{
// Or Condtion
Condition condition = Condition.Or;
var animalsQuery = Animals.AsQueryable();
// Loop over all search values to extend the query
foreach (FilterValue filterValue in FilterValues)
{
switch (filterValue.LikeType)
{
case LikeType.Left: // LIKE '%value'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => !animal.Name.EndsWith(filterValue.Value))
: animalsQuery.Where(animal => animal.Name.EndsWith(filterValue.Value));
break;
case LikeType.Right: // LIKE 'value%'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => !animal.Name.StartsWith(filterValue.Value))
: animalsQuery.Where(animal => animal.Name.StartsWith(filterValue.Value));
break;
case LikeType.LeftAndRight: // LIKE '%value%'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => !animal.Name.Contains(filterValue.Value))
: animalsQuery.Where(animal => animal.Name.Contains(filterValue.Value));
break;
case LikeType.Equals: // Like 'value'
animalsQuery = filterValue.IsNegated
? animalsQuery.Where(animal => animal.Name != filterValue.Value)
: animalsQuery.Where(animal => animal.Name == filterValue.Value);
break;
}
}
var result = animalsQuery.ToList();
}
/// Values to filter
public static List<Animal> Animals = new()
{
new() {Name = "Lenny"},
new() {Name = "Gideon"},
new() {Name = "Shania"},
new() {Name = "Jada"},
new() {Name = "Kamil"},
new() {Name = "Fariha"},
};
/// Search Values
public static List<FilterValue> FilterValues = new()
{
new() {Value = "a", LikeType = LikeType.Left},
new() {Value = "n", LikeType = LikeType.Right},
new() {Value = "f", LikeType = LikeType.LeftAndRight},
new() {Value = "k", LikeType = LikeType.Equals},
};
}
public class Animal
{
public string Name { get; set; }
}
public class FilterValue
{
public string Value { get; set; }
public bool IsNegated { get; set; }
public LikeType LikeType { get; set; }
}
public enum LikeType
{
Left = 1,
Right = 2,
LeftAndRight = 3,
Equals = 4,
}
public enum Condition
{
And = 1,
Or = 2,
}
}
这进入了表达式树重写领域。好消息是:它并不复杂并且您可以同时执行取反步骤:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
namespace SampleProject
{
public class Program
{
static Expression<Func<T, bool>> Combine<T>(Condition condition, Expression<Func<T, bool>> left, Expression<Func<T, bool>> right, bool negateRight)
{
if (right is null) return left;
if (left is null)
{
return negateRight ?
Expression.Lambda<Func<T, bool>>(
Expression.Not(right.Body), right.Parameters)
: right;
}
var leftP = left.Parameters.Single();
var rightP = right.Parameters.Single();
var rightBody = right.Body;
if (!ReferenceEquals(leftP, rightP))
{
// swap all uses of rightP on rightBody to leftP
// i.e. normalize on the parameter
rightBody = new SwapVisitor(rightP, leftP).Visit(rightBody);
}
if (negateRight)
{
rightBody = Expression.Not(rightBody);
}
return Expression.Lambda<Func<T, bool>>(condition switch
{
Condition.And => Expression.AndAlso(left.Body, rightBody),
Condition.Or => Expression.OrElse(left.Body, rightBody),
_ => throw new ArgumentOutOfRangeException(nameof(condition)),
}, left.Parameters);
}
class SwapVisitor : ExpressionVisitor
{
private readonly Expression _from, _to;
public SwapVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node)
=> ReferenceEquals(node, _from) ? _to : base.Visit(node);
}
public static void Main(string[] args)
{
// Or Condtion
Condition condition = Condition.Or;
var animalsQuery = Animals.AsQueryable();
// Loop over all search values to extend the query
Expression<Func<Animal, bool>> predicate = null;
foreach (FilterValue filterValue in FilterValues)
{
switch (filterValue.LikeType)
{
case LikeType.Left: // LIKE '%value'
predicate = Combine(condition, predicate, animal => animal.Name.EndsWith(filterValue.Value), filterValue.IsNegated);
break;
case LikeType.Right: // LIKE 'value%'
predicate = Combine(condition, predicate, animal => animal.Name.StartsWith(filterValue.Value), filterValue.IsNegated);
break;
case LikeType.LeftAndRight: // LIKE '%value%'
predicate = Combine(condition, predicate, animal => animal.Name.Contains(filterValue.Value), filterValue.IsNegated);
break;
case LikeType.Equals: // Like 'value'
predicate = Combine(condition, predicate, animal => animal.Name == filterValue.Value, filterValue.IsNegated);
break;
}
}
if (predicate is not null)
{
animalsQuery = animalsQuery.Where(predicate);
}
var result = animalsQuery.ToList();
}
/// Values to filter
public static List<Animal> Animals = new()
{
new() { Name = "Lenny" },
new() { Name = "Gideon" },
new() { Name = "Shania" },
new() { Name = "Jada" },
new() { Name = "Kamil" },
new() { Name = "Fariha" },
};
/// Search Values
public static List<FilterValue> FilterValues = new()
{
new() { Value = "a", LikeType = LikeType.Left },
new() { Value = "n", LikeType = LikeType.Right },
new() { Value = "f", LikeType = LikeType.LeftAndRight },
new() { Value = "k", LikeType = LikeType.Equals },
};
}
public class Animal
{
public string Name { get; set; }
}
public class FilterValue
{
public string Value { get; set; }
public bool IsNegated { get; set; }
public LikeType LikeType { get; set; }
}
public enum LikeType
{
Left = 1,
Right = 2,
LeftAndRight = 3,
Equals = 4,
}
public enum Condition
{
And = 1,
Or = 2,
}
}