我有一个使用MVC和EF的网络应用。我正在使用Microsoft在线文档中的Repository and Unit of Work Patterns。 我试图从多个表中插入多个行。 代码看起来像这样:
unitOfWork.Table1.Insert(row1);
unitOfWork.Save();//recId is primary key, will be auto generated after save.
table2Row.someId = table1Row.recId;
unitOfWork.Table2.Insert(row2);
unitOfWork.Save();
如果在插入row2时出现任何问题,我需要回滚row1和row2。 如何使用UnitOfWork模式实现BeginTransaction / Commit / Rollback?
谢谢。
答案 0 :(得分:0)
为避免这些问题,最好通过使用引用属性而不是直接设置FK值来将EF用作ORM,而不是简单的数据转换服务。
您的示例似乎并没有提供比DbContext的抽象抽象更多的功能。
给出一个新客户的订单示例,您希望在该订单上显示一个CustomerId。
问题:新的客户ID由数据库生成,因此仅在SaveChanges之后可用。
解决方案:使用EF导航属性,并让EF管理FK。
var customer = new Customer
{
Name = customerName,
// etc.
};
var order = new Order
{
OrderNumber = orderNumber,
// etc.
Customer = customer,
};
dbContext.Orders.Add(order);
dbContext.SaveChanges();
请注意,我们不必将Customer显式添加到dbContext,它将通过订单的Customer引用添加,并且Order表的CustomerId将自动设置。如果上下文中有一个Customer DbSet,则也可以显式添加该客户,但是只需要一个SaveChanges调用即可。
要设置导航属性,请参见:https://stackoverflow.com/a/50539429/423497
**编辑(基于关于一对多关系的评论)** 对于集合,应避免的一些常见陷阱包括直接为已经与DbContext关联的实体设置集合引用,并利用虚拟引用,以便EF可以最好地管理对集合中实例的跟踪。
如果一个订单有多个客户,那么您将在该订单中有一个客户集合:
public virtual List<Customer> Customers{ get; set; } = new List<Customer>();
以及客户中的订单参考(可选):
public virtual Order Order { get; set; }
将其映射为:(从“订单”角度看)
HasMany(x => x.Customers)
.WithRequired(x => x.Order)
.Map(x => x.MapKey("OrderId"));
或如果客户没有订单参考,则替换为.WithRequired()
。
这基于实体未声明FK字段的关系。如果声明FK,则.Map(...)
变为.HasForeignKey(x => x.FkProperty)
如果要创建新订单,请从此处开始
var order = new Order
{
OrderNumber = "12345",
Customers = new []
{
new Customer { Name = "Fred" },
new Customer { Name = "Ginger" }
}.ToList()
};
using (var context = new MyDbContext())
{
context.Orders.Add(order);
context.SaveChanges();
}
这应该都能按预期工作。它将保存新订单和两个相关客户。
但是,如果您从DbContext加载订单并想操纵与之关联的客户,则有一些警告。 1.您应该急于加载“客户”集合,以便EF了解这些实体。 2.您需要使用“添加/删除”来操作集合,而不是设置引用,以避免混淆代码的外观和EF的解释。
类似的东西:
using (var context = new MyDbContext())
{
var order = context.Orders.Find(1);
order.Customers = new []
{
new Customer { Name = "Roy" }
}.ToList();
context.SaveChanges();
}
将导致“ Roy”被添加到客户中,而不是替换它们。
要替换它们,您需要先将其删除,然后添加新的。
using (var context = new MyDbContext())
{
var order = context.Orders.Find(1);
context.Customers.RemoveRange(order.Customers); // Assuming customers cannot exist without orders. If OrderId is nullable, this line can be omitted.
order.Customers.Clear();
order.Customers,Add(new Customer { Name = "Roy" });
context.SaveChanges();
}
如果集合不是虚拟的,则开始崩溃。例如:
using (var context = new MyDbContext())
{
var order = context.Orders.Find(1);
order.Customers = new []
{
new Customer { Name = "Roy" }
}.ToList();
context.SaveChanges();
}
如果客户集合是虚拟的,则在SaveChanges之后订购。客户将报告集合大小为3个元素。如果它不是虚拟的,则即使数据库中有3条记录与此订单相关联,它也会将大小报告为1个元素。这会导致各种问题,其中项目会因无效的数据状态,重复的记录等而陷入困境。
在某些情况下会丢失一些记录的情况可能是由于缺少参考上的虚拟,操纵EF所跟踪的集合之外的其他集合或对跟踪状态的其他操纵。 (将项目设置为从上下文分离/重新附加实体时,这是一个常见问题。)