程式碼第一次插入、更新和刪除預存程式

注意

僅限 EF6 及更新版本 - Entity Framework 6 已引進此頁面中所討論的功能及 API 等等。 如果您使用的是較早版本,則不適用部分或全部的資訊。

根據預設,Code First 會設定所有實體,以使用直接資料表存取來執行插入、更新和刪除命令。 從 EF6 開始,您可以設定 Code First 模型,針對模型中的某些或所有實體使用預存程式。

基底實體對應

您可以使用 Fluent API 選擇使用預存程式進行插入、更新和刪除。

modelBuilder
  .Entity<Blog>()
  .MapToStoredProcedures();

這樣做會導致 Code First 使用一些慣例,在資料庫中建置預存程式的預期形狀。

  • 三個名為 type_name_Insert、 < type_name > _Update 和 < type_name > _Delete預存程式(例如Blog_Insert、Blog_Update和Blog_Delete)。 ><
  • 參數名稱會對應至屬性名稱。

    注意

    如果您使用 HasColumnName() 或 Column 屬性來重新命名指定屬性的資料行,則這個名稱會用於參數,而不是屬性名稱。

  • 插入預存程式 會針對每個屬性都有參數,但標示為已產生之儲存區的屬性除外(識別或計算)。 預存程式應該傳回結果集,其中包含每個存放區所產生屬性的資料行。
  • 更新預存程式 會有每個屬性的參數,但標示為 「計算」之預存模式的預存程式除外。 某些並行權杖需要原始值的參數,請參閱下方的 並行權杖 一節以取得詳細資料。 預存程式應該傳回結果集,其中包含每個計算屬性的資料行。
  • 刪除預存程式 應該有實體索引鍵值的參數(如果實體有複合索引鍵,則為多個參數)。 此外,刪除程式也應該在目標資料表上具有任何獨立關聯外鍵的參數(實體中未宣告對應外鍵屬性的關聯性)。 某些並行權杖需要原始值的參數,請參閱下方的 並行權杖 一節以取得詳細資料。

使用下列類別作為範例:

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  public string Url { get; set; }  
}

預設預存程式為:

CREATE PROCEDURE [dbo].[Blog_Insert]  
  @Name nvarchar(max),  
  @Url nvarchar(max)  
AS  
BEGIN
  INSERT INTO [dbo].[Blogs] ([Name], [Url])
  VALUES (@Name, @Url)

  SELECT SCOPE_IDENTITY() AS BlogId
END
CREATE PROCEDURE [dbo].[Blog_Update]  
  @BlogId int,  
  @Name nvarchar(max),  
  @Url nvarchar(max)  
AS  
  UPDATE [dbo].[Blogs]
  SET [Name] = @Name, [Url] = @Url     
  WHERE BlogId = @BlogId;
CREATE PROCEDURE [dbo].[Blog_Delete]  
  @BlogId int  
AS  
  DELETE FROM [dbo].[Blogs]
  WHERE BlogId = @BlogId

覆寫預設值

您可以覆寫預設設定的部分或所有專案。

您可以變更一或多個預存程式的名稱。 此範例只會重新具名更新預存程式。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.HasName("modify_blog")));

本範例會重新命名這三個預存程式。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.HasName("modify_blog"))  
     .Delete(d => d.HasName("delete_blog"))  
     .Insert(i => i.HasName("insert_blog")));

在這些範例中,呼叫會鏈結在一起,但您也可以使用 Lambda 區塊語法。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    {  
      s.Update(u => u.HasName("modify_blog"));  
      s.Delete(d => d.HasName("delete_blog"));  
      s.Insert(i => i.HasName("insert_blog"));  
    });

本範例會重新具名更新預存程式上 BlogId 屬性的參數。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.Parameter(b => b.BlogId, "blog_id")));

