【问题标题】:IDENTITY_INSERT for multiple tables in .net core 2.net core 2 中多个表的 IDENTITY_INSERT
【发布时间】:2017-10-25 19:23:35
【问题描述】:

我正在尝试将数据从现有数据库迁移到新数据库。旧数据库非常复杂,这意味着大多数表都基于外部 id 与许多其他表有关系。我遇到了这个插入 id 的解决方案:

using (var context = new EmployeeContext())
{
    context.Employees.Add(new Employee { EmployeeId = 100, Name = "John Doe" });
    context.Employees.Add(new Employee { EmployeeId = 101, Name = "Jane Doe" });

    context.Database.OpenConnection();
    try
    {
        context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees ON");
        context.SaveChanges();
        context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees OFF");
    }
    finally
    {
        context.Database.CloseConnection();
    }


    foreach (var employee in context.Employees)
    {
        Console.WriteLine(employee.EmployeeId + ": " + employee.Name);
    }
}

来自此 Microsoft 指南:https://docs.microsoft.com/en-us/ef/core/saving/explicit-values-generated-properties

有没有办法在应用context.SaveChanges();之前在多个表上设置IDENTITY_INSERT

【问题讨论】:

    标签: c# sql-server entity-framework asp.net-core-2.0


    【解决方案1】:

    不。查看 IDENTITY_INSERT 的文档。 https://docs.microsoft.com/en-us/sql/t-sql/statements/set-identity-insert-transact-sql

    它明确指出:

    在任何时候,一个会话中只有一个表可以有 IDENTITY_INSERT 属性设置为 ON。如果表已经将此属性设置为 ON, 并为另一个表发出 SET IDENTITY_INSERT ON 语句, SQL Server 返回一条错误消息,指出 SET IDENTITY_INSERT 是 已经开启并报告它设置为开启的表。

    【讨论】:

      【解决方案2】:

      我在从存储在 json 文件中的对象树中播种数据时遇到了同样的问题。

      例子:

      jsonData = System.IO.File.ReadAllText(@"Data\InputParameters.json");
      var inputParameters = JsonConvert.DeserializeObject<List<ParameterCategory>> jsonData, settings);
      context.AddRange(inputParameters);
      context.SaveChanges();
      

      查看 EFCore 源代码后,我想出了以下解决方案:

      1.新建一个类“SqlServerUpdateSqlGeneratorInsertIdentity”,负责为每个插入操作开启和关闭Identity_Insert:

      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      using Microsoft.EntityFrameworkCore.SqlServer.Update.Internal;
      using Microsoft.EntityFrameworkCore.Storage;
      using Microsoft.EntityFrameworkCore.Update;
      
      /// <summary>
      /// SqlServerUpdateSqlGenerator with Insert_Identity.
      /// </summary>
      public class SqlServerUpdateSqlGeneratorInsertIdentity : SqlServerUpdateSqlGenerator
      {
          /// <summary>
          /// Initializes a new instance of the <see cref="SqlServerUpdateSqlGeneratorInsertIdentity"/> class.
          /// </summary>
          /// <param name="dependencies">The dependencies.</param>
          public SqlServerUpdateSqlGeneratorInsertIdentity(UpdateSqlGeneratorDependencies dependencies)
              : base(dependencies)
          {
          }
      
          /// <inheritdoc/>
          public override ResultSetMapping AppendBulkInsertOperation(
              StringBuilder commandStringBuilder,
              IReadOnlyList<ModificationCommand> modificationCommands,
              int commandPosition)
          {
              var columns = modificationCommands[0].ColumnModifications.Where(o => o.IsWrite).Select(o => o.ColumnName)
                  .ToList();
              var schema = modificationCommands[0].Schema;
              var table = modificationCommands[0].TableName;
      
              GenerateIdentityInsert(commandStringBuilder, table, schema, columns, on: true);
              var result = base.AppendBulkInsertOperation(commandStringBuilder, modificationCommands, commandPosition);
              GenerateIdentityInsert(commandStringBuilder, table, schema, columns, on: false);
      
              return result;
          }
      
          private void GenerateIdentityInsert(
              StringBuilder builder,
              string table,
              string schema,
              IEnumerable<string> columns,
              bool on)
          {
              var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));
      
              builder.Append("IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE").Append(" [name] IN (")
                  .Append(string.Join(", ", columns.Select(stringTypeMapping.GenerateSqlLiteral)))
                  .Append(") AND [object_id] = OBJECT_ID(").Append(
                      stringTypeMapping.GenerateSqlLiteral(
                          Dependencies.SqlGenerationHelper.DelimitIdentifier(table, schema))).AppendLine("))");
      
              builder.Append("SET IDENTITY_INSERT ")
                  .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table, schema)).Append(on ? " ON" : " OFF")
                  .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
          }
      }
      

      2.将原来的“SqlServerUpdateSqlGenerator”替换为继承的新的:

      在 Startup.cs - ConfigureServices 中使用以下代码:

      services.AddDbContext<YourDataContext>(options =>
      {
          options.UseSqlServer(YourConnectionString);
          options.ReplaceService<ISqlServerUpdateSqlGenerator, SqlServerUpdateSqlGeneratorInsertIdentity>();
      });
      

      在 YourDataContext.cs - OnConfiguring 使用这个(未测试):

      options.ReplaceService<ISqlServerUpdateSqlGenerator, SqlServerUpdateSqlGeneratorInsertIdentity>();
      

      在播种后可能需要将服务配置重置为原始配置。在我的情况下,它不是。

      希望对某人有所帮助...

      【讨论】:

      • 感谢您采用这种方法。另一个问题:播种后如何重置服务配置?谢谢!
      猜你喜欢
      • 1970-01-01
      • 2020-08-10
      • 2018-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多