[英]Applying LINQ filters based on a multi-dimensional array
Given an entity framework query, such as 给定实体框架查询,例如
var query = (from property in _dbContext.Properties
join location in _db.Locations
on property.Id equals location.PropertyId
select new PropertyDetail
{
Url = property.Url,
Type = property.Type,
Title = property.Title,
Continent = location.Continent,
Country = location.Country,
State = location.State,
});
I have applied filters such as: 我已应用过滤器,例如:
if (!string.IsNullOrWhitespace(searchFilters.Type))
{
query = query.Where(model => model.Type == searchFilters.Type);
}
if (!string.IsNullOrWhitespace(searchFilters.Title))
{
query = query.Where(model => model.Title.Contains(searchFilters.Title));
}
Given the following multi-dimensional array 给出以下多维数组
var locations = new[]
{
new[] {"Africa", "Algeria", ""},
new[] {"Asia", "Hong Kong", ""},
new[] {"Asia", "Singapore", ""},
new[] {"Oceania", "Australia", "New South Wales"},
new[] {"North America", "United States", "California"}
};
How can the "query" be further restricted to only include those entries that match the specified locations {Continent, Country, State(optional)} ? 如何将“查询”进一步限制为仅包括与指定位置{大陆,国家,州(可选)}匹配的条目?
This calls for what is called a correlated subquery in SQL. 这需要SQL中所谓的相关子查询。 Assuming they will always occupy the same position, you can use array indexers to access the elements within your
locations
jagged array. 假设它们总是占据相同的位置,您可以使用数组索引器来访问
locations
锯齿数组中的元素。
query = query.Where(model =>
locations.Any(location =>
location[0] == model.Continent &&
location[1] == model.Country &&
(string.IsNullOrEmpty(location[2]) || location[2] == model.State)));
Update : Since LINQ to Entities does not support array indexers, you could convert your jagged array into a collection of anonymous types. 更新 :由于LINQ to Entities不支持数组索引器,因此您可以将锯齿状数组转换为匿名类型的集合。 (In the long term, it would be preferable to create a class for instantiating your filters. This would be more intuitive than remembering what the elements at each index represent.)
(从长远来看,最好创建一个用于实例化过滤器的类。这比记住每个索引所代表的元素更直观。)
var locationsTyped =
locations.Select(location => new
{
Continent = location[0],
Country = location[1],
State = location[2],
}).ToArray();
query = query.Where(model =>
locationsTyped.Any(location =>
location.Continent == model.Continent &&
location.Country == model.Country &&
(string.IsNullOrEmpty(location.State) || location.State == model.State)));
Unfortunately LINQ to Entities currently does not support joins to inmemory collection, nor Contains
for non primitive inmemory collection. 不幸的是,LINQ to Entities目前不支持对内存集合的连接,也不支持非原始内存集合的
Contains
。 The only way I see (actually there is another one described here Entity Framework LINQ Get all items part of another collection , but now I think this is more appropriate) is to construct OR
filter using some expression build helper. 我看到的唯一方法(实际上还有另一个在这里描述的实体框架LINQ获取所有项目的另一个集合的一部分 ,但现在我认为这更合适)是使用一些表达式构建帮助器来构造
OR
过滤器。
For instance, using the PredicateUtils
class from Establish a link between two lists in linq to entities where clause , it could be like this: 例如,使用
PredicateUtils
类从linq中的两个列表到实体where子句之间建立链接 ,它可以是这样的:
First, add a little helper method 首先,添加一个小帮助方法
static Expression<Func<PropertyDetail, bool>> LocationFilter(string value, int index)
{
if (!string.IsNullOrEmpty(value))
{
if (index == 0) return d => d.Continent == value;
if (index == 1) return d => d.Country == value;
if (index == 2) return d => d.State == value;
}
return null;
}
and then use 然后使用
var locationsFilter = locations.Select(location => location.Select(LocationFilter)
.Aggregate(PredicateUtils.And)).Aggregate(PredicateUtils.Or);
if (locationsFilter != null)
query = query.Where(locationsFilter);
For completeness, here is the helper class used: 为了完整性,这里是使用的助手类:
public static class PredicateUtils
{
sealed class Predicate<T>
{
public static readonly Expression<Func<T, bool>> True = item => true;
public static readonly Expression<Func<T, bool>> False = item => false;
}
public static Expression<Func<T, bool>> Null<T>() { return null; }
public static Expression<Func<T, bool>> True<T>() { return Predicate<T>.True; }
public static Expression<Func<T, bool>> False<T>() { return Predicate<T>.False; }
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (Equals(left, right)) return left;
if (left == null || Equals(left, True<T>())) return right;
if (right == null || Equals(right, True<T>())) return left;
if (Equals(left, False<T>()) || Equals(right, False<T>())) return False<T>();
var body = Expression.AndAlso(left.Body, right.Body.Replace(right.Parameters[0], left.Parameters[0]));
return Expression.Lambda<Func<T, bool>>(body, left.Parameters);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (Equals(left, right)) return left;
if (left == null || Equals(left, False<T>())) return right;
if (right == null || Equals(right, False<T>())) return left;
if (Equals(left, True<T>()) || Equals(right, True<T>())) return True<T>();
var body = Expression.OrElse(left.Body, right.Body.Replace(right.Parameters[0], left.Parameters[0]));
return Expression.Lambda<Func<T, bool>>(body, left.Parameters);
}
static Expression Replace(this Expression expression, Expression source, Expression target)
{
return new ExpressionReplacer { Source = source, Target = target }.Visit(expression);
}
class ExpressionReplacer : ExpressionVisitor
{
public Expression Source;
public Expression Target;
public override Expression Visit(Expression node)
{
return node == Source ? Target : base.Visit(node);
}
}
}
UPDATE: As requested in the comments, here is the solution for locations
being List<Location>
: 更新:根据评论中的要求,以下是
List<Location>
locations
解决方案:
var locationsFilter = locations.Select(location =>
{
var filter = PredicateUtils.Null<PropertyDetail>();
if (!string.IsNullOrEmpty(location.Continent))
filter = filter.And(d => d.Continent == location.Continent);
if (!string.IsNullOrEmpty(location.Country))
filter = filter.And(d => d.Country == location.Country);
if (!string.IsNullOrEmpty(location.State))
filter = filter.And(d => d.State == location.State);
return filter;
}).Aggregate(PredicateUtils.Or);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.