c# - 将 IQueryables 的“列表”应用于目标?
问题描述
我有这个想法来创建一个执行不同类型操作的 IQueryables 的“列表”。
所以基本上:
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
var listOfQueries = new List<IQueryable<Person>
{
query1,
query2,
query3
};
现在,我也有这个充满“Persons”的 DbSet,我想“应用”我对那个 DbSet 的所有查询。我该怎么做?甚至可能吗?
一个更新的例子:
var personQueryFactory = new PersonQueryFactory();
var personQueryByFirstname = personQueryFactory.CreateQueryByFirstname("Ronald"); //Query by Firstname.
var personQueryByAge = personQueryFactory.CreateQueryByAge(42); //Query by age.
var personQueryByHasChildWithAgeOver = personQueryFactory.CreateQueryByChildAgeOver(25); //Query using a "join" to the child-relationship.
var personQuerySkip = personQueryFactory.Take(5); //Only get the 5 first matching the queries.
var personQuery = personQueryFactory.AggregateQueries //Aggragate all the queries into one single query.
(
personQueryByFirstname,
personQueryByAge,
personQueryByHasChildWithAgeOver,
personQuerySkip
);
var personSurnames = personsService.Query(personQuery, e => new { Surname = e.Surname }); //Get only the surname of the first 5 persons with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDomainObjects = personsService.Query<DomainPerson>(personQuery); //Get the first 5 persons as a domain-object (mapping behind the "scenes") with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDaos = personsService.Query(personQuery); //Get the first 5 persons as a DAO-objects/entityframework-entities with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
这样做的原因是创建一种更“统一”的方式来创建和重用预定义查询,然后能够针对 DbSet 执行它们并将结果作为域对象而不是“实体”返回框架模型/对象"
解决方案
This is possible, but you need to reframe your approach slightly.
Storing the filters
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
Reading your intention, you don't actually want to handle IQueryable
objects, but rather the parameter that you supply to the Where
method.
Edit: I missed that the third one was a Select()
, not a Where()
. Ive adjusted the rest of the answer as if this had also been a Where()
. See the comments below for my response as to why you can't mix Select()
with Where()
easily.
Func<Person,bool> filter1 = (e => e.Name == "Ronald");
Func<Person,bool> filter2 = (e => e.Age == 43);
Func<Person,bool> filter3 = (e => e.EyeColor == "Blue");
This stores the same information (filter criteria), but it doesn't wrap each filter in an IQueryable
of its own.
A short explanation
Notice the
Func<A,B>
notation. In this case,A
is the input type (Person
), andB
is the output type (bool
).This can be extended further. A
Func<string,Person,bool>
has two input parameters (string
,Person
) and one output parameter (bool
). A usage example:
Func<string, Person, bool> filter = (inputString, inputPerson) => inputString == "TEST" && inputPerson.Age > 35;
There is always one output parameter (the last type). Every other mentioned type is an input parameter.
Putting the filters in a list
Intuitively, since Func<Person,bool>
represents a single filter; you can represent a list of filters by using a List<Func<Person,bool>>
.
Nesting generic types get a bit hard to read, but it does work just like any other List<T>
.
List<Func<Person,bool>> listOfFilters = new List<Func<Person,bool>>()
{
(e => e.Name == "Ronald"),
(e => e.Age == 43),
(e => e.EyeColor == "Blue")
};
Executing the filters
You're in luck. Because you want to apply all the filters (logical AND), you can do this very easily by stacking them:
var myFilteredData = myContext.Set<Person>()
.Where(filter1)
.Where(filter2)
.Where(filter3)
.ToList();
Or, if you're using a List<Func<Person,bool>>
:
var myFilteredData = myContext.Set<Person>().AsQueryable();
foreach(var filter in listOfFilters)
{
myFilteredData = myFilteredData.Where(filter);
}
Fringe cases
However, if you were trying to look for all items which fit one or more filters (logical OR), it becomes slightly more difficult.
The full answer is quite complicated.. You can check it here.
However, assuming you have the filters set in known variables, there is a simpler method:
Func<Person, bool> filterCombined =
e => filter1(e) || filter2(e) || filter3(e);
var myFilteredData = myContext.Set<Person>()
.Where(filterCombined)
.ToList();
推荐阅读
- c# - Unity2D 编辑器在放置/移除瓷砖后崩溃
- python - 在不覆盖方法的子类中调用 super().method()
- c# - 如何使用 OleDB 访问没有列标题的 .csv 文件的特定列?
- api - 通过 Flutter App 和 JSON Web Token 在 Django 中验证用户
- r - 当我有两个条件时如何制作热图,每个条件都一式三份?
- python - 如何像在 Pandas 中一样进行索引和匹配
- matlab - 冒号运算符在这里做什么?
- r - 下载已从 CRAN 中删除的旧版本的软件包
- python - python中变量和列表的范围
- kendo-grid - 如何从 Kendo Grid 获取客户端数据到控制器