当网格属性没有实体属性时,如何构建基于离网过滤的 LINQ predicate/dynamic.LINQ 查询?
How can I build a LINQ predicate/dynamic.LINQ query based off grid filtering when the grid properties don't have the entity properties?
我有一个网格通过我的过滤器。所以我可能有一个 object 像:
var filter = new Filter(){
Member = "Titles",
Operator = Filter.Operators.IsEqualTo,
Value = "Developer"
};
然后我需要使用它并扩展一个 IQueryable 以便做到这一点,我使用 dynamic.LINQ 并有一个方法来应用这些过滤器:
private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery)
{
switch (filter.Operator)
{
case ReportFilter.Operators.Contains:
baseQuery = baseQuery.Where(string.Format("{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.DoesNotContain:
baseQuery = baseQuery.Where(string.Format("!{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsEqualTo:
baseQuery = baseQuery.Where(string.Format("{0} = @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNotEqualTo:
baseQuery = baseQuery.Where(string.Format("{0} != @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.StartsWith:
baseQuery = baseQuery.Where(string.Format("{0}.StartsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.EndsWith:
baseQuery = baseQuery.Where(string.Format("{0}.EndsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNull:
baseQuery = baseQuery.Where(string.Format("{0} = NULL", filter.Member));
break;
case ReportFilter.Operators.IsNotNull:
baseQuery = baseQuery.Where(string.Format("{0} != NULL", filter.Member));
break;
case ReportFilter.Operators.IsEmpty:
baseQuery = baseQuery.Where(string.Format("string.IsNullOrEmpty({0})", filter.Member));
break;
case ReportFilter.Operators.IsNotEmpty:
baseQuery = baseQuery.Where(string.Format("!string.IsNullOrEmpty({0})", filter.Member));
break;
}
return baseQuery;
}
然而,这仅适用于 non-collections。我怎样才能让它与 collections 一起工作?如果我有这个模型:
public class UserReport : Entity
{
public string Name { get; set; }
public string Email { get; set; }
public List<string> Titles { get; set; }
}
并将此查询作为基础:
IQueryable<UserReport> baseQuery = MyDbContext.DbSet<User>.Select(user => new UserReport
{
Id = user.Id,
Name = user.FirstName + " " + user.LastName,
Email = user.Email,
Titles = user.Positions.Select(apptment => apptment.Title).ToList()
})
所以我可以这样打电话:
IQueryable<UserReport> filteredQuery = ApplyFilters(filters, baseQuery);
如何将上述过滤器转换为 LINQ,例如:
baseQuery.Where(userReport => userReport.Titles.Any(title => title == "Developer")
这可以用动态 LINQ 完成吗?还是我需要构建自己的谓词?如果是这样,我该怎么做?
System.Linq.Dynamic
和 System.Linq.Expressions
都可以。
这是 System.Linq.Dynamic
的解决方案,以使其接近您当前的代码。您只需确定成员是否为 collection 并使用以下模式作为动态标准:
Collection: {PropertyName}.Any(it{condition})
Object: {PropertyName}{condition}
实现可能是这样的(基本上用 Func<string, string, string>
替换 string.Format
):
private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery)
{
var property = typeof(TReportClass).GetProperty(filterMember);
bool isCollection = property.Type != typeof(string) &&
&& typeof(IEnumerable).IsAssignableFrom(property.Type);
Func<string, string, string> condtion;
if (isCollection)
condition = (format, member) => string.Format("{0}.Any({1})", member, string.Format(format, "it"));
else
condition = (format, member) => string.Format(format, member);
switch (filter.Operator)
{
case ReportFilter.Operators.Contains:
baseQuery = baseQuery.Where(condition("{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.DoesNotContain:
baseQuery = baseQuery.Where(condition("!{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsEqualTo:
baseQuery = baseQuery.Where(condition("{0} = @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNotEqualTo:
baseQuery = baseQuery.Where(condition("{0} != @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.StartsWith:
baseQuery = baseQuery.Where(condition("{0}.StartsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.EndsWith:
baseQuery = baseQuery.Where(condition("{0}.EndsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNull:
baseQuery = baseQuery.Where(condition("{0} = NULL", filter.Member));
break;
case ReportFilter.Operators.IsNotNull:
baseQuery = baseQuery.Where(condition("{0} != NULL", filter.Member));
break;
case ReportFilter.Operators.IsEmpty:
baseQuery = baseQuery.Where(condition("string.IsNullOrEmpty({0})", filter.Member));
break;
case ReportFilter.Operators.IsNotEmpty:
baseQuery = baseQuery.Where(condition("!string.IsNullOrEmpty({0})", filter.Member));
break;
}
return baseQuery;
}
我有一个网格通过我的过滤器。所以我可能有一个 object 像:
var filter = new Filter(){
Member = "Titles",
Operator = Filter.Operators.IsEqualTo,
Value = "Developer"
};
然后我需要使用它并扩展一个 IQueryable 以便做到这一点,我使用 dynamic.LINQ 并有一个方法来应用这些过滤器:
private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery)
{
switch (filter.Operator)
{
case ReportFilter.Operators.Contains:
baseQuery = baseQuery.Where(string.Format("{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.DoesNotContain:
baseQuery = baseQuery.Where(string.Format("!{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsEqualTo:
baseQuery = baseQuery.Where(string.Format("{0} = @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNotEqualTo:
baseQuery = baseQuery.Where(string.Format("{0} != @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.StartsWith:
baseQuery = baseQuery.Where(string.Format("{0}.StartsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.EndsWith:
baseQuery = baseQuery.Where(string.Format("{0}.EndsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNull:
baseQuery = baseQuery.Where(string.Format("{0} = NULL", filter.Member));
break;
case ReportFilter.Operators.IsNotNull:
baseQuery = baseQuery.Where(string.Format("{0} != NULL", filter.Member));
break;
case ReportFilter.Operators.IsEmpty:
baseQuery = baseQuery.Where(string.Format("string.IsNullOrEmpty({0})", filter.Member));
break;
case ReportFilter.Operators.IsNotEmpty:
baseQuery = baseQuery.Where(string.Format("!string.IsNullOrEmpty({0})", filter.Member));
break;
}
return baseQuery;
}
然而,这仅适用于 non-collections。我怎样才能让它与 collections 一起工作?如果我有这个模型:
public class UserReport : Entity
{
public string Name { get; set; }
public string Email { get; set; }
public List<string> Titles { get; set; }
}
并将此查询作为基础:
IQueryable<UserReport> baseQuery = MyDbContext.DbSet<User>.Select(user => new UserReport
{
Id = user.Id,
Name = user.FirstName + " " + user.LastName,
Email = user.Email,
Titles = user.Positions.Select(apptment => apptment.Title).ToList()
})
所以我可以这样打电话:
IQueryable<UserReport> filteredQuery = ApplyFilters(filters, baseQuery);
如何将上述过滤器转换为 LINQ,例如:
baseQuery.Where(userReport => userReport.Titles.Any(title => title == "Developer")
这可以用动态 LINQ 完成吗?还是我需要构建自己的谓词?如果是这样,我该怎么做?
System.Linq.Dynamic
和 System.Linq.Expressions
都可以。
这是 System.Linq.Dynamic
的解决方案,以使其接近您当前的代码。您只需确定成员是否为 collection 并使用以下模式作为动态标准:
Collection: {PropertyName}.Any(it{condition})
Object: {PropertyName}{condition}
实现可能是这样的(基本上用 Func<string, string, string>
替换 string.Format
):
private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery)
{
var property = typeof(TReportClass).GetProperty(filterMember);
bool isCollection = property.Type != typeof(string) &&
&& typeof(IEnumerable).IsAssignableFrom(property.Type);
Func<string, string, string> condtion;
if (isCollection)
condition = (format, member) => string.Format("{0}.Any({1})", member, string.Format(format, "it"));
else
condition = (format, member) => string.Format(format, member);
switch (filter.Operator)
{
case ReportFilter.Operators.Contains:
baseQuery = baseQuery.Where(condition("{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.DoesNotContain:
baseQuery = baseQuery.Where(condition("!{0}.Contains(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsEqualTo:
baseQuery = baseQuery.Where(condition("{0} = @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNotEqualTo:
baseQuery = baseQuery.Where(condition("{0} != @0", filter.Member), filter.Value);
break;
case ReportFilter.Operators.StartsWith:
baseQuery = baseQuery.Where(condition("{0}.StartsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.EndsWith:
baseQuery = baseQuery.Where(condition("{0}.EndsWith(@0)", filter.Member), filter.Value);
break;
case ReportFilter.Operators.IsNull:
baseQuery = baseQuery.Where(condition("{0} = NULL", filter.Member));
break;
case ReportFilter.Operators.IsNotNull:
baseQuery = baseQuery.Where(condition("{0} != NULL", filter.Member));
break;
case ReportFilter.Operators.IsEmpty:
baseQuery = baseQuery.Where(condition("string.IsNullOrEmpty({0})", filter.Member));
break;
case ReportFilter.Operators.IsNotEmpty:
baseQuery = baseQuery.Where(condition("!string.IsNullOrEmpty({0})", filter.Member));
break;
}
return baseQuery;
}