asp.net-mvc - Insert data into multiple tables with transaction and rollback on failure
问题描述
What is the best way to store the data to database, while inserting multiple classes together to database. I have around 10 classes where I would like to save all the data together to database.
Posting some classes with relations:
Public class ClassMain
{
public int ClassMainId { get; set; }
}
Public class ClassA
{
public int ClassAId { get; set; }
public int ClassMainId { get; set; }
public string ClassAName { get; set; }
}
Public class ClassB
{
public int ClassBId { get; set; }
public int ClassMainId { get; set; }
public string ClassBName { get; set; }
}
Like above I have some other tables with the relation
I would like to insert this data in one go, if there's any failure, I would like to rollback the data, in Entity Framework or stored procedure approach do we have the approach to do this.
I am going through this approach to achieve
https://dotnettutorials.net/lesson/unit-of-work-csharp-mvc/
Is there any other approach?
解决方案
如果它们是使用相同的 DbContext 和单个SaveChanges()
调用插入的,它们将在同一个事务中提交。
如果实体通过 FK 相互关联,那么您希望 DB 管理 PK 并自动解析 FK 的最佳解决方案是设置导航属性并将从属实体添加到顶级实体,然后将顶级的添加到各自的 DBSet 并调用 SaveChanges。
在你的情况下,你会想要这样的东西:
public class ClassMain
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClassMainId { get; set; }
[InverseProperty("ClassMain")]
public virtual ICollection<ClassA> ClassAs { get; set;} = new List<ClassA>();
[InverseProperty("ClassMain")]
public virtual ICollection<ClassB> ClassBs { get; set;} = new List<ClassB>();
}
public class ClassA
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClassAId { get; set; }
public int ClassMainId { get; set; }
[ForeignKey("ClassMainId")]
public virtual ClassMain ClassMain{ get; set; }
public string ClassAName { get; set; }
}
Public class ClassB
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClassBId { get; set; }
public int ClassMainId { get; set; }
[ForeignKey("ClassMainId")]
public virtual ClassMain ClassMain{ get; set; }
public string ClassBName { get; set; }
}
对于 EF Core 5,ClassA 和 B 中的 FK ClassMainId 是可选的。EF Core 可以简单地使用 Shadow 属性进行映射,而不是将它们作为属性公开。(推荐的)
现在,当您创建一个 ClassMain 以及关联的 A 和 B 时,您只需实例化新实例,填充各种字段,并将顶级实体添加到 DbSet。
using(var context = new AppDbContext())
{
var newMain = new ClassMain
{
// ... set main properties, but don't worry about PK
ClassA = new ClassA { ClassAName = someAName },
ClassB = new ClassB { ClassBName = someBName }
};
context.ClassMains.Add(newMain);
context.SaveChanges();
}
您也可以在没有身份 PK 的情况下执行此操作,您可以在代码中设置 PK/FK,但您有责任确保它们是唯一的。通过设置导航属性,EF 将负责在保存所有内容时关联 FK。他们将一起保存或根本不保存。
编辑:如果您不希望 ClassMain 包含 ClassA 和 ClassB 实例的集合,您可以删除这些集合和 InverseProperty 属性。填充实例看起来更像:
using(var context = new AppDbContext())
{
var newMain = new ClassMain();
var classA = new ClassA { ClassAName = someAName, ClassMain = newMain };
var classB = new ClassB { ClassBName = someBName, ClassMain = newMain };
context.ClassAs.Add(classA);
context.ClassBs.Add(classB);
context.SaveChanges();
}
在这种情况下,ClassMain 实例将与 ClassA 和 B 一起保存。
推荐阅读
- react-native - 反应本机应用程序中需要领域时出错
- xquery - 我想在一个 xml 文件中返回具有相同文本值的重复节点
- vuejs2 - vue-router:如何将`/`添加到每个链接的末尾
- excel - VBA循环,选择数据透视表中的下一个条目
- python - Python multipart/form-data 请求错误:数据不能是字符串
- javascript - 当 componentDidMount 方法开始触发时,在初始 ReactDOM.render 之后整个 DOM 准备好了吗?
- java - 轮班结束时暂停处理
- javascript - 在引导程序中,如果未指定 col-md 和 col-lg,是否将 class="col-sm-4" 视为 class="col-sm-4 col-md-4 col-lg-4"?
- python - 进程以退出代码 -1073741819 (0xC0000005) Pycharm 完成
- php - Symfony 4 不会翻译并且总是显示英文默认