Entity Framework 6提供支援儲存過程的新特性,本文具體演示Entity Framework 6 Code First的儲存過程操作。
Code First的插入/修改/刪除儲存過程
預設情況下下,Code First配置對全部實體的插入/修改/刪除操作均直接針對表進行。從EF6開始可以配置對全部或部分實體來選擇使用儲存過程。
1. 基本實體對映
1.1 通過Fluent API,配置使用插入/修改/刪除儲存過程
modelBuilder .Entity<Blog>() .MapToStoredProcedures();
1.2 Code First在資料庫中生成儲存過程的約定
◊ 生成三個儲存過程,名稱分別為<type_name>_Insert, <type_name>_Update, <type_name>_Delete;
◊ 引數名對應於屬性名 (注意:如果在 property上使用 HasColumnName() 或者 Column attribute 來重新命名,那麼引數也將使用這個重新命名過的名稱 );
◊ The insert stored procedure 為每一個屬性都有一個引數,除了那些標記為資料庫產生的(identity or computed),返回結果為那些標記為資料庫產生的屬性列;
◊ The update stored procedure 為每一個屬性都有一個引數,除了那些標記為資料庫產生且模式為 computed 的。一些併發標記的需要一個代表原始值的引數。返回值為那些 computed property 的列;
◊ The delete stored procedure 引數為實體主鍵(或者組合主鍵),此外也需要為每一個獨立關聯的外來鍵準備一個引數(指那些沒有在實體上定義相應外來鍵屬性的關係),一些併發標記的需要一個代表原始值的引數。
示例:
實體檔案blog.cs:
using System; using System.Collections.Generic; namespace EF6.Models { public partial class Blog { public int BlogID { get; set; } public string Name { get; set; } public string Url { get; set; } } }
實體對映檔案BlogMap.cs:
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; namespace EF6.Models.Mapping { public class BlogMap : EntityTypeConfiguration<Blog> { public BlogMap() { // Primary Key this.HasKey(t => t.BlogID); // Properties this.Property(t => t.Name) .HasMaxLength(50); this.Property(t => t.Url) .HasMaxLength(100); // Table & Column Mappings this.ToTable("Blog"); this.Property(t => t.BlogID).HasColumnName("BlogID"); this.Property(t => t.Name).HasColumnName("Name"); this.Property(t => t.Url).HasColumnName("Url"); // Procedures this.MapToStoredProcedures(); } } }
在程式包管理器控制檯中依次執行:
PM> Enable-Migrations -EnableAutomaticMigrations
PM> Add-Migration InitialCreate
PM> Update-Database -Verbose
執行完成之後生成資料庫:
監控生成資料的SQL語句:
CREATE TABLE [dbo].[Blog] ( [BlogID] [int] NOT NULL IDENTITY, [Name] [nvarchar](50), [Url] [nvarchar](100), CONSTRAINT [PK_dbo.Blog] PRIMARY KEY ([BlogID])
CREATE PROCEDURE [dbo].[Blog_Insert] @Name [nvarchar](50), @Url [nvarchar](100) AS BEGIN INSERT [dbo].[Blog]([Name], [Url]) VALUES (@Name, @Url) DECLARE @BlogID int SELECT @BlogID = [BlogID] FROM [dbo].[Blog] WHERE @@ROWCOUNT > 0 AND [BlogID] = scope_identity() SELECT t0.[BlogID] FROM [dbo].[Blog] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[BlogID] = @BlogID END
CREATE PROCEDURE [dbo].[Blog_Update] @BlogID [int], @Name [nvarchar](50), @Url [nvarchar](100) AS BEGIN UPDATE [dbo].[Blog] SET [Name] = @Name, [Url] = @Url WHERE ([BlogID] = @BlogID) END
CREATE PROCEDURE [dbo].[Blog_Delete] @BlogID [int] AS BEGIN DELETE [dbo].[Blog] WHERE ([BlogID] = @BlogID) END
2、重新預設約定
2.1、重新命名儲存過程名稱
modelBuilder .Entity<Blog>() .MapToStoredProcedures(s => s.Update(u => u.HasName("Modify_Blog")));
modelBuilder .Entity<Blog>() .MapToStoredProcedures(s => s.Update(u => u.HasName("Update_Blog")) .Delete(d => d.HasName("Delete_Blog")) .Insert(i => i.HasName("Insert_Bblog")));
modelBuilder .Entity<Blog>() .MapToStoredProcedures(s => { s.Update(u => u.HasName("Update_Blog")); s.Delete(d => d.HasName("Delete_Blog")); s.Insert(i => i.HasName("Insert_Blog")); });
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; namespace EF6.Models.Mapping { public class BlogMap : EntityTypeConfiguration<Blog> { public BlogMap() { // Primary Key this.HasKey(t => t.BlogID); // Properties this.Property(t => t.Name) .HasMaxLength(50); this.Property(t => t.Url) .HasMaxLength(100); // Table & Column Mappings this.ToTable("Blog"); this.Property(t => t.BlogID).HasColumnName("BlogID"); this.Property(t => t.Name).HasColumnName("Name"); this.Property(t => t.Url).HasColumnName("Url"); // Procedures this.MapToStoredProcedures(s =>{ s.Insert(u => u.HasName("Insert_Blog")); s.Update(u => u.HasName("Update_Blog")); s.Delete(u => u.HasName("Delete_Blog")); }); } } }
執行之後執行的SQL語句:
EXECUTE sp_rename @objname = N'dbo.Blog_Insert', @newname = N'Insert_Blog', @objtype = N'OBJECT' EXECUTE sp_rename @objname = N'dbo.Blog_Update', @newname = N'Update_Blog', @objtype = N'OBJECT' EXECUTE sp_rename @objname = N'dbo.Blog_Delete', @newname = N'Delete_Blog', @objtype = N'OBJECT'
2.2、重新命名儲存過程引數名稱
modelBuilder .Entity<Blog>() .MapToStoredProcedures(s => s.Update(u => u.Parameter(b => b.BlogID, "Blog_ID")));
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; namespace EF6.Models.Mapping { public class BlogMap : EntityTypeConfiguration<Blog> { public BlogMap() { // Primary Key this.HasKey(t => t.BlogID); // Properties this.Property(t => t.Name) .HasMaxLength(50); this.Property(t => t.Url) .HasMaxLength(100); // Table & Column Mappings this.ToTable("Blog"); this.Property(t => t.BlogID).HasColumnName("BlogID"); this.Property(t => t.Name).HasColumnName("Name"); this.Property(t => t.Url).HasColumnName("Url"); // Procedures this.MapToStoredProcedures(s => { s.Update(u => u.Parameter(b => b.BlogID, "Blog_ID")); }); } } }
執行之後執行的SQL語句:
ALTER PROCEDURE [dbo].[Blog_Update] @Blog_ID [int], @Name [nvarchar](50), @Url [nvarchar](100) AS BEGIN UPDATE [dbo].[Blog] SET [Name] = @Name, [Url] = @Url WHERE ([BlogID] = @Blog_ID) END
modelBuilder .Entity<Blog>() .MapToStoredProcedures(s => s.Update(u => u.HasName("Update_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")));
2.3、重新命名資料庫自動生成列的返回值的列名
modelBuilder .Entity<Blog>() .MapToStoredProcedures(s => s.Insert(i => i.Result(b => b.BlogID, "generated_blog_identity")));
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; namespace EF6.Models.Mapping { public class BlogMap : EntityTypeConfiguration<Blog> { public BlogMap() { // Primary Key this.HasKey(t => t.BlogID); // Properties this.Property(t => t.Name) .HasMaxLength(50); this.Property(t => t.Url) .HasMaxLength(100); // Table & Column Mappings this.ToTable("Blog"); this.Property(t => t.BlogID).HasColumnName("BlogID"); this.Property(t => t.Name).HasColumnName("Name"); this.Property(t => t.Url).HasColumnName("Url"); // Procedures this.MapToStoredProcedures(s => { s.Insert(i => i.Result(b => b.BlogID, "generated_blog_identity")); }); } } }
執行之後執行的SQL語句:
ALTER PROCEDURE [dbo].[Blog_Insert] @Name [nvarchar](50), @Url [nvarchar](100) AS BEGIN INSERT [dbo].[Blog]([Name], [Url]) VALUES (@Name, @Url) DECLARE @BlogID int SELECT @BlogID = [BlogID] FROM [dbo].[Blog] WHERE @@ROWCOUNT > 0 AND [BlogID] = scope_identity() SELECT t0.[BlogID] AS generated_blog_identity FROM [dbo].[Blog] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[BlogID] = @BlogID END
3、多對多關係
兩個多對多的實體類:
using System; using System.Collections.Generic; namespace EF6.Models { public partial class Post { public int PostID { get; set; } public string Title { get; set; } public string Content { get; set; } public virtual ICollection<Tag> Tags { get; set; } } }
using System; using System.Collections.Generic; namespace EF6.Models { public partial class Tag { public int TagID { get; set; } public string TagName { get; set; } public virtual ICollection<Post> Posts { get; set; } } }
對映儲存過程:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Post>() .HasMany(p => p.Tags) .WithMany(t => t.Posts) .MapToStoredProcedures(); }
預設生成的儲存過程:
◊ 生成兩個儲存過程,命名為 <type_one><type_two>_Insert 和 <type_one><type_two>_Delete
◊ 引數為每一型別的主鍵(或組合主鍵),命名為 <type_name>_<property_name>
程式碼執行後生成的儲存過程:
CREATE PROCEDURE [dbo].[PostTag_Insert] @Post_PostID [int], @Tag_TagID [int] AS BEGIN INSERT [dbo].[PostTags]([Post_PostID], [Tag_TagID]) VALUES (@Post_PostID, @Tag_TagID) END
CREATE PROCEDURE [dbo].[PostTag_Delete] @Post_PostID [int], @Tag_TagID [int] AS BEGIN DELETE [dbo].[PostTags] WHERE (([Post_PostID] = @Post_PostID) AND ([Tag_TagID] = @Tag_TagID)) END
參考資料: