Entity Framework 6 vs Entity Framework Core Raw Sql

entity-framework-6 entity-framework-core

Question

Entity Framework 6 example writing SQL queries for non-entity types:

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

In Entity Framework 6, I can also write the following query with SqlQuery. How can I run the following query with 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

How can I do with Entity Framework Core? Writing SQL queries for non-entity types?

1
0
2/26/2018 6:22:08 PM

Popular Answer

I have done the 'fromsql' off of the context directly when it is a single table, but I realize this is not what you want but it builds on it.

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

However in a case like yours it is complex and a joining of multiple tables and CTEs. I would suggest you create a custom object, POCO C# in code, and assign it a DbSet<> in your model builder. Then you can do something like this:

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

If your return matches the type it may work. I did something similar and just wrapped my whole method in a procedure. However EF Core you need to make a migration manually up and then add the creation of the proc manually in the 'Up' method of the migration if you wish to deploy it. If you went that route your proc would need to exist on the server already or deploy it like said above and do something similar to this:

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

The important thing to note is you need to create a DBSet object first in your model context so the context you are calling knows the well typed object it is returning from direct SQL. It must match EXACTLY the columns and types being returned.

EDIT 3-8 To be sure you need to do a few steps I will write out:

  1. A POCO class that has a Data Annotation of [Key] above a distinct property. This class matches your columns of what a procedure returns exactly.
  2. A DBSet<(POCO)> in your context.
  3. Create a new Migration with: "Dotnet ef Migrations add 'yourname'"
  4. Observe the new migration scripts. If anything generating a table for the POCO gets created, erase it. You don't need it. This is for a result set not storage in the database.
  5. Change the 'Up' section to manually script your SQL to the database something like below. Also ensure you drop the data if you ever want to revert in the 'Down' section

    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. So now you essentially hijacked the migration to do explicitly what you want. Test it out by deploying the changes to the database with "dotnet ef database update 'yourmigrationname'".
  7. Observe the database, it should have your proc if the database update succeeded and you did not accidentally create a table in your migration.
  8. The section you said you didn't understand is what gets the data in EF Core. Let's break it up:

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

context.pGetResult = is using the DbSet you made up. It keeps you well typed to your proc. .FromSQL( = telling the context you are going to do some SQL directly in the string. "pGetResult @p0, @p1, @p2" = I am naming a procedure in the database that has three params. , parameters: new[] { "Flight", null, null }) = I am just doing an array of objects that is in order of the parameters as needed. You need to match the SQL types of course but provided that is okay it will be fine. .ToListAsync() = I want a collection and my goto is always ToList when debugging something.

Hope that helps. Once I learned this would work it opened up a whole other world of what I could do. You can take a look at a project I have done that is unfinished for reference. I hard coded a controller to show the proc with preset values. But it could be changed easily to just inject them in the api. https://github.com/djangojazz/EFCoreTest/tree/master/EFCoreCodeFirstScaffolding

0
3/8/2018 5:52:33 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow