【问题标题】:Is there a way to have Entity Framework Core partially save data?有没有办法让 Entity Framework Core 部分保存数据?
【发布时间】:2023-03-03 03:44:01
【问题描述】:

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

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

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

【问题讨论】:

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

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


【解决方案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 术语。
    猜你喜欢
    • 2020-05-03
    • 2016-11-08
    • 2018-09-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 2020-07-03
    • 2015-05-25
    • 1970-01-01
    相关资源
    最近更新 更多