程式碼第一次插入、更新和刪除預存程式
注意
僅限 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")));