如何使用 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<Dto>
是here。
@RobertHarvey 来自 microsoft 的示例返回 Dbset 的模型以及包含返回的其他表。我没有那些导航道具,只需要初始 DbSet 中的一个列。
@BjørnNørgaard returns the model of the Dbset<>
不,DbSet 不是 模型。这些示例不直接返回任何数据。 FromSqlRaw
或 FromSqlInterpolated
返回一个可以过滤的 IQueryable<T>
。没有什么能阻止您在 FromSqlRaw
.BTW 之后添加 .Select(x=>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?