一次在多个表中插入/更新数据的最佳实践

Posted

技术标签:

【中文标题】一次在多个表中插入/更新数据的最佳实践【英文标题】:Best practice for inserting/updating data in multiple tables at once 【发布时间】:2011-06-25 07:34:30 【问题描述】:

所以我的数据库中有两个表,联系人和地址:

联系人:ContactID |地址ID |名字 |姓氏

地址:地址ID |地址1 |地址2 |城市 |状态 |邮政编码

我有一个页面,您可以在其中添加联系人。它包含联系人的所有信息和联系人的地址。这是我提交表单时的代码:

[HttpPost]
public ActionResult Edit(ContactsViewModel viewModel)

    //if editing an contact, fetch it; otherwise, create a new one
    Contact contact = viewModel.ContactId == 0
        ? new Contact()
        : _adminRepository.GetContact(viewModel.ContactId);
    TryUpdateModel(contact);

    if (ModelState.IsValid)
    
        _adminRepository.SaveAddress(contact.Address);
        _adminRepository.SaveContact(contact);

        return RedirectToAction("Index");
    
    return View(viewModel);     // validation error, so redisplay same view

现在我主要关心的是地址添加成功,但是当我尝试保存联系人时出现错误,使地址在数据库中没有联系人。

如果发生错误,回滚任何更改的最佳做法是什么?

【问题讨论】:

你在 SaveAddress 和 SaveContact 中做什么?你尝试提交给数据库吗?尝试使用工作单元 【参考方案1】:

那应该是一种方法,而不是两种

如果一个 Address 总是与一个 Contact 相关联(看起来是这样),那么您应该只有一个存储库 - 例如一个 ContactRepository.

您应该为每个聚合根创建一个存储库,或者简单地说 - 每个需要自行检索的实体都有一个存储库。

我不知道_adminRepository 是什么,但我认为你只是有一个SaveContact(contact) 方法。

您使用的是像实体框架这样的 ORM 吗?如果是这样,两个插入都将在一个事务中完成(由 EF 处理),所以如果一个失败,两个都会失败。

如果您开始对多个存储库/聚合根进行更改,那么您将需要工作单元TransactionScope。

所以听起来您需要重新考虑一下您的存储库设计。单个聚合实体(联系人)应该有一个通用的 Save 方法,而不是每个关系的单独方法。

【讨论】:

是的,我正在使用实体框架。我的管理存储库包含每个实体的函数,因此有 SaveContact()、SaveAddress、GetAllContacts()、GetAllAddresses() 等。我这样做对吗? @Steven - 两个问题:1)你可以添加一个没有联系人的地址吗? 2) 你可以添加没有地址的联系人吗?听起来答案是否定的。对吗? 实际上是和不是:)。您可以添加没有联系人的地址,但您必须添加有地址的联系人。 是的,ContactRepository 似乎是 transactionscope 的更好名称 +1 @Steven - 对,那么它归结为您是否需要独立访问联系人和地址(例如通过 ID)。如果这样做,那么您将需要 2 个存储库。【参考方案2】:

“工作单元”模式很好并被接受,用于 db 上的多个操作 - 或其他操作 - 请阅读来自 Faisal Mohamood, Program Manager, Entity Framework blogs. 的示例

【讨论】:

【参考方案3】:

您需要在存储库中使用 TransactionScope()。查看MSDN forum entry。第一个回复显示了一个将其集成到方法中的简单示例。关键是没有它就没有办法专门回滚更改。

【讨论】:

【参考方案4】:

我的偏好是创建一个存储过程来插入两条记录。您可以使用 TSQL 事务。使用 TransactionScope() 也可以,因此决定可能应该基于您的数据访问层。如果您已经在使用很多 SPROC,请使用事务制作一个复杂的 SPROC。

如果您使用的是实体框架(......痛苦!!!!)那么 .net TransactionScope() 会更好。

【讨论】:

我正在使用简单的 dapper 进行数据操作,并希望使用普通的 sql 查询(无存储过程)来实现。如果我采用第二种方法,我必须为我的数据库中的每个实体创建存储库,其中代码可能更多,而且我需要对所有存储库进行单元测试。

以上是关于一次在多个表中插入/更新数据的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

如何一次将数据插入和更新到 EF 多个表中

如何一次在多个表中添加行?

使用 Postgres 一次在 3 个表中插入数据

一次在多个站点上更新克隆插件

Flyway 最佳实践:一个大型迁移脚本与多个增量脚本

Cassandra 编辑最佳实践:删除和重新插入与更新?