[英]IDENTITY_INSERT for multiple tables in .net core 2
I'm trying to migrate data from an existing database to a new one. 我正在尝试将数据从现有数据库迁移到新数据库。 The old database is very entangled, meaning most tables have relationships to many other tables based on foreign ids. 旧数据库非常纠结,这意味着大多数表都基于外部ID与许多其他表有关系。 I came across this solution for inserting ids: 我遇到了插入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);
}
}
from this Microsoft guide: https://docs.microsoft.com/en-us/ef/core/saving/explicit-values-generated-properties 来自此Microsoft指南: https : //docs.microsoft.com/zh-cn/ef/core/saving/explicit-values-generation-properties
Is there a way to set IDENTITY_INSERT
on multiple tables before applying context.SaveChanges();
有没有一种方法可以在应用context.SaveChanges();
之前在多个表上设置IDENTITY_INSERT
context.SaveChanges();
? ?
Nope. 不。 Look at the documentation of IDENTITY_INSERT. 查看IDENTITY_INSERT的文档。 https://docs.microsoft.com/en-us/sql/t-sql/statements/set-identity-insert-transact-sql https://docs.microsoft.com/zh-cn/sql/t-sql/statements/set-identity-insert-transact-sql
It clearly states: 它明确指出:
At any time, only one table in a session can have the IDENTITY_INSERT property set to ON. 任何时候,会话中只有一个表可以将IDENTITY_INSERT属性设置为ON。 If a table already has this property set to ON, and a SET IDENTITY_INSERT ON statement is issued for another table, SQL Server returns an error message that states SET IDENTITY_INSERT is already ON and reports the table it is set ON for. 如果一个表的此属性已设置为ON,并且为另一个表发出了SET IDENTITY_INSERT ON语句,则SQL Server返回一条错误消息,指出SET IDENTITY_INSERT已经为ON,并报告其设置为ON的表。
I had the same Problem here while seeding data from an object tree stored in a json file. 从存储在json文件中的对象树中播种数据时,我在这里遇到了同样的问题。
Example: 例:
jsonData = System.IO.File.ReadAllText(@"Data\InputParameters.json");
var inputParameters = JsonConvert.DeserializeObject<List<ParameterCategory>> jsonData, settings);
context.AddRange(inputParameters);
context.SaveChanges();
After a look at the EFCore sources I came up with the following solution: 在查看了EFCore的源代码之后,我想出了以下解决方案:
1. Create a new class "SqlServerUpdateSqlGeneratorInsertIdentity" which is responsible for turning Identity_Insert on and off for each insert Operation: 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. Replace the original "SqlServerUpdateSqlGenerator" by the inherited new one: 2.用继承的新的替换原来的“ SqlServerUpdateSqlGenerator”:
In Startup.cs - ConfigureServices use the following code: 在Startup.cs-ConfigureServices中,使用以下代码:
services.AddDbContext<YourDataContext>(options =>
{
options.UseSqlServer(YourConnectionString);
options.ReplaceService<ISqlServerUpdateSqlGenerator, SqlServerUpdateSqlGeneratorInsertIdentity>();
});
Or in YourDataContext.cs - OnConfiguring use this one (not tested): 或在YourDataContext.cs-OnConfiguring中使用此选项(未经测试):
options.ReplaceService<ISqlServerUpdateSqlGenerator, SqlServerUpdateSqlGeneratorInsertIdentity>();
It may be necessary to reset the service configuration to it´s original after seeding. 播种后,可能有必要将服务配置重置为原始配置。 In my case it wasn´t. 就我而言,不是。
Hope that´ll help someone... 希望能帮助到某人...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.