CreateQuery<T> 正在检索所有数据,而不是应用谓词

问题描述 投票:0回答:2

我正在使用自动生成的 OData 客户端,我正在尝试创建一个通用的客户端类。

我想通过一些谓词。

public T Get<T>(Func<T, bool> predicate)
{
  var entityName = GetEntitySetName<T>();
  var query = _d365Context.CreateQuery<T>(entityName);
  return query.Where(predicate).FirstOrDefault();
}

private static string GetEntitySetName<T>()
{
  var originalEntitySetName = typeof(T).GetCustomAttributes(typeof(EntitySetAttribute), true).Cast<EntitySetAttribute>().SingleOrDefault()?.EntitySet;
  return originalEntitySetName;
}

此代码正在读取所有数据而不是应用谓词。 我已经尝试过的。

  • 我知道查询有 AddQueryOption 但我不想使用它。
  • 我也可以使用 context.EntityName,但它将是实体的硬编码。

所以我的问题是,是否有可能实现我想要做的事情? 有现成的图书馆吗?

PS - 我不想使用 Simple.Odata.Client


一个想法在我脑海中流动,使用谓词并以某种方式解析它并在 AddQueryOption 中使用它(此方法采用字符串名称和对象值)。但我对 Func 没有太多经验,所以不确定是否可以完成。

c# odata
2个回答
0
投票

你应该把你的

Func<T, bool>
换成表情:

public T Get<T>(Expression<Func<T, bool>> predicate)
{
  var entityName = GetEntitySetName<T>();
  var query = _d365Context.CreateQuery<T>(entityName);
  return query.Where(predicate).FirstOrDefault();
}

原因是不能以这种方式构造查询以从委托在数据库端执行。然而,如果你把它包装成一个表达式树,它就可以被正确地分析和转换。

相同的规则适用于

OData
协议 - 如果您想直接从源获取过滤后的数据,您应该使用
IQueryable<T>
从表达式构造而不是
IEnumerable<T>
从委托转换。


0
投票

使用

Func
使您的查询切换到
IEnumerable
所以它需要获取所有数据。 OData 需要与
IQueryable
一起工作才能翻译查询(同样适用于基于 LINQ 的 ORM,如 EF Core)。来自 OData 文档(假设您正在使用它,如果不是相同的规则适用于其他
IQueryable
提供商):

由于

DataServiceQuery<TElement> 
类实现了
IQueryable<T>
接口,OData 客户端库能够将针对实体集的 Linq 查询转换为针对数据服务资源执行的 URI。

所以你需要使用表达式树而不是简单的函数:

public T Get<T>(Expression<Func<T, bool>> predicate)
{
  var entityName = GetEntitySetName<T>();
  var query = _d365Context.CreateQuery<T>(entityName);
  return query.Where(predicate).FirstOrDefault();
}

阅读更多:

  1. IQueryable 和 IEnumerable 有什么区别?
  2. 表达树
© www.soinside.com 2019 - 2024. All rights reserved.