从表达式创建DbSet<T>以创建动态查询服务

5

我正在尝试使用 Entity Framework 创建一个动态查询服务。

我的方法是创建一个轻量级的客户端,用于在 DTO 上创建查询,生成一个 IQueryable<T> 或一个 Expression,然后将该查询\表达式通过网络发送到执行服务,该服务将把 IQueryable<T> \ Expression 转换成 SQL,运行并返回一个 IEnumerable<S>

我想使用 Enumerable.Empty<T>.AsQueryable() 来创建查询,但是当我需要执行查询时,我需要能够将它们转换为 SQL - 这就是 Entity Framework 应该帮助我的地方。

Entity Framework 利用实现了 IQueryable<T> 接口的类(如 DbSet<T>DbQuery<T>)来完成其工作。
我可以创建一个客户端,使用一个 Entity Framework 的虚拟 DbContext 来创建 IQueryable<T>,这样我就可以在客户端上创建查询,并有能力从中生成 SQL。

我尝试混合使用框架,使用 Entity Framework 从我的 IQueryable<T> 中生成 SQL,然后使用 Dapper 执行它,但是当涉及到嵌套导航属性时会变得复杂,并且我仍然希望能够在我的服务上使用自定义的 Expression<Func<T,T>> 来修改查询。
因此,在客户端创建查询并将 SQL 发送到服务端并不适合我。

有没有办法可以从一个 Expression 创建一个 DbSet<T>?或者以某种方式“序列化”一个 DbSet<T>
是否有其他推荐的框架可以帮助我实现这个功能?

编辑 -> 这里是我试图实现的一些伪代码:

客户端:

var query = QueryCreator
.Get<MyDto>()
.Include(d => d.MySonDto)
.Where(d => d.IsActive)
.OrderBy(d => d.MySonDto.Date)
.Take(10);

 var result = service.Query(query); // or `service.Query(query.Expression);`

我的服务需要以某种方式接收查询,将其翻译成 SQL 语句,执行并在数据库中运行。
现在我考虑使用 Entity Framework 进行翻译,但我的主要问题是如何将 IQueryable 或 Expression 序列化,并在服务上创建一个 DbSet? (DbSet.Parse(expression) 就好了 :-) )

过去,我尝试过类似于你所提出的事情,特别是在泛型和表达式树序列化方面,遇到了巨大的困难。你是否考虑在服务层上公开一个 OData 端点?例如:https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-endpoint - Chris Pickford
@ChrisPickford - 我考虑了一个较弱的OData解决方案。由于我有其他未在此处指定的要求,因此仍在尝试这条路。 - Amir Popovich
你能添加一个伪代码示例吗? - Camilo Terevinto
@CamiloTerevinto - 我添加了一些伪代码。希望它能帮助你理解我的问题。 - Amir Popovich
你能使 DbContext 静态类型化(即添加已知实体),或者必须完全通用(即适用于任何数据库/表)吗? - Camilo Terevinto
显示剩余3条评论
1个回答

1

假设您已经知道T(根据注释),您可以使用收到的Expression使用CreateQuery创建查询:

var context = new YourContext();
var data = context.Set<T>()
    .AsQueryable()
    .Provider.CreateQuery(yourReceivedExpressionOfTEntity)
    .ToList();

谢谢回复。我可以通过使用新的MyDbContext().Set<T>()更轻松地获取DbSet<T>。 我不需要第二个代码部分,因为DbSet<T>IQueryable<T>。如果我不想在代码中留下DbSet<T>痕迹,我总是可以进行强制转换。 我的问题是如何传送查询并从查询\表达式创建一个DbSet<T> - Amir Popovich
如果你不知道T是什么,那么Set<T>()就无法工作。你不需要创建一个DbSet<T>,而是需要将你的Expression应用到DbSet<T>上(至少从你的问题中可以理解到这一点)。 - Camilo Terevinto
我知道T是什么,即使我收到T作为一种类型,我也可以始终使用MakeGenericMethod或其他反射解决方案来检索DbSet<T> - Amir Popovich
DbSet<T> 仅用于对数据库进行更改,查询时应使用 IQueryable<T>,所以我现在不确定你的问题是什么。 - Camilo Terevinto
DbSet<T> 是一个实现了 IQueryable<T> 和其他接口的 Entity Framework 类。你可以将 DbSet<T> 强制转换为 IQueryable<T>。基本上,DbSet (或继承它的 DbQuery<T>) 使您能够将查询转换为 SQL。这是您无法通过从 Enumerable.Empty<T>().AsQueryable 创建的简单 IQueryable<T> 实现的。 - Amir Popovich
@AmirPopovich 这就是我使用 .Provider.CreateQuery 的原因。 - Camilo Terevinto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接