我有一个想法是创建一个执行不同类型操作的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
};现在,我还有一个充满“DbSet”的DbSet,我想对这个Person“应用”我的所有查询。我该怎么做呢?这有可能吗?
更新后的示例:
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执行它们,并以域对象而不是“实体-框架-模型/对象”的形式返回结果。
发布于 2018-04-25 17:18:03
这是可能的,但您需要稍微调整一下您的方法。
存储过滤器
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);读懂您的意图后,您实际上并不想处理IQueryable对象,而是想要处理您提供给Where方法的参数。
编辑:我错过了第三个是Select()__,而不是Where()__。我已经调整了答案的其余部分,就好像这也是一个Where()__。请看下面的评论,我的回答是为什么你不能轻易地将Select()和Where()混为一谈。
Func<Person,bool> filter1 = (e => e.Name == "Ronald");
Func<Person,bool> filter2 = (e => e.Age == 43);
Func<Person,bool> filter3 = (e => e.EyeColor == "Blue");这将存储相同的信息(过滤条件),但它不会将每个过滤器包装在其自己的IQueryable中。
一个简短的解释
请注意Func<A,B>符号。在本例中,输入类型(Person),为A,输出类型(bool).为B
这可以进一步扩展。Func<string,Person,bool>有两个输入参数( (string,Person)和一个输出参数(bool__)。使用示例:
Func<string, Person, bool> filter = (inputString, inputPerson) => inputString == "TEST" && inputPerson.Age > 35;
总是有一个输出参数(最后一个类型)。提到的每个其他类型都是一个输入参数。
将过滤器放入列表中
直观地说,由于Func<Person,bool>表示单个筛选器,因此您可以使用List<Func<Person,bool>>表示筛选器列表。
嵌套泛型类型有点难读,但它的工作方式与任何其他List<T>一样。
List<Func<Person,bool>> listOfFilters = new List<Func<Person,bool>>()
{
(e => e.Name == "Ronald"),
(e => e.Age == 43),
(e => e.EyeColor == "Blue")
};执行过滤器
你真走运。因为您希望应用所有的过滤器(逻辑AND),所以可以通过堆叠它们来非常容易地完成此操作:
var myFilteredData = myContext.Set<Person>()
.Where(filter1)
.Where(filter2)
.Where(filter3)
.ToList();或者,如果您使用的是List<Func<Person,bool>>
var myFilteredData = myContext.Set<Person>().AsQueryable();
foreach(var filter in listOfFilters)
{
myFilteredData = myFilteredData.Where(filter);
}边缘案例
但是,如果您试图查找符合一个或多个过滤器(逻辑或)的所有项,则会变得稍微困难一些。
The full answer is quite complicated.。您可以在这里查看。
但是,假设您在已知变量中设置了过滤器,则there is a simpler method
Func<Person, bool> filterCombined =
e => filter1(e) || filter2(e) || filter3(e);
var myFilteredData = myContext.Set<Person>()
.Where(filterCombined)
.ToList();发布于 2018-04-25 21:15:05
其中一个问题是,只有当您的DbSet有效时,IQueryable集合才有效。一旦您的DbContext处理完毕,您精心填充的集合就一文不值了。
因此,您必须考虑使用DbSet<Person>之外的另一种方法来重建查询
虽然乍一看,它们似乎是一样的,但IEnumerable和IQueryable之间是有区别的。Enumerable中包含所有内容,可在结果序列中进行枚举。
另一方面,Queryable包含一个表达式和一个提供者。提供程序知道可以从何处获取数据。这通常是一个数据库,但也可以是一个CSV文件或其他可以获取序列的项目。提供程序的任务是解释表达式并将其转换为数据库可以理解的格式,通常是SQL。
将linq语句连接成一个大型linq语句时,数据库不会被访问。只有表达式被更改。
一旦调用了GetEnumerator()和MoveNext() (通常是通过执行ForEach或ToList()或类似的操作),表达式就会被发送到提供程序,由提供程序将其转换为数据库能够理解的查询格式并执行查询。查询的结果是一个可枚举的序列,因此调用提供者的查询结果的Getenumerator()和MoveNext()。
因为您的IQueryable持有此提供程序,所以在释放该提供程序后,您不能再枚举。
当使用实体框架时,DbSet持有提供者。在提供程序中是由DbContext保存的数据库信息。一旦清除了DbContext,就不能再使用IQueryable了:
IQueryable<Person> query = null;
using (var dbContext = new MyDbcontext())
{
query = dbContext.Persons.Where(person => person.Age > 20);
}
foreach (var person in query)
{
// expect exception: the DbContext is already Disposed
}因此,您不能将提供程序放入集合或可能的查询中。但是,您可以记住这个表达式。您对表达式的唯一要求是它返回一个Person。您还需要一个接受此表达式的函数和Person的QueryaProvider,以便将其转换为IQueryable。
让我们为此创建一个泛型函数,这样它就可以用于任何类型,而不仅仅是Person:
static IQueryable<TSource> ToQueryable<TSource>(this IQueryProvider provider,
Expression expression)
{
return provider.CreateQuery(expression);
}//好的,让我们再添加以下内容:
static IQueryable<Tsource> ToQueryable<TSource>(this DbContext dbContext,
Expression expression)
{
return dbContext.Set<TSource>.Provider.ToQueryable<TSource>(expression);
}有关扩展函数的帮助,请参阅Extension Functions Demystified
现在只需创建一次表达式集合。对于快速查找,请将其设置为字典:
enum PersonQuery
{
ByFirstname,
ByAge,
ByHasChildWithAgeOver,
Skip,
}
public IReadOnlyDictionary<PersonQuery, Expression> CreateExpressions()
{
Dictionary<PersonQuery, Expression> dict = new Dictionary<PersonQuery, Expression>();
using (var dbContext = new MyDbContext())
{
IQueryable<Person> queryByFirstName = dbContext.Persons
.Where(...);
dict.Add(PersonQuery.ByfirstName, queryByFirstName.Expression);
... // etc for the other queries
}
return dict.
}用法:
IReadOnlyCollection<Person> PerformQuery(PersonQuery queryId)
{
using (var dbContext = new MyDbContext())
{
// get the Expression from the dictionary:
var expression = this.QueryDictionary[queryId];
// translate to IQueryable:
var query = dbContext.ToQueryable<Person>(expression);
// perform the query:
return query.ToList();
// because all items are fetched by now, the DbContext may be Disposed
}
}https://stackoverflow.com/questions/50017736
复制相似问题