如何使用 Entity Framework Core 5.0 将复杂 SQL 查询的结果行映射到自定义 DTO?

Posted

技术标签:

【中文标题】如何使用 Entity Framework Core 5.0 将复杂 SQL 查询的结果行映射到自定义 DTO?【英文标题】:How do I map result rows of complex SQL query into custom DTO with Entity Framework Core 5.0? 【发布时间】:2021-08-30 09:46:28 【问题描述】:

我有一个自定义且有些复杂的 SQL 查询,需要通过 EF Core DbContext 执行。

我意识到下面的示例代码可以使用简单的 .Select() 来解决,但不幸的是,我的实际表格是通过魔术键连接的,没有导航属性。 ????

无论如何我可以在不通过导航属性选择的情况下实现以下目标?

namespace ClassLibrary1

    public class MyService
    
        private readonly MyContext _context;

        public void MyMethod()
        
            var mysql = @"SELECT Foo.FooName, Bar.BarValue FROM Foo JOIN Bar ON Foo.Key = Bar.Key";
            List<Dto> dtoList = _context.Database.MethodImLookingFor<Dto>(mySql); // Any ideas?
        
    
    
    public class MyContext : DbContext
    
        public DbSet<Foo> Foo  get; set; 
        public DbSet<Bar> Bar  get; set; 
    

    // My models are only records for brevity.
    public record Foo(string Key, string FooName);
    public record Bar(string Key, string BarValue);
    public record Dto(string FooName, string BarValue);

实际的 SQL 查询:

SELECT R.Id, S.Code, V.Code
FROM dbo.Results R
         JOIN dbo.Variable V ON V.Code = R.Value
         JOIN dbo.Severity S ON S.Id = V.SeverityId
WHERE R.Type = N'Type' AND S.Code = 'ABC' OR S.Code = 'CBA' AND R.Id = '123'

我已经通过microsoft docs。如果不通过导航属性,这些都不会映射到自定义 DTO。

【问题讨论】:

老实说,我不会说带有单个连接的查询是“复杂的”;我们还缺少另一个查询吗? 我知道。实际的查询是完全不同的。我会在一分钟内发布旧的。 MethodImLookingFor&lt;Dto&gt; 是here。 @RobertHarvey 来自 microsoft 的示例返回 Dbset 的模型以及包含返回的其他表。我没有那些导航道具,只需要初始 DbSet 中的一个列。 @BjørnNørgaard returns the model of the Dbset&lt;&gt; 不,DbSet 不是 模型。这些示例不直接返回任何数据。 FromSqlRawFromSqlInterpolated 返回一个可以过滤的 IQueryable&lt;T&gt;。没有什么能阻止您在 FromSqlRaw.BTW 之后添加 .Select(x=&gt;newx.Id, x.SCode,x.VCode),这就是您在 EF Core 中查询函数的方式 【参考方案1】:

稍微改变你的查询

SELECT R.Id, S.Code as Scode, V.Code as Vcode
FROM dbo.Results R
         JOIN dbo.Variable V ON V.Code = R.Value
         JOIN dbo.Severity S ON S.Id = V.SeverityId
WHERE R.Type = N'Type' AND S.Code = 'ABC' OR S.Code = 'CBA' AND R.Id = '123'

为查询结果创建一个新类

[NotMapped]
public class SpResult

public int Id get;set;
public string Scode get; set;
public string Vcode get; set;

添加到数据库上下文

 public virtual DbSet<SpResult> SpResults  get; set; 

 modelBuilder.Entity<SpResult>(e =>
   
  e.HasNoKey();
   );

最后是代码

var spResult =  _context.SpResults.FromSqlRaw(mysql).ToList();

List<Dto> dtoList = spResult.Select( i=> new Dto

 ID = i.Id,
 FooName =i.Scode,
 BarValue=i.Vcode,
 
).ToList();

【讨论】:

在创建迁移时这不会也导致创建新表吗? 我不这么认为。我用了很多次,从来没有遇到过任何问题。它具有 [NotMapped] 属性【参考方案2】:

你所做的是使用Dapper。比 EF 快很多,并且您可以通过 EF dbcontext 访问它 - 所以它确实是一个扩展 EF 的库,无需创建新连接或类似的东西。

效果很好。

public class Dog

    public int? Age  get; set; 
    public Guid Id  get; set; 
    public string Name  get; set; 
    public float? Weight  get; set; 

    public int IgnoredProperty  get  return 1;  


var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new  Age = (int?)null, Id = guid );

【讨论】:

【参考方案3】:

决定硬着头皮将查询重写为 LINQ。

【讨论】:

这是什么意思?我知道熟悉 EF 的人往往对编写 SQL 感到害羞,但是……真的吗?很难看出这个答案对其他人有何用处。 我很好奇 EF 没有简单的“将此查询的行映射到此模型”。所有答案要么修改 dbContext 要么创建数据库级别的视图。这些解决方案对我来说都不可行,因此我决定将 SQL 查询重写为纯 LINQ。

以上是关于如何使用 Entity Framework Core 5.0 将复杂 SQL 查询的结果行映射到自定义 DTO?的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 学习系列 - 认识理解Entity Framework

如何使用 Entity Framework Core 模拟异步存储库

如何使用 Entity Framework 生成和自动递增 Id

如何使用 Entity Framework Core 正确保存 DateTime?

如何使用 Entity Framework Core 获取主键值

如何在 Entity Framework 6 中使用 DbDataReader 获取表名?