這些呼叫全都是可鏈結且可組合的。 以下是重新命名這三個預存程式及其參數的範例。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.HasName("modify_blog")  
                   .Parameter(b => b.BlogId, "blog_id")  
                   .Parameter(b => b.Name, "blog_name")  
                   .Parameter(b => b.Url, "blog_url"))  
     .Delete(d => d.HasName("delete_blog")  
                   .Parameter(b => b.BlogId, "blog_id"))  
     .Insert(i => i.HasName("insert_blog")  
                   .Parameter(b => b.Name, "blog_name")  
                   .Parameter(b => b.Url, "blog_url")));

您也可以變更結果集中包含資料庫產生值的資料行名稱。

modelBuilder
  .Entity<Blog>()
  .MapToStoredProcedures(s =>
    s.Insert(i => i.Result(b => b.BlogId, "generated_blog_identity")));
CREATE PROCEDURE [dbo].[Blog_Insert]  
  @Name nvarchar(max),  
  @Url nvarchar(max)  
AS  
BEGIN
  INSERT INTO [dbo].[Blogs] ([Name], [Url])
  VALUES (@Name, @Url)

  SELECT SCOPE_IDENTITY() AS generated_blog_id
END

類別中沒有外鍵的關聯性(獨立關聯)

當類別定義中包含外鍵屬性時,可以使用與任何其他屬性相同的方式來重新命名對應的參數。 當類別中沒有外鍵屬性的關聯性存在時,預設參數名稱會 < navigation_property_name > _ < primary_key_name >

例如,下列類別定義會導致預存程式中預期Blog_BlogId參數,以插入和更新貼文。

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  public string Url { get; set; }

  public List<Post> Posts { get; set; }  
}  

public class Post  
{  
  public int PostId { get; set; }  
  public string Title { get; set; }  
  public string Content { get; set; }  

  public Blog Blog { get; set; }  
}

覆寫預設值

您可以將主鍵屬性的路徑提供給 Parameter 方法,以變更類別中未包含之外鍵的參數。

modelBuilder
  .Entity<Post>()  
  .MapToStoredProcedures(s =>  
    s.Insert(i => i.Parameter(p => p.Blog.BlogId, "blog_id")));

如果您沒有相依實體的導覽屬性(亦即沒有 Post.Blog 屬性),您可以使用 Association 方法來識別關聯性的另一端,然後設定對應至每個索引鍵屬性的參數。

modelBuilder
  .Entity<Post>()  
  .MapToStoredProcedures(s =>  
    s.Insert(i => i.Navigation<Blog>(  
      b => b.Posts,  
      c => c.Parameter(b => b.BlogId, "blog_id"))));

並行語彙基元

更新和刪除預存程式可能需要處理並行處理:

  • 如果實體包含並行權杖,預存程式可以選擇性地擁有輸出參數,以傳回更新/刪除的資料列數目(受影響的資料列)。 這類參數必須使用 RowsAffectedParameter 方法來設定。
    根據預設,EF 會使用 ExecuteNonQuery 的傳回值來判斷受影響的資料列數目。 如果您在 sproc 中執行任何邏輯,導致 ExecuteNonQuery 的傳回值在執行結束時不正確(從 EF 的觀點來看),指定受影響的輸出參數會很有用。
  • 針對每個並行權杖,將會有名為 < property_name > 的參數_Original (例如,Timestamp_Original )。 這會傳遞此屬性的原始值 – 從資料庫查詢時的值。
    • 資料庫計算的並行權杖 ,例如時間戳記 , 只會有原始值參數。
    • 設定為並行權杖的非計算屬性也會有更新程式中新值的參數。 這會使用已針對新值討論的命名慣例。 這類權杖的範例是使用部落格的 URL 做為並行權杖,因此需要新的值,因為這可以更新為程式碼的新值(不同于只有資料庫更新的時間戳記權杖)。

