繁体   English   中英

带有子集合的 Select 的动态 LINQ 表达式(实体框架)

[英]Dynamic LINQ expression for Select with child collection (Entity Framework)

我想问你一种使用嵌套子集合动态创建 LINQ Select 表达式的方法。 所选子集合中的字段可以是 static,但是我想动态传递当前实体中的字段列表,以及导航属性引用的其他实体中的字段。 这是查询的 static 版本,类似于我在很多地方的代码中使用的那个,并且我想动态创建它:

var listItems = _efDbContext.Blogs.Select(x => new {      
    ID = x.ID,
    Name = x.Name, //field from the current entity     
    AuthorName = x.Author.Name, //field referenced by navigation property
    ...<other fields from current or referenced entities(like AuthorName above), passed dynamically>
    Posts = x.Posts.Select(y => new { //this select is 'static', is the same for other queries 
       Id = x.Id,
       Name = x.Name
    })
});

我试图从这两个帖子的答案中找出一些东西,但我没有成功。 Ivan Stoev 的回答真的很酷,但它不支持引用的属性,而且我在上面的示例中也有这个 static 嵌套子集合 select

动态构建 select 列表从 linq 到实体查询

EF Linq-动态 Lambda 表达式树

I have also tried to accomplish this using libraries like Dynamic.Linq.Core or Automapper but it seems that the former does not support select with child collections and the latter needs a DTO class and I dont want to create hundred of DTOs for each combination of select 表达式中的字段。

我还认为整个方法可能过于复杂,也许我应该改用参数化 SQL 查询,这绝对是一个可能的选择,但我认为我可以在我项目的其他地方以不同的变体重用这个动态 LINQ 表达式,以及从长远来看,这会有所帮助:)

(我使用的是 EF Core 3.1)

我来自链接帖子的解决方案处理了非常简化的场景。 它可以扩展为处理嵌套属性和方法调用,但主要问题是它的结果不是真正动态的。 它需要预定义的类型并选择性地选择/填充该类型的成员。 这使得它类似于 AutoMapper 显式扩展功能,因此使用后者可能会更好,因为它提供了灵活的自动/手动映射选项。

对于真正动态的 output,您需要一个在运行时生成动态类的库。 动态 LINQ 就是其中之一,除了它不寻常的表达语言之外,它实际上可以用于您的场景(至少来自https://dynamic-linq.net/的那个,因为该库有多种风格,它们都支持/不支持某些东西)。

以下是使用System.Linq.Dynamic.Core package(或它的 EF Core 版本Microsoft.EntityFrameworkCore.DynamicLinq以防您需要异步可查询支持)的示例:

var selectList = new List<string>();
// Dynamic part
selectList.Add("ID");
selectList.Add("Name");
selectList.Add("Author.Name as AuthorName");
// Static part. But the nested Select could be built dynamically as well
selectList.Add("Posts.Select(new (Id, Name)) as Posts");

var listItems = _efDbContext.Blogs
    .Select($"new ({string.Join(", ", selectList)})")
    .ToDynamicList();

如果您可以添加额外的依赖项,我强烈建议您依赖Automapper 的ProjectTo() ,它有效地用允许可重用映射(包括嵌套映射)的方法替换您的Select

一个简单的例子是:

public class PostDto
{
    public string Title { get; set; }
}

public class BlogDto
{
    public string Name { get; set; }
    public PostDto[] Posts { get; set; }
}

public class AuthorDto
{
    public string Name { get; set; }
    public PostDto MostRecentPost { get; set; }
}

var configuration = new MapperConfiguration(cfg => {
    cfg.CreateMap<Post, PostDto>();
    cfg.CreateMap<Blog, BlogDto>();
    cfg.CreateMap<Author, AuthorDto>()
       .ForMember(
           dto => dto.MostRecentPost, 
           conf => conf.MapFrom(
               author => author.Posts
                               .OrderByDescending(x => x.Date)
                               .FirstOrDefault()));
});

public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
    using (var context = new orderEntities())
    {
        var blogsWithPosts = context
                               .Blogs
                               .ProjectTo<BlogDto>(configuration)
                               .ToList();

        var authorsWithPost = context
                               .Authors
                               .ProjectTo<AuthorDto>(configuration)
                               .ToList();
    }
}

请注意,在我的实际 LINQ 查询中,我什至不需要提及PostPostDto 这是因为ProjectTo使用映射配置来确定需要填写哪些字段,而配置包含每个 DTO 字段需要哪些实体字段的信息。

我在很大程度上假设实体和 DTO 之间的所有属性的名称都匹配,因为这样您就不需要手动 map 它们。 这样做是为了保持示例简单,但还包括一个示例,说明如何手动将 map AuthorDto.MostRecentPost到实际子查询。
对于更复杂的映射,我建议查看 Automapper 的文档。 这比我在这里的一个答案中所能解释的要多。

实际上, ProjectTo会为您生成适当的Select语句。

这也意味着,如果我决定更改PostDto及其映射,则此更改会自动反映在两个 LINQ 查询中,而无需更改 LINQ 查询本身。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM