我有一个类似的问题。在我的生产代码中,实体依赖于身份生成。但是对于集成测试,我需要手动设置一些 ID。在我不需要明确设置它们的地方,我在我的测试数据构建器中生成了它们。为了实现这一点DbContext
,我在生产代码中创建了一个继承,并为每个实体配置了身份生成,如下所示:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Entity1>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<Entity2>().Property(e => e.Id).ValueGeneratedNever();
...
}
但这还不够,我不得不禁用 SQL Server IDENTITY_INSERT
。这在单个表中插入数据时有效。但是,当您有相互关联的实体并且想要插入对象图时,这将失败DbContext.SaveChanges()
。原因是根据SQL Server 文档IDENTITY_INSERT ON
,您在会话期间一次只能拥有一个表。我的同事建议使用与此问题的其他答案DbCommandInterceptor
类似的a 。我让它只工作,但这个概念可以进一步扩展。目前它拦截并修改单个. 可以优化代码以使用Span.SliceINSERT INTO
INSERT INTO
DbCommand.CommandText
为了避免由于字符串操作而导致过多的内存,但是由于我找不到Split
方法,所以我没有为此投入时间。无论如何,我正在使用它DbCommandInterceptor
进行集成测试。如果您觉得有帮助,请随意使用它。
/// <summary>
/// When enabled intercepts each INSERT INTO statement and detects which table is being inserted into, if any.
/// Then adds the "SET IDENTITY_INSERT table ON;" (and same for OFF) statement before (and after) the actual insertion.
/// </summary>
public class IdentityInsertInterceptor : DbCommandInterceptor
{
public bool IsEnabled { get; set; }
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
if (IsEnabled)
{
ModifyAllStatements(command);
}
return base.ReaderExecuting(command, eventData, result);
}
private static void ModifyAllStatements(DbCommand command)
{
string[] statements = command.CommandText.Split(';', StringSplitOptions.RemoveEmptyEntries);
var commandTextBuilder = new StringBuilder(capacity: command.CommandText.Length * 2);
foreach (string statement in statements)
{
string modified = ModifyStatement(statement);
commandTextBuilder.Append(modified);
}
command.CommandText = commandTextBuilder.ToString();
}
private static string ModifyStatement(string statement)
{
const string insertIntoText = "INSERT INTO [";
int insertIntoIndex = statement.IndexOf(insertIntoText, StringComparison.InvariantCultureIgnoreCase);
if (insertIntoIndex < 0)
return $"{statement};";
int closingBracketIndex = statement.IndexOf("]", startIndex: insertIntoIndex, StringComparison.InvariantCultureIgnoreCase);
string tableName = statement.Substring(
startIndex: insertIntoIndex + insertIntoText.Length,
length: closingBracketIndex - insertIntoIndex - insertIntoText.Length);
// we should probably check whether the table is expected - list with allowed/disallowed tables
string modified = $"SET IDENTITY_INSERT [{tableName}] ON; {statement}; SET IDENTITY_INSERT [{tableName}] OFF;";
return modified;
}
}