這是範例類別,並使用時間戳並行權杖更新預存程式。

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  public string Url { get; set; }  
  [Timestamp]
  public byte[] Timestamp { get; set; }
}
CREATE PROCEDURE [dbo].[Blog_Update]  
  @BlogId int,  
  @Name nvarchar(max),  
  @Url nvarchar(max),
  @Timestamp_Original rowversion  
AS  
  UPDATE [dbo].[Blogs]
  SET [Name] = @Name, [Url] = @Url     
  WHERE BlogId = @BlogId AND [Timestamp] = @Timestamp_Original

以下是範例類別,並使用非計算並行權杖更新預存程式。

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  [ConcurrencyCheck]
  public string Url { get; set; }  
}
CREATE PROCEDURE [dbo].[Blog_Update]  
  @BlogId int,  
  @Name nvarchar(max),  
  @Url nvarchar(max),
  @Url_Original nvarchar(max),
AS  
  UPDATE [dbo].[Blogs]
  SET [Name] = @Name, [Url] = @Url     
  WHERE BlogId = @BlogId AND [Url] = @Url_Original

覆寫預設值

您可以選擇性地引進受影響的資料列參數。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.RowsAffectedParameter("rows_affected")));

針對資料庫計算的並行權杖 – 只傳遞原始值的位置 – 您只能使用標準參數重新命名機制來重新命名原始值的參數。

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.Parameter(b => b.Timestamp, "blog_timestamp")));

針對非計算並行權杖 ,其中傳遞了原始和新的值,您可以使用 Parameter 的多載,讓您為每個參數提供名稱。

modelBuilder
 .Entity<Blog>()
 .MapToStoredProcedures(s => s.Update(u => u.Parameter(b => b.Url, "blog_url", "blog_original_url")));

多對多關聯性

我們將使用下列類別作為本節中的範例。

public class Post  
{  
  public int PostId { get; set; }  
  public string Title { get; set; }  
  public string Content { get; set; }  

  public List<Tag> Tags { get; set; }  
}  

public class Tag  
{  
  public int TagId { get; set; }  
  public string TagName { get; set; }  

  public List<Post> Posts { get; set; }  
}

許多關聯性可以對應至具有下列語法的預存程式。

modelBuilder  
  .Entity<Post>()  
  .HasMany(p => p.Tags)  
  .WithMany(t => t.Posts)  
  .MapToStoredProcedures();

如果未提供其他組態,則預設會使用下列預存程式圖形。

  • 兩個名為 < type_one ><> type_two_Insert 和 < type_one >< type_two > _Delete預存程式(例如PostTag_Insert和PostTag_Delete)。
  • 參數會是每個類型的索引鍵值。 type_name <> _ < property_name > 的每個參數名稱(例如,Post_PostId和Tag_TagId)。

以下是插入和更新預存程式的範例。

CREATE PROCEDURE [dbo].[PostTag_Insert]  
  @Post_PostId int,  
  @Tag_TagId int  
AS  
  INSERT INTO [dbo].[Post_Tags] (Post_PostId, Tag_TagId)   
  VALUES (@Post_PostId, @Tag_TagId)
CREATE PROCEDURE [dbo].[PostTag_Delete]  
  @Post_PostId int,  
  @Tag_TagId int  
AS  
  DELETE FROM [dbo].[Post_Tags]    
  WHERE Post_PostId = @Post_PostId AND Tag_TagId = @Tag_TagId

覆寫預設值

程式與參數名稱可以設定成與實體預存程式類似的方式。

modelBuilder  
  .Entity<Post>()  
  .HasMany(p => p.Tags)  
  .WithMany(t => t.Posts)  
  .MapToStoredProcedures(s =>  
    s.Insert(i => i.HasName("add_post_tag")  
                   .LeftKeyParameter(p => p.PostId, "post_id")  
                   .RightKeyParameter(t => t.TagId, "tag_id"))  
     .Delete(d => d.HasName("remove_post_tag")  
                   .LeftKeyParameter(p => p.PostId, "post_id")  
                   .RightKeyParameter(t => t.TagId, "tag_id")));