首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将IQueryables的“列表”应用于目标?

将IQueryables的“列表”应用于目标?
EN

Stack Overflow用户
提问于 2018-04-25 16:42:09
回答 2查看 60关注 0票数 0

我有一个想法是创建一个执行不同类型操作的IQueryables的“列表”。

所以基本上:

代码语言:javascript
复制
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“应用”我的所有查询。我该怎么做呢?这有可能吗?

更新后的示例:

代码语言:javascript
复制
        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执行它们,并以域对象而不是“实体-框架-模型/对象”的形式返回结果。

EN

回答 2

Stack Overflow用户

发布于 2018-04-25 17:18:03

这是可能的,但您需要稍微调整一下您的方法。

存储过滤器

代码语言:javascript
复制
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()混为一谈。

代码语言:javascript
复制
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>有两个输入参数( (stringPerson)和一个输出参数(bool__)。使用示例:

Func<string, Person, bool> filter = (inputString, inputPerson) => inputString == "TEST" && inputPerson.Age > 35;

总是有一个输出参数(最后一个类型)。提到的每个其他类型都是一个输入参数

将过滤器放入列表中

直观地说,由于Func<Person,bool>表示单个筛选器,因此您可以使用List<Func<Person,bool>>表示筛选器列表。

嵌套泛型类型有点难读,但它的工作方式与任何其他List<T>一样。

代码语言:javascript
复制
List<Func<Person,bool>> listOfFilters = new List<Func<Person,bool>>()
{
    (e => e.Name == "Ronald"),
    (e => e.Age == 43),
    (e => e.EyeColor == "Blue")
};

执行过滤器

你真走运。因为您希望应用所有的过滤器(逻辑AND),所以可以通过堆叠它们来非常容易地完成此操作:

代码语言:javascript
复制
var myFilteredData = myContext.Set<Person>()
                                 .Where(filter1)
                                 .Where(filter2)
                                 .Where(filter3)
                                 .ToList();

或者,如果您使用的是List<Func<Person,bool>>

代码语言:javascript
复制
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

代码语言:javascript
复制
Func<Person, bool> filterCombined = 
              e => filter1(e) || filter2(e) || filter3(e);

var myFilteredData = myContext.Set<Person>()
                                 .Where(filterCombined)
                                 .ToList();
票数 4
EN

Stack Overflow用户

发布于 2018-04-25 21:15:05

其中一个问题是,只有当您的DbSet有效时,IQueryable集合才有效。一旦您的DbContext处理完毕,您精心填充的集合就一文不值了。

因此,您必须考虑使用DbSet<Person>之外的另一种方法来重建查询

虽然乍一看,它们似乎是一样的,但IEnumerableIQueryable之间是有区别的。Enumerable中包含所有内容,可在结果序列中进行枚举。

另一方面,Queryable包含一个表达式和一个提供者。提供程序知道可以从何处获取数据。这通常是一个数据库,但也可以是一个CSV文件或其他可以获取序列的项目。提供程序的任务是解释表达式并将其转换为数据库可以理解的格式,通常是SQL。

将linq语句连接成一个大型linq语句时,数据库不会被访问。只有表达式被更改。

一旦调用了GetEnumerator()和MoveNext() (通常是通过执行ForEach或ToList()或类似的操作),表达式就会被发送到提供程序,由提供程序将其转换为数据库能够理解的查询格式并执行查询。查询的结果是一个可枚举的序列,因此调用提供者的查询结果的Getenumerator()和MoveNext()。

因为您的IQueryable持有此提供程序,所以在释放该提供程序后,您不能再枚举。

当使用实体框架时,DbSet持有提供者。在提供程序中是由DbContext保存的数据库信息。一旦清除了DbContext,就不能再使用IQueryable了:

代码语言:javascript
复制
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:

代码语言:javascript
复制
static IQueryable<TSource> ToQueryable<TSource>(this IQueryProvider provider, 
    Expression expression)
{
     return provider.CreateQuery(expression);
}

//好的,让我们再添加以下内容:

代码语言:javascript
复制
static IQueryable<Tsource> ToQueryable<TSource>(this DbContext dbContext, 
     Expression expression)
{
     return dbContext.Set<TSource>.Provider.ToQueryable<TSource>(expression);
}

有关扩展函数的帮助,请参阅Extension Functions Demystified

现在只需创建一次表达式集合。对于快速查找,请将其设置为字典:

代码语言:javascript
复制
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.
}

用法:

代码语言:javascript
复制
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
     }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50017736

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档