【问题标题】:Code First Migrations and Stored Procedures代码优先迁移和存储过程
【发布时间】:2012-12-17 20:19:37
【问题描述】:

我刚刚创建了一个数据库并完成了我的第一次迁移(只是一个简单的表添加)。现在我想添加一些我刚刚添加的存储过程,方法是编写 sql 并在 Management Studio 中执行它。但如果可能的话,我想在迁移中包含这些存储过程,以便保存它们,我可以对它们运行 Up 或 Down 方法。这可能吗?如果可以,需要使用什么语法?还是我只需要使用 Management Studio 添加/编辑/删除它们?

【问题讨论】:

标签: sql-server asp.net-mvc entity-framework code-first


【解决方案1】:

我将尝试提供不同的视角,因为在 C# 字符串中包含 SQL 代码并不是很吸引人,人们应该期望在提供智能感知的工具(例如 SSMS)中更改此类脚本。

以下解决方案在 ASP.NET Core 2.0 Web API 项目中实现。

  1. 使用任何方便的工具维护开发数据库中的过程

  2. 生成程序脚本:

    public class ProcedureItemMetadata
    {
        /// <summary>
        /// SQL server side object identifier
        /// </summary>
        [Key]
        public int ObjectId { get; set; }
    
        /// <summary>
        /// schema name
        /// </summary>
        public string SchemaName { get; set; }
    
        /// <summary>
        /// procedure name
        /// </summary>
        public string Name { get; set; }
    
        /// <summary>
        /// procedure body
        /// </summary>
        public string Definition { get; set; }
    }
    
    
    public string GetProceduresScript()
    {
       var query = Context.ProcedureItemMetadata.AsNoTracking().FromSql(@"
          SELECT ao.object_id as ObjectId, SCHEMA_NAME(ao.schema_id) as SchemaName, ao.name, sm.definition
          FROM sys.all_objects ao 
          JOIN sys.sql_modules sm ON sm.object_id = ao.object_id
          WHERE ao.type = 'P'
             and execute_as_principal_id IS NULL
          order by 1;");
    
          var list = query.ToList();
          string text = string.Join($" {Base.Constants.General.ScriptGeneratorSeparator}\n", list.Select(p => p.Definition));
    
          // replace create with create or alter
          string replaced = Regex.Replace(text,
             @"(?<create>CREATE\s+PROCEDURE\s+)",
             "CREATE OR ALTER PROCEDURE ", 
             RegexOptions.IgnoreCase);
    
          return replaced;
    }
    

这是一个手动过程,但允许在开发准备就绪时获取程序。此外,它还可以轻松扩展到其他类型的对象(例如视图)。

  1. 在解决方案中创建一个文件夹以保存要在应用程序启动时运行的脚本(例如 _SQL)

  2. 在文件夹中复制生成的脚本(例如 all_procedures.sql)

像这样存储脚本的一个优点是 IDE 可能会自动验证语法 + 突出显示的内容等。

  1. 创建“种子”代码以在应用程序启动时自动运行

    private static void EnsureSqlObjects(CustomContext context)
    {
        string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "_Sql");
        foreach (var file in Directory.GetFiles(path, "*.sql"))
        {
            string fileText = File.ReadAllText(file);
            // escaping { } for those rare cases when sql code contains {..}
            // as ExecuteSqlCommand tries to replace them with params values
            fileText = fileText.Replace("{", "{{");
            fileText = fileText.Replace("}", "}}");
    
            // splitting objects (cannot run more than one DDL in a command)
            string[] ddlParts = fileText.Split(Base.Constants.General.ScriptGeneratorSeparator, StringSplitOptions.RemoveEmptyEntries);
            foreach (string ddl in ddlParts)
            {
                context.Database.ExecuteSqlCommand(ddl);
            }
        }
    }
    

这种方法允许管理任何不容易通过迁移维护的idempotent 脚本。

