如何使用Entity Framework Core运行迁移SQL脚本

shk*_*apo 8 c# ef-migrations entity-framework-core asp.net-core

我遇到了一个问题,我无法访问SQL脚本来应用迁移.这是我的迁移代码:

 public partial class AddSomethingMigration : Migration
{
    private const string MIGRATION_SQL_SCRIPT_FILE_NAME = @"Migrations\Scripts\20170710123314_AddSomethingMigration.sql";

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        string sql = Path.Combine(Directory.GetParent(Directory.GetCurrentDirectory()).FullName, MIGRATION_SQL_SCRIPT_FILE_NAME));
        migrationBuilder.Sql(File.ReadAllText(sql));
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,当我在本地计算机上使用程序包管理器控制台时,一切正常.但是当我部署到环境中时,我得到了与文件的差异.

我可以自动通过EF迁移运行我的静态SQL脚本,还是应该在代码中内嵌SQL查询?

shk*_*apo 14

我找到了这个问题的几个答案.

  1. 将脚本添加为项目资源并使用它:

        string sql = Resources._20170630085940_AddMigration;
        migrationBuilder.Sql(sql);
    
    Run Code Online (Sandbox Code Playgroud)

这个选项不太好,因为.sql会嵌入程序集中.

  1. 如果您使用.csproj结构的Net Core项目,则可以将itemgroup添加到xml:

    <ItemGroup> <Content Include="Migrations\**\*.sql" CopyToPublishDirectory="PreserveNewest" /><!-- CopyToPublishDirectory = { Always, PreserveNewest, Never } --></ItemGroup>
    
    Run Code Online (Sandbox Code Playgroud)

然后指定文件的路径,如:

Path.Combine(AppContext.BaseDirectory, relativePath)
Run Code Online (Sandbox Code Playgroud)


Yan*_*lla 11

我喜欢做的是将 SQL 脚本作为资源嵌入到程序集中,以便程序集不依赖于任何外部文件。我已经使用 Visual Studio Community 2019 16.4.2 测试了这种方法。就我而言,DbContext它保留在 .NET Standard 2.0 库中,而我的 Web 应用程序正在运行 .NET Core 2.2。

首先你需要创建一个迁移文件:

  1. 在 Visual Studio 中,确保将 Web 应用程序设置为启动项目。
  2. 在 Visual Studio 中打开 PMC:查看 -> 其他 Windows -> 包管理器控制台 (PMC)
  3. 在 PMC 中将默认项目设置为包含 DbContext 的项目(在我的情况下为 .NET 标准 2.2 库)
  4. 添加新的迁移:

    Add-Migration RunSqlScript

在迁移文件夹中添加一个Sql Script(为了方便起见,我将其命名为与迁移文件相同的前缀)

解决方案资源管理器中的迁移文件夹

在文件属性窗口中,确保构建操作是“嵌入式资源”。请注意,我们不需要复制到输出文件夹,因为 sql 脚本将嵌入到程序集中。

更新RunSqlScript迁移中的 Up 方法

var assembly = Assembly.GetExecutingAssembly();
string resourceName = typeof(RunSqlScript).Namespace + ".20191220105024_RunSqlScript.sql";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
  using (StreamReader reader = new StreamReader(stream))
  {
    string sqlResult = reader.ReadToEnd();
    migrationBuilder.Sql(sqlResult);
  }
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,我已将此代码重新分解为实用程序方法。为简洁起见,我没有进行这种重构。

更新:

我上面提到的重构代码:

var assembly = Assembly.GetExecutingAssembly();
string resourceName = typeof(RunSqlScript).Namespace + ".20191220105024_RunSqlScript.sql";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
  using (StreamReader reader = new StreamReader(stream))
  {
    string sqlResult = reader.ReadToEnd();
    migrationBuilder.Sql(sqlResult);
  }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

public static class MigrationUtility
{
  /// <summary>
  /// Read a SQL script that is embedded into a resource.
  /// </summary>
  /// <param name="migrationType">The migration type the SQL file script is attached to.</param>
  /// <param name="sqlFileName">The embedded SQL file name.</param>
  /// <returns>The content of the SQL file.</returns>
  public static string ReadSql(Type migrationType, string sqlFileName)
  {
    var assembly = migrationType.Assembly;
    string resourceName = $"{migrationType.Namespace}.{sqlFileName}";
    using (Stream stream = assembly.GetManifestResourceStream(resourceName))
    {
      if (stream == null)
      {
        throw new FileNotFoundException("Unable to find the SQL file from an embedded resource", resourceName);
      }

      using (var reader = new StreamReader(stream))
      {
        string content = reader.ReadToEnd();
        return content;
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


cur*_*Boy 9

您可以首先在同一项目中创建一个辅助方法:

public static class SqlFileTrigger
{
    public static string GetRawSql(string sqlFileName)
    {
        var baseDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Migrations");
        var path = Path.Combine(baseDirectory, sqlFileName);
        return File.ReadAllText(path);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后添加您的迁移。假设您添加为: add-migration AddDefaultUser 然后生成如下:

在此输入图像描述

现在添加 2 个具有相同名称且后缀为 _Up 和 _Down 的 sql 文件(包括要运行的 SQL 语句,例如插入记录等)。所以它会是这样的:

在此输入图像描述

然后在迁移文件的 UP 和 DOWN 方法中,使用 MigrationBuilder 对象调用它们。所以你的迁移文件将如下所示:

public partial class AddDefaultUser : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    { 
        migrationBuilder.Sql(SqlFileTrigger.GetRawSql("20220918043843_AddDefaultUser_Up.sql")); 
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {  
        migrationBuilder.Sql(SqlFileTrigger.GetRawSql("20220918043843_AddDefaultUser_Down.sql"));
    }
}
Run Code Online (Sandbox Code Playgroud)

PS:请确保设置 SQL 文件属性以COPY ALWAYS确保它们已部署。

在此输入图像描述

希望能帮助到你..