声明:本文只针对 EF6+
默认情况下,Code First 对实体进行插入、更新、caozuo.html" target="_blank">删除操作是直接在表上进行的,从 EF6 开始你可以选择使用存储过程(Stored Procedures)
简单实体映射 Basic Entity Mapping
注意:本文将使用 Fluent API 来配置使用存储过程
class="code_img_closed" src="/Upload/Images/2013122318/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('22705d36-e8e1-4950-882e-735a0dc8c4c7',event)" src="/Upload/Images/2013122318/2B1B950FA3DF188F.gif" alt="" />public class Blog { public int BlogId { get; set; } public string Name { get; set; } public string Url { get; set; } [Timestamp] public byte[] Timestamp { get; set; } public virtual ICollection<Post> Posts { get; set; } }View Code
modelBuilder.Entity<Blog>()
.MapToStoredProcedures();
上面的代码执行后,Code First 将利用某些约定在数据库中生成一些存储过程:
存储过程的具体内容如下
CREATE PROCEDURE [dbo].[Blog_Insert] @Name [nvarchar](max), @Url [nvarchar](max) 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], t0.[Timestamp] FROM [dbo].[Blog] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[BlogId] = @BlogId END CREATE PROCEDURE [dbo].[Blog_Update] @BlogId [int], @Name [nvarchar](max), @Url [nvarchar](max), @Timestamp_Original [rowversion] AS BEGIN UPDATE [dbo].[Blog] SET [Name] = @Name, [Url] = @Url WHERE (([BlogId] = @BlogId) AND (([Timestamp] = @Timestamp_Original) OR ([Timestamp] IS NULL AND @Timestamp_Original IS NULL))) SELECT t0.[Timestamp] FROM [dbo].[Blog] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[BlogId] = @BlogId END CREATE PROCEDURE [dbo].[Blog_Delete] @BlogId [int], @Timestamp_Original [rowversion] AS BEGIN DELETE [dbo].[Blog] WHERE (([BlogId] = @BlogId) AND (([Timestamp] = @Timestamp_Original) OR ([Timestamp] IS NULL AND @Timestamp_Original IS NULL))) ENDView Code
你可以重写部分或全部的默认配置
重写 update 存储过程名
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 block syntax
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")); });
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 [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, t0.[Timestamp] FROM [dbo].[Blog] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[BlogId] = @BlogId END
无外键关系 Relationships Without a Foreign Key in the Class
如果实体上有定义外键属性,那么其重命名操作与其它属性无异。如果实体间的关系存在,但是并没定义外键属性,那么默认的参数名为 <navigation_property_name>_<primary_key_name>
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; } }View Code
如上类定义将会导致在 Insert 和 Update Post 存储过程中产生参数 Blog_BlogId
CREATE PROCEDURE [dbo].[Post_Insert] @Title [nvarchar](max), @Content [nvarchar](max), @Blog_BlogId [int] AS BEGIN INSERT [dbo].[Post]([Title], [Content], [Blog_BlogId]) VALUES (@Title, @Content, @Blog_BlogId) DECLARE @PostId int SELECT @PostId = [PostId] FROM [dbo].[Post] WHERE @@ROWCOUNT > 0 AND [PostId] = scope_identity() SELECT t0.[PostId] FROM [dbo].[Post] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[PostId] = @PostId END CREATE PROCEDURE [dbo].[Post_Update] @PostId [int], @Title [nvarchar](max), @Content [nvarchar](max), @Blog_BlogId [int] AS BEGIN UPDATE [dbo].[Post] SET [Title] = @Title, [Content] = @Content, [Blog_BlogId] = @Blog_BlogId WHERE ([PostId] = @PostId) ENDView Code
通过提供主键属性给 Parameter 方法,你可以重命名在类中没有包含的外键参数名
modelBuilder.Entity<Post>() .MapToStoredProcedures(s => s.Insert(i => i.Parameter(p => p.Blog.BlogId, "blog_id")));
生成的 Insert 存储过程如下
CREATE PROCEDURE [dbo].[Post_Insert] @Title [nvarchar](max), @Content [nvarchar](max), @blog_id [int] AS BEGIN INSERT [dbo].[Post]([Title], [Content], [Blog_BlogId]) VALUES (@Title, @Content, @blog_id) DECLARE @PostId int SELECT @PostId = [PostId] FROM [dbo].[Post] WHERE @@ROWCOUNT > 0 AND [PostId] = scope_identity() SELECT t0.[PostId] FROM [dbo].[Post] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[PostId] = @PostId END
如果在从属实体(dependent entity)上没有导航属性(navigation property)(例如 Post.Blog),你可以使用 Navigation(原文是 Association 方法,但笔者发现根本没有此方法) 方法来确定另一端的关系然后为相应的主键(或组合主键)配置参数
public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } }
modelBuilder.Entity<Post>() .MapToStoredProcedures(s => s.Insert(i => i.Navigation<Blog>( b => b.Posts, c => c.Parameter(b => b.BlogId, "blog_id"))));
产生的存储过程如下
CREATE PROCEDURE [dbo].[Post_Insert] @Title [nvarchar](max), @Content [nvarchar](max), @blog_id [int] AS BEGIN INSERT [dbo].[Post]([Title], [Content], [Blog_BlogId]) VALUES (@Title, @Content, @blog_id) DECLARE @PostId int SELECT @PostId = [PostId] FROM [dbo].[Post] WHERE @@ROWCOUNT > 0 AND [PostId] = scope_identity() SELECT t0.[PostId] FROM [dbo].[Post] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[PostId] = @PostId END
并发标记 Concurrency Tokens
Update 和 Delete 存储过程也需要处理并发问题:
一个计算并发标记(timestamp)标记的例子
public class Blog { public int BlogId { get; set; } public string Name { get; set; } public string Url { get; set; } [Timestamp] public byte[] Timestamp { get; set; } }
modelBuilder.Entity<Blog>()
.MapToStoredProcedures();
CREATE PROCEDURE [dbo].[Blog_Update] @BlogId [int], @Name [nvarchar](max), @Url [nvarchar](max), @Timestamp_Original [rowversion] AS BEGIN UPDATE [dbo].[Blog] SET [Name] = @Name, [Url] = @Url WHERE (([BlogId] = @BlogId) AND (([Timestamp] = @Timestamp_Original) OR ([Timestamp] IS NULL AND @Timestamp_Original IS NULL))) SELECT t0.[Timestamp] FROM [dbo].[Blog] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[BlogId] = @BlogId END
一个非计算并发标记(URL)例子
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 BEGIN UPDATE [dbo].[Blog] SET [Name] = @Name, [Url] = @Url WHERE (([BlogId] = @BlogId) AND (([Url] = @Url_Original) OR ([Url] IS NULL AND @Url_Original IS NULL))) END
使用 RowsAffectedParameter 方法
modelBuilder.Entity<Blog>() .MapToStoredProcedures(s => s.Update(u => u.RowsAffectedParameter("rows_affected")));
CREATE PROCEDURE [dbo].[Blog_Update] @BlogId [int], @Name [nvarchar](max), @Url [nvarchar](max), @Url_Original [nvarchar](max), @rows_affected [int] OUT AS BEGIN UPDATE [dbo].[Blog] SET [Name] = @Name, [Url] = @Url WHERE (([BlogId] = @BlogId) AND (([Url] = @Url_Original) OR ([Url] IS NULL AND @Url_Original IS NULL))) SET @rows_affected = @@ROWCOUNT END
对于计算并发标记 - 只有原始值需要传递 - 我们可以使用标准的 Pameter 方法来重命名参数名
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")));
N:N 关系 Many to Many Relationships
定义如下类
public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public virtual ICollection<Tag> Tags { get; set; } } public class Tag { public int TagId { get; set; } public string TagName { get; set; } public virtual ICollection<Post> Posts { get; set; } }
映射到存储过程
modelBuilder.Entity<Post>() .HasMany(p => p.Tags) .WithMany(t => t.Posts) .MapToStoredProcedures();
默认生成的存储过程如下:
CREATE PROCEDURE [dbo].[PostTag_Insert] @Post_PostId [int], @Tag_TagId [int] AS BEGIN INSERT [dbo].[PostTag]([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].[PostTag] WHERE (([Post_PostId] = @Post_PostId) AND ([Tag_TagId] = @Tag_TagId)) END
可以像配置实体存储过程一样来配置此存储过程和参数的名称
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")));
产生的存储过程如下
CREATE PROCEDURE [dbo].[add_post_tag] @post_id [int], @tag_id [int] AS BEGIN INSERT [dbo].[PostTag]([Post_PostId], [Tag_TagId]) VALUES (@post_id, @tag_id) END CREATE PROCEDURE [dbo].[remove_post_tag] @post_id [int], @tag_id [int] AS BEGIN DELETE [dbo].[PostTag] WHERE (([Post_PostId] = @post_id) AND ([Tag_TagId] = @tag_id)) END
原文链接:http://msdn.microsoft.com/en-us/data/dn468673