【讨论】:

    【解决方案2】:
    namespace QuickProject.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;
    
    
    public partial class CreateStoredProcedure_GellAllAgents : DbMigration
    {
        public override void Up()
        {
            CreateStoredProcedure("dbo.GellAllAgents", c => new
            {
                DisplayLength = c.Int(10),
                DisplayStart = c.Int(0),
                UserName = c.String(maxLength: 255, defaultValueSql: "NULL"),
                FullName = c.String(maxLength: 255, defaultValueSql: "NULL"),
                PhoneNumber = c.String(maxLength: 255, defaultValueSql: "NULL"),
                LocationDescription = c.String(maxLength: 255, defaultValueSql: "NULL"),
                AgentStatusId = c.Int(defaultValueSql: "NULL"),
                AgentTypeId = c.Int(defaultValueSql: "NULL")
            }, StoredProcedureBody);
        }
    
        public override void Down()
        {
            DropStoredProcedure("dbo.GellAllAgents");
        }
    
    
        private const string StoredProcedureBody = @"
    Declare @FirstRec int, @LastRec int
    Set @FirstRec = @DisplayStart;
    Set @LastRec = @DisplayStart + @DisplayLength;
    
    With CTE_AspNetUsers as
    (
         Select ROW_NUMBER() over (order by AspNetUsers.Id) as RowNum,
             COUNT(*) over() as TotalCount, AspNetUsers.Id, AspNetUsers.FullName, AspNetUsers.UserName, AspNetUsers.PhoneNumber, Locations.Desciption as LocationDescription, Cities.Name as LocationCity, AgentStatus.Name as AgentStatusName, AgentTypes.Name as AgentTypeName
             from AspNetUsers
         join Locations on AspNetUsers.LocationId = Locations.id
         join Cities on Locations.CityId = Cities.Id
         join AgentStatus on AspNetUsers.AgentStatusId = AgentStatus.Id
         join AgentTypes on AspNetUsers.AgentTypeId = AgentTypes.Id
         where (Discriminator = 'Agent' 
             and (@UserName is null or UserName like '%' + @UserName + '%')
             and (@FullName is null or FullName like '%' + @FullName + '%')
             and (@PhoneNumber is null or PhoneNumber like '%' + @PhoneNumber + '%')
             and (@LocationDescription is null or  @LocationDescription like '%' + (select Cities.Name from Cities where Locations.CityId = Cities.Id) + '%' or  @LocationDescription like '%' + Desciption + '%')
             and (@AgentStatusId is null or AgentStatusId = @AgentStatusId)
             and (@AgentTypeId is null or AgentTypeId = @AgentTypeId)
         )
         group by AspNetUsers.Id, AspNetUsers.FullName,AspNetUsers.UserName, AspNetUsers.PhoneNumber, Locations.Desciption, Cities.Name, AgentStatus.Name, AgentTypes.Name
    )
    Select *
    from CTE_AspNetUsers
    where RowNum > @FirstRec and RowNum <= @LastRec
    ";
    }
    }
    

    结果,当您在 SQL Server 中查看/修改 SP 时,它会显示“ALTER PROCEDURE”

    【讨论】:

      【解决方案3】:

      我正在使用 EF6,DbMigration 类提供了创建/更改/删除存储过程的方法

      • 创建一个新的存储过程

        public partial class MyFirstMigration : DbMigration
        {
            public override void Up()
            {
                // Create a new store procedure
                CreateStoredProcedure("dbo.DequeueMessages"
                // These are stored procedure parameters
                , c => new{                
                    MessageCount = c.Int()
                },
                // Here is the stored procedure body
                @"
                SET NOCOUNT ON;
                SELECT TOP (@MessageCount)
                    *
                FROM
                    dbo.MyTable;
                ");
            }
        
            public override void Down()
            {
                // Delete the stored procedure
                DropStoredProcedure("dbo.DequeueMessages");                
            }
        }
        
      • 修改存储过程

        public partial class MySecondMigration : DbMigration
        {
            public override void Up()
            {
                // Modify an existing stored procedure
                AlterStoredProcedure("dbo.DequeueMessages"
                // These are new stored procedure parameters
                , c => new{                
                    MessageCount = c.Int(),
                    StatusId = c.Int()
                },
                // Here is the new stored procedure body
                @"
                SET NOCOUNT ON;
                SELECT TOP (@MessageCount)
                    *
                FROM
                    dbo.MyTable
                WHERE
                    StatusId = @StatusId;
                ");
            }
        
            public override void Down()
            {
                // Rollback to the previous stored procedure
                // Modify an existing stored procedure
                AlterStoredProcedure("dbo.DequeueMessages"
                // These are old stored procedure parameters
                , c => new{                
                    MessageCount = c.Int()
                },
                // Here is the old stored procedure body
                @"
                SET NOCOUNT ON;
                SELECT TOP (@MessageCount)
                    *
                FROM
                    dbo.MyTable;
                ");              
            }
        }
        

      【讨论】:

        【解决方案4】:

        我就是这么干的……

        在当前迁移类中-

        public partial class MyMigration : DbMigration
        {
            public override void Up()
            {
                ... other table creation logic
        
                // This command executes the SQL you have written
                // to create the stored procedures
                Sql(InstallScript);
        
                // or, to alter stored procedures
                Sql(AlterScript);
            }
        
            public override void Down()
            {
                ... other table removal logic
        
                // This command executes the SQL you have written
                // to drop the stored procedures
                Sql(UninstallScript);
        
                // or, to rollback stored procedures
                Sql(RollbackScript);
            }
        
            private const string InstallScript = @"
                CREATE PROCEDURE [dbo].[MyProcedure]
                ... SP logic here ...
            ";
        
            private const string UninstallScript = @"
                DROP PROCEDURE [dbo].[MyProcedure];
            ";
        
            // or for alters
            private const string AlterScript = @"
                ALTER PROCEDURE [dbo].[AnotherProcedure]
                ... Newer SP logic here ...
            ";
        
            private const string RollbackScript = @"
                ALTER PROCEDURE [dbo].[AnotherProcedure]
                ... Previous / Old SP logic here ...
            ";
        }
        

        【讨论】:

        • 如果您正在更改该过程,因为它是在以前的迁移中创建的,然后您需要关闭,那该怎么办?您不会只是 DROP 程序,它必须回到原来的状态,即程序之前的样子......
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-03-18
        • 2016-05-26
        • 1970-01-01
        • 1970-01-01
        • 2018-01-28
        • 2014-12-20
        • 1970-01-01
        相关资源
        最近更新 更多