EntityFramework Core使用原生SQL語句,執行存儲過程和視圖


參考資料:
微軟MVP楊旭教程:https://www.bilibili.com/video/BV1xa4y1v7rR?p=10

在EF Core項目中,如果想在數據庫中添加視圖或者存儲過程或者類似的東西,不可以直接操作數據庫,而應該把生成視圖或生成存儲過程的腳本放在一個Migration里面,讓它來執行生成視圖或者創建存儲過程。

創建視圖和存儲過程

直接Add一個空的Migration,然后再修改Migration的代碼。空的Migration:

public partial class AddView : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {

    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {

    }
}

然后使用migrationBuilder.Sql(),把SQL腳本用@"xxxx"包裹起來當作參數放進去。創建視圖和存儲過程的腳本都應該放在Up方法中,先創建視圖再創建存儲過程:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.Sql(
        @"CREATE VIEW [dbo].[PlayerClubView]
            AS SELECT p.Id as PlayerId, p.Name as PlayerName, c.Name as ClubName
            FROM [dbo].[Players] as p
            INNER JOIN [dbo].[Clubs] as c
            ON p.ClubId = c.Id");
    migrationBuilder.Sql(
        @"CREATE PROCEDURE [dbo].[RemoveGamePlayersProcedure] @playerId int = 0
            AS 
                DELETE FROM [dbo].[GamePlayers] WHERE [PlayerId] = @playerId
            RETURN 0");
}

同時Down方法中應該包含回滾的操作,如果更新失敗就回滾,回滾時應該先刪除存儲過程再刪除視圖:

protected override void Down(MigrationBuilder migrationBuilder)
{
    migrationBuilder.Sql(
        @"DROP PROCEDURE [dbo].[RemoveGamePlayersProcedure]");
    migrationBuilder.Sql(
        @"DROP VIEW [dbo].[PlayerClubView]");
}

Update-Database之后,看一下數據庫:

UTOOLS1593166699745.png

可以看到視圖和存儲過程全都有了。

無主鍵的Entity

我們建立的實體Model基本都有主鍵,而且基本都是Id,但:

  • .NET Core 3.1 允許無主鍵的Entity
  • 它們不會被追蹤
  • 映射到沒有主鍵的Table或者View

我們根據剛才建立的視圖建立一個沒有主鍵的模型:

public class PlayerClub
{
    public int PlayerId { get; set; }
    public string PlayerName { get; set; }
    public string ClubName { get; set; }
}

然后把這個類添加到Context里的DbSet屬性。但DbSet不識別沒有主鍵的類,我們需要再OnModelCreating中設置一下,用HasNoKey()方法設置PlayerClub這個Entity。但只這樣設置還不足夠,如果后面再添加遷移,它會認為我們想要創建PlayerClubs這樣一個Table,所以要用ToView()方法把它映射到我們前面創建的視圖[dbo].[PlayerClubView]上。需要稍微修改一下視圖名,去掉中括號和dbo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    ...
    modelBuilder.Entity<PlayerClub>()
        .HasNoKey()
        .ToView("PlayerClubView");
}

針對這種沒有主鍵的Entity,查出來的結果都是無法追蹤的。

然后我們像查表一樣查一下視圖:

var playerClubs = context.PlayerClubs.ToList();

UTOOLS1593168338482.png

這種查詢也可以加Where查詢條件:

var playerClubs = context.PlayerClubs
    .Where(x => x.PlayerId > 1)
    .ToList();

但如果使用DbSet的Find()方法,在編譯的時候不會報錯,在運行的時候會報錯。因為Find后面跟的是主鍵,而這個視圖沒有主鍵。

原生SQL查詢

一共有兩種方法,還有它們各自的異步版本:

  • FromSQLRaw("SELECT" * ...")
  • FromSQLRawAsync("SELECT" * ...")
  • FromSQLInterpolated($"SELECT * WHERE x={var}")
  • FromSQLInterpolatedAsync($"SELECT * WHERE x={var}")

第二種方法支持C#6出現的字符串插值。這幾種方法都是DbSet的方法,所以只能針對DbSet來執行這個方法。最后還要調用ToList()等方法,否則查詢不會執行。

var leagues = context.Leagues
    .FromSqlRaw("SELECT * FROM dbo.Leagues")
    .ToList();

UTOOLS1593169259412.png

也可以加查詢條件和Include()等。過濾條件可以在SQL語句里面寫,在外面寫沒有太大意義:

var clubs = context.Clubs
    .FromSqlRaw("SELECT * FROM dbo.Clubs")
    .Include(x => x.League)
    .Include(x => x.Players)
        .ThenInclude(x => x.GamePlayers)
    .ToList();

原生SQL查詢的要求

  • 必須返回Entity類型的所有(標量)屬性

    也就是SELECT *,如果寫列名必須一個不多一個不少,而且不包含導航屬性

  • 字段名和Entity的屬性名匹配
  • 無法包含關聯的數據

    指在SQL語句中無法包含關聯的數據

  • 只能查詢已知的Entity

字符串插值

插值的部分在生成的SQL語句中也是SQL參數。

var id = 0;

var clubs = context.Clubs
    .FromSqlInterpolated($"SELECT * FROM dbo.Clubs WHERE Id > {id}")
    .ToList();

UTOOLS1593169799606.png

如果數據庫中有對應的Club類的存儲過程的話,Clubs.FromSqlInterpolated()Clubs.FromSqlRaw()也可以執行存儲過程。前提是要求存儲過程返回的字段必須與Club類匹配。

執行非查詢類SQL

執行非查詢類SQL,包括執行非查詢類的存儲過程,不能使用DbSet的方法,應當使用Context的Database屬性,它有下面兩種方法,各自還有一個異步方法。。

  • Context.Database.ExecuteSQLRaw()
  • Context.Database.ExecuteSQLRawAsync()
  • Context.Database.ExecuteSQLInterpolated()
  • Context.Database.ExecuteSQLInterpolatedAsync()
  • 無法用於查詢
  • 只能返回影響的行數
var count = context.Database
    .ExecuteSqlRaw("EXEC dbo.RemoveGamePlayersProcedure {0}", 2);

count = context.Database
    .ExecuteSqlInterpolated($"EXEC dbo.RemoveGamePlayersProcedure {2}");

可以看到無論那種方法,都使用了參數形式,因為這是非查詢類SQL語句,不使用參數的話很容易被SQL注入。

UTOOLS1593180643043.png


免責聲明!

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



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