代码优先迁移和存储过程

问题描述 投票:25回答:4

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

sql-server asp.net-mvc entity-framework code-first
4个回答
26
投票

我这样做了......

在当前的迁移类中 -

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 ...
    ";
}

11
投票

我使用的是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; "); } }

1
投票
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服务器中查看/修改SP时,这就是它显示“ALTER PROCEDURE”的原因

enter image description here


0
投票

我将尝试提供不同的视角,因为在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脚本。

© www.soinside.com 2019 - 2024. All rights reserved.