EFCore執行Sql語句的方法:FromSql與ExecuteSqlCommand


前言

在EFCore中執行Sql語句的方法為:FromSql與ExecuteSqlCommand;在EF6中的為SqlQuery與ExecuteSqlCommand,而FromSql和SqlQuery有很大區別,FromSql返回值為IQueryable,因此為延遲加載的,可以與Linq擴展方法配合使用,但是有不少的坑(EFCore版本為1.1.0),直接執行Sql語句的建議不要使用FromSql,但是EFCore中並沒有提供SqlQuery方法,因此下面會貼出SqlQuery的實現代碼供大家參考,以便在EFCore中能使用。

 

FromSql和ExecuteSqlCommand的使用

測試時使用了SqlServer2008和SqlServer Profiler進行Sql語句捕捉,EFCore的版本為1.1.0。

測試的Entity Model與DbContext

  View Code

 

ExecuteSqlCommand

EFCore的ExecuteSqlCommand和EF6的一樣,執行非查詢的Sql語句:

復制代碼
1             var db = new MSSqlDBContext();
2             db.Database.ExecuteSqlCommand($"update {nameof(Person)} set name=@name where id=@id", new[] 
3             {
4                 new SqlParameter("name", "tom1"),
5                 new SqlParameter("id", 1),
6             });
復制代碼

 

FromSql

官方參考文檔:https://docs.microsoft.com/en-us/ef/core/querying/raw-sql

簡單使用

1             var db = new MSSqlDBContext();
2             var name = "tom";
3             var list = db.Set<Person>().FromSql($"select * from {nameof(Person)} where {nameof(name)}=@{nameof(name)} ", 
4                 new SqlParameter(nameof(name), name)).ToList();

生成的Sql:

exec sp_executesql N'select * from Person where name=@name 
',N'@name nvarchar(3)',@name=N'tom'

注意:

默認生成的為Person的Model,如果Select獲取的字段中不包含Person中的某字段就會拋異常了,例如:下面的語句只獲取name字段,並沒有包含Person的其他字段,那么拋異常:The required column 'id' was not present in the results of a 'FromSql' operation.

db.Set<Person>().FromSql($"select name from {nameof(Person)} ").ToList();

那么改為:

db.Set<Person>().Select(l => l.name).FromSql($"select name from {nameof(Person)} ").ToList();

 

執行存儲過程

1             var db = new MSSqlDBContext();
2             db.Set<Person>().FromSql("exec testproc @id", new SqlParameter("id", 1)).ToList();

生成的Sql:

exec sp_executesql N'exec testproc @id
',N'@id int',@id=1

 

與Linq擴展方法配合使用

1             var db = new MSSqlDBContext();
2             db.Set<Person>().FromSql($"select * from {nameof(Person)} where name=@name ", new SqlParameter("@name", "tom"))
3                 .Select(l => new { l.name, l.birthday }).ToList();

生成的Sql:

  View Code

inner join + order by

1             var db = new MSSqlDBContext();
2              (from p in db.Set<Person>().FromSql($"select * from {nameof(Person)} ")
3              join a in db.Set<Address>().Where(l => true)
4              on p.addrid equals a.id
5              select new { p.id, p.name, a.fullAddress }).OrderBy(l => l.id).ToList();

生成的Sql:

  View Code

left join + order by

復制代碼
1             var db = new MSSqlDBContext();
2              (from p in db.Set<Person>().FromSql($"select * from {nameof(Person)} ")
3              join a in db.Set<Address>().Where(l => true)
4              on p.addrid equals a.id into alist
5              from a in alist.DefaultIfEmpty()
6              select new { p.id, p.name, fullAddress = a == null ? null : a.fullAddress }).OrderBy(l => l.id).ToList();
復制代碼

生成的Sql:(生成的Sql很有問題,order by后面多了[p].[addrid],而且生成的select的字段也是多了)

復制代碼
SELECT [p].[id], [p].[addrid], [p].[birthday], [p].[name], [t].[id], [t].[fullAddress], [t].[lat], [t].[lon]
FROM (
    select * from Person 
) AS [p]
LEFT JOIN (
    SELECT [l0].[id], [l0].[fullAddress], [l0].[lat], [l0].[lon]
    FROM [Address] AS [l0]
) AS [t] ON [p].[addrid] = [t].[id]
ORDER BY [p].[id], [p].[addrid]
復制代碼

FromSql換成Where擴展方法試試:

1             (from p in db.Set<Person>().Where(l => true)
2              join a in db.Set<Address>().Where(l => true)
3              on p.addrid equals a.id into alist
4              from a in alist.DefaultIfEmpty()
5              select new { p.id, p.name, fullAddress = a == null ? null : a.fullAddress }).OrderBy(l => l.id).ToList();

EFCore生成的Sql(order by后面還是多了[addrid],select的字段也是多了):

復制代碼
SELECT [l].[id], [l].[addrid], [l].[birthday], [l].[name], [t].[id], [t].[fullAddress], [t].[lat], [t].[lon]
FROM [Person] AS [l]
LEFT JOIN (
    SELECT [l1].[id], [l1].[fullAddress], [l1].[lat], [l1].[lon]
    FROM [Address] AS [l1]
) AS [t] ON [l].[addrid] = [t].[id]
ORDER BY [l].[id], [l].[addrid]
復制代碼

而在EF6中生成的Sql,比EFCore的生成好多了:

復制代碼
SELECT 
    [Project1].[id] AS [id], 
    [Project1].[name] AS [name], 
    [Project1].[C1] AS [C1]
    FROM ( SELECT 
        [Extent1].[id] AS [id], 
        [Extent1].[name] AS [name], 
        CASE WHEN ([Extent2].[id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE [Extent2].[fullAddress] END AS [C1]
        FROM  [dbo].[Person] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Address] AS [Extent2] ON [Extent1].[addrid] = [Extent2].[id]
    )  AS [Project1]
    ORDER BY [Project1].[id] ASC
復制代碼

結果說明

FromSql不能代替原來EF6的SqlQuery使用,而且結合Linq擴展方法使用的時候生成的Sql會存在一些問題(EFCore版本為:1.1.0),那么為了能在EFCore中執行Sql查詢語句,下面提供對SqlQuery方法的實現。

SqlQuery的實現

  View Code

使用:

復制代碼
1             var db = new MSSqlDBContext();
2             string name = "tom";
3             var list = SqlQuery<PAModel>(db,
4                 $" select p.id, p.name, a.fullAddress, a.lat, a.lon " +
5                 $" from ( select * from {nameof(Person)} where {nameof(name)}=@{nameof(name)} ) as p " +
6                 $" left join {nameof(Address)} as a on p.addrid = a.id ",
7                 new[] { new SqlParameter(nameof(name), name) });
復制代碼

生成的Sql:

exec sp_executesql N' select p.id, p.name, a.fullAddress, a.lat, a.lon  from ( select * from Person where name=@name ) as p  left join Address as a on p.addrid = a.id ',N'@name nvarchar(3)',@name=N'tom'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM