【问题标题】:Entity Framework 6 vs Entity Framework Core Raw Sql实体框架 6 与实体框架核心原始 Sql
【发布时间】:2018-03-09 01:52:33
【问题描述】:

Entity Framework 6 示例为非实体类型编写 SQL 查询:

context.Database.SqlQuery<string>(" ; with tempSet as " + 
                                   "(select " + 

在 Entity Framework 6 中,我还可以使用SqlQuery 编写以下查询。如何使用 Entity Framework Core 运行以下查询?

; with tempSet as 
(
    select 
        transitionDatetime = l.transitionDate,
        gateName = g.gateName,
        staffid = l.staffid,
        idx = row_number() over(partition by l.staffid order by l.transitionDate) -
              row_number() over(partition by l.staffid, cast(l.transitionDate as date) order by l.transitionDate),
        transitionDate = cast(l.transitionDate as date)
    from
        logs l 
    inner join 
        staff s on l.staffid = s.staffid and staffType = 'Student'
    join  
        gate g on g.gateid = l.gateid
), groupedSet as
(
    select 
        t1.*,
        FirstGateName = t2.gatename,
        lastGateName = t3.gatename
    from
        (select
             staffid,
             mintransitionDate = min(transitionDatetime),
             maxtransitionDate = case when count(1) > 1 then max(transitionDatetime) else null end,
             transitionDate = max(transitionDate),
             idx
         from
             tempSet 
         group by 
             staffid, idx) t1
    left join
        tempSet t2 on t1.idx = t2.idx 
                   and t1.staffid = t2.staffid 
                   and t1.mintransitionDate = t2.transitionDatetime
    left join
        tempSet t3 on t1.idx = t3.idx 
                   and t1.staffid = t3.staffid 
                   and t1.maxtransitionDate = t3.transitionDatetime
    where 
        t1.transitionDate between @startdate and @enddate
 )
 select
     t.*,
     g.mintransitionDate,
     g.maxtransitionDate,
     g.FirstGateName,
     g.LastGateName
 from 
     groupedSet g
 right join
     (select 
          d,
          staffid
      from
          (select top (select datediff(d, @startdate, @endDate))
               d = dateadd(d, row_number() over(order by (select null)) - 1,  @startDate)
           from
               sys.objects o1 
           cross join 
               sys.objects o2) tally
    cross join
        staff 
    where 
        staff.stafftype = 'Student') t on cast(t.d as date) = cast(g.transitionDate as date) 
                                       and t.staffid = g.staffid
    order by 
        t.d asc, t.staffid asc

如何使用 Entity Framework Core?为非实体类型编写 SQL 查询?

【问题讨论】:

    标签: entity-framework-6 entity-framework-core


    【解决方案1】:

    当它是单个表时,我直接从上下文中完成了“fromsql”,但我意识到这不是你想要的,但它建立在它之上。

    var blogs = context.Blogs
        .FromSql("SELECT * FROM dbo.Blogs")
        .ToList();
    

    但是,在像您这样的情况下,它很复杂,并且需要连接多个表和 CTE。我建议您在代码中创建一个自定义对象 POCO C#,并在您的模型构建器中为其分配一个 DbSet。然后你可以这样做:

     var custom = context.YOURCUSTOMOBJECT.FromSql("(crazy long SQL)").ToList();
    

    如果您的回报与它可能工作的类型匹配。我做了类似的事情,只是将我的整个方法包装在一个过程中。但是,如果您希望部署 EF Core,您需要手动向上迁移,然后在迁移的“向上”方法中手动添加 proc 的创建。如果你走这条路,你的 proc 需要已经存在于服务器上,或者像上面所说的那样部署它,并做类似的事情:

    context.pGetResult.FromSql("pGetResult @p0, @p1, @p2", parameters: new[] { "Flight", null, null }).ToList()
    

    需要注意的重要一点是,您需要首先在模型上下文中创建一个 DBSet 对象,以便您调用的上下文知道它从直接 SQL 返回的类型良好的对象。它必须与返回的列和类型完全匹配。

    编辑 3-8 为了确保你需要做几个步骤,我会写出来:

    1. 一个 POCO 类,在不同属性之上具有 [Key] 的数据注释。此类与您的列完全匹配过程返回的内容。
    2. 您的上下文中的 DBSet。
    3. 使用以下命令创建新迁移:“Dotnet ef Migrations add 'yourname'”
    4. 观察新的迁移脚本。如果创建了为 POCO 生成表的任何内容,请将其删除。你不需要它。这是针对结果集而不是存储在数据库中的。
    5. 更改“向上”部分以手动将 SQL 脚本写入数据库,如下所示。如果您想在“向下”部分恢复,请确保删除数据

      protected override void Up(MigrationBuilder migrationBuilder)
      {
          migrationBuilder.Sql(
          "create proc POCONameAbove" +
          "( @param1 varchar(16), @Param2 int) as " +
          "BEGIN " +
          "Select * " +
          "From Table "
          "Where param1 = @param1 " +
          " AND param2 = @param2 "
          "END"
          );
       }
      
       protected override void Down(MigrationBuilder migrationBuilder)
       {
          migrationBuilder.Sql("drop proc POCONameAbove");
       }
      
    6. 所以现在您基本上劫持了迁移以明确地执行您想要的操作。通过使用“dotnet ef database update 'yourmigrationname'”将更改部署到数据库来测试它。
    7. 观察数据库,如果数据库更新成功并且您没有在迁移中意外创建表,它应该有您的 proc。
    8. 你说你不明白的部分是什么在 EF Core 中获取数据。让我们分解一下:

      context.pGetResult.FromSql("pGetResult @p0, @p1, @p2", parameters: new[] { "Flight", null, null }).ToList()
      

    context.pGetResult = 正在使用您制作的 DbSet。它可以让你很好地输入你的过程。 .FromSQL( = 告诉上下文你将直接在字符串中执行一些 SQL。 "pGetResult @p0, @p1, @p2" = 我在数据库中命名一个具有三个参数的过程。 , parameters: new[] { "Flight", null, null }) = 我只是在做一个对象数组,根据需要按参数顺序排列。当然,您需要匹配 SQL 类型,但只要没问题就可以了。 .ToListAsync() = 我想要一个集合,调试时我的 goto 总是 ToList。

    希望对您有所帮助。一旦我知道这会起作用,它就打开了我可以做的另一个世界。你可以看看我做过的一个未完成的项目,以供参考。我硬编码了一个控制器来显示带有预设值的过程。但是可以很容易地更改为将它们注入 api 中。 https://github.com/djangojazz/EFCoreTest/tree/master/EFCoreCodeFirstScaffolding

    【讨论】:

    • 我不明白这部分。 context.pGetResult.FromSql("pGetResult "@p0, "@p1, "@p2", parameters: new[] { "Flight", null, null }).ToList() 如何将上述问题写成实体框架核心?
    • @serenity 它是 EF Core
    • 我照你说的做了。他给出了这个错误。实体类型需要定义一个主键
    • 您需要将 [Key] 属性添加到您的模型,(您的自定义对象):类似这样:entityframeworktutorial.net/code-first/…
    • 奇怪的是,我以为这个人死了,从来没有收到通知。是的@misshomme 是正确的,您必须将 [Key] 属性添加到不同的列结果。我不知道为什么,但 EF Core 必须拥有这个,除此之外它真的不重要。您也可能要小心,最好使用 'dotnet ef database update ...' 方法创建数据库。还要确保他们不会尝试从您的 DbSet 创建表的任何快照。这是一个结果集,而不是一个表格。更新了答案。