zoukankan      html  css  js  c++  java
  • Entity Framework Code First (六)存储过程

      声明:本文只针对 EF6+

      默认情况下,Code First 对实体进行插入、更新、删除操作是直接在表上进行的,从 EF6 开始你可以选择使用存储过程(Stored Procedures

    简单实体映射 Basic Entity Mapping

      注意:本文将使用 Fluent API 来配置使用存储过程

    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 将利用某些约定在数据库中生成一些存储过程:

    • 生成三个存储过程,名称分别为<type_name>_Insert<type_name>_Update<type_name>_Delete (本例为 Blog_Insert, Blog_Update, Blog_Delete);
    • 参数名对应于属性名 (注意:如果在 property上使用 HasColumnName() 或者 Column attribute 来重命名,那么参数也将使用这个重命名过的名称 );
    • The insert stored procedure 为每一个属性都有一个参数,除了那些标记为数据库产生的(identity or computed),返回结果为那些标记为数据库产生的属性列;
    • The update stored procedure 为每一个属性都有一个参数,除了那些标记为数据库产生且模式为 computed 的。一些并发标记的需要一个代表原始值的参数(更多信息请参考 Concurrency Tokens section)。返回值为那些 computed property 的列;
    • The delete stored procedure 参数为实体主键(或者组合主键),此外也需要为每一个独立关联的外键准备一个参数(指那些没有在实体上定义相应外键属性的关系),一些并发标记的需要一个代表原始值的参数(更多信息请参考 Concurrency Tokens section

      存储过程的具体内容如下

    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)))
    END
    View Code

    Overriding the Defaults

      你可以重写部分或全部的默认配置

    重写存储过程名

      重写 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

      如上类定义将会导致在 InsertUpdate 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)
    END
    View Code

    Overriding the Defaults  

      通过提供主键属性给 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

      UpdateDelete 存储过程也需要处理并发问题:

    • 如果实体包含并发标记,存储过程可选择地有一个 Output 参数用于返回更新/删除的列的数目,这样一个参数必须通过方法 RowsAffectedParameter 配置(注意:EF 默认使用 ExecuteNonQuery 的返回值来确定有多少行受影响,如果你在存储过程中执行逻辑操作将导致 ExecuteNonQuery 的返回值是错误的,此时指定一个行影响的 Output 参数是有必要的);
    • 每一个并发标记,都有一个参数,命名为 <property_name>_Original (如 Timestamp_Original),这个参数将传递属性的原始值 - 从数据库查询的值
      • 数据库计算(computed)的并发标记 - 例如 timestamp - 将有一个原始值参数;
      • 非计算属性的并发标记在 Update 存储过程中也有一个更新值参数,只是使用之前讨论过的为新值的命名约定。此处的一个例子为用 BlogURL 作为并发标记,更新后的新值是必须的因为这个值可能被你的代码更新成另一个新值(不像 Timestamp 标记只能被数据库更新

      

      一个计算并发标记(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

    Overriding the Defaults

      使用 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();

      默认生成的存储过程如下:

    • 生成两个存储过程,命名为 <type_one><type_two>_Insert 和 <type_one><type_two>_Delete (本例中为 PostTag_Insert PostTag_Delete);
    • 参数为每一类型的主键(或组合主键),命名为 <type_name>_<property_name> (本例为 Post_PostIdTag_TagId
    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

    Overriding the Defaults

      可以像配置实体存储过程一样来配置此存储过程和参数的名称

    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

      摘抄:http://www.cnblogs.com/panchunting/p/entity-framework-code-first-stored-procedures.html
     
     
  • 相关阅读:
    基于发布/订阅模型的应用程序的主循环设计
    C++使用继承时子对象的内存布局
    安装 CentOS 后的系统配置及软件安装备忘
    环形无锁队列
    并发编程基础
    线程池实现
    Git远程操作
    Unix权限管理
    jquery中,某些写法后来更新导致版本不支持的替代方法
    js相关
  • 原文地址:https://www.cnblogs.com/caoyc/p/5820888.html
Copyright © 2011-2022 走看看