有没有办法让 Entity Framework Core 部分保存数据?

Posted

技术标签:

【中文标题】有没有办法让 Entity Framework Core 部分保存数据?【英文标题】:Is there a way to have Entity Framework Core partially save data? 【发布时间】:2021-07-06 09:20:31 【问题描述】:

我正在使用 Entity Framework Core 进行一些批量插入。为了最大程度地减少到数据库的往返次数,新插入以 100 个为一组进行批处理,然后再添加到数据库上下文中并使用 SaveChanges() 保存。

当前的问题是,如果任何批处理由于例如表上的唯一键违规而无法插入,则整个事务将回滚。在这种情况下,理想的做法是简单地丢弃无法插入的记录,然后插入其余记录。

我很可能需要为此编写一个存储过程,但是有没有办法让 Entity Framework Core 跳过无法插入的行?

【问题讨论】:

这里有两个您可能想阅读的帖子。我不建议任何人作为解决方案。只是它们与您的问题有关:sqlteam.com/articles/introduction-to-transactions 和 ***.com/questions/22486489/… @jdweng 谢谢,但问题是我不希望事务在失败时回滚,理想情况下我希望它提交它能够提交的任何行并丢弃其余行。跨度> 您只想回滚一行,而不是所有行。 您的插入必须包含 where not exists 检查,我不知道您的 EF 是否可以这样做,如果不是,最好的方法是您将一批数据传递给的存储过程,或者使用序列化的 json 或数据表转换成自定义数据类型。 【参考方案1】:

在您的存储过程中,使用 MERGE 语句而不是 INSERT,然后仅使用 WHEN NOT MATCHED

MERGE  @tvp  incoming
INTO targetTable existing WITH (HOLDLOCK)
   ON (incoming.PK = existing.PK)
WHEN NOT MATCHED
    INSERT

匹配的记录将被丢弃。 @tvp 是从您的应用代码中提供给存储过程的表值参数。

在使用 MERGE 语句时有一些锁定注意事项可能适用于您的方案,也可能不适用。值得一读并发性和原子性,以确保您涵盖其余的基础。

【讨论】:

【参考方案2】:

如果您要使用存储过程,那么您可以声明 TVP。在 C# 中,当您填充 TVP 时,它会失败,并且您会在 catch 中知道它失败了,然后递归这 100 行。

递归函数 它将 N 行分解为 n/2 并再次调用 TVP 填充。如果第一组没问题,它将继续,第二组将失败。失败的集合你可以简单地再次调用这个递归函数。它将把您的安全记录分别保存在 TVP 和失败的记录中。您最多可以调用此递归函数 X。其中 X 是一个数字,例如 5,6,7。之后,您将只有不良记录。

如果您需要了解TVP,可以查看this

注意:您不能对这种方法使用并行查询执行。

【讨论】:

从应用程序代码到数据库来回执行此操作会很慢,甚至可能比仅执行单次插入还要慢。 @StingyJack 我知道我给出的方法,但我发现你的答案更适合这个。我不知道 Merge 语句,也从未使用过,感谢您教导并告诉我们另一个 SQL 术语。

以上是关于有没有办法让 Entity Framework Core 部分保存数据?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 Entity Framework Core 中定义通用关系过滤器?

ADO.NET Entity Framework 和标识列

有没有办法以编程方式检查 Entity Framework Core 中的待定模型更改?

Entity Framework linq 中的动态表名

Entity Framework Core RC2 表名复数

如何让 Visual Studio 发布 Web 向导相信我的数据库是 Entity Framework CodeFirst?