我有两节课。第一个表示主数据,第二个表示其详细数据(它们通过外键连接)。 由于详细记录需要现有的主记录,我必须先保存主记录。不幸的是,在保存详细数据期间可能会发生错误,所以我应该在出现错误的情况下回滚保存主文件。
我有以下代码。
大师班:
MyDataContext db=new MyDataContext();
DetailData detail=new DetailData();
using (var transaction= db.Database.BeginTransaction())
{
try
{
//db.Database.CommandTimeout = 10;
db.SaveChanges();
//saving details
detail.SaveData(masterId);
//on success
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
详情课程:
MyDataContext db;
DataItem dataItem;
public DetailData()
{
db=new MyDataContext();
dataItem=new DataItem();
db.DataItem.Attach(dataItem);
}
public SaveData(int id)
{
dataItem.Master_ID=id;
db.SaveChanges();
}
由于best practice by Microsoft按需使用DataContext,我不想将现有的上下文传递给详细数据。
通常,DataContext实例设计为持续一个"单元 工作"但是您的应用程序定义该术语。 DataContext是 重量轻,创建起来并不昂贵。典型的LINQ to SQL 应用程序在方法范围或作为a创建DataContext实例 表示逻辑相关集的短寿类成员 数据库操作。
不幸的是,我得到了一个 Timeout异常,因为主事务尚未提交,我在另一个上下文对象上开始一个新的SaveChanges
。
如果我使用db.DataBase.UseTransaction
并传递主类的事务,我会得到另一个关于属于另一个上下文的事务的例外。
那么我怎样才能将主数据和详细数据的保存合并到一个工作事务中呢?
答案 0 :(得分:1)
也许我误解了你的问题,或者在你的情况下可能无法使用以下模式,但通常你可以像这样添加主/详细记录:
var customer = new Customer();
dbContext.Customers.Add(customer);
var order = new Order();
order.Customer = customer;
dbContext.SaveChanges();
这假定您的Customer
和Order
表与外键相关联,并且具有合适的EF导航属性(例如order.Customer
)。
实体框架将以正确的顺序添加Customer
和Order
记录,并添加到事务内的整个更新中,因此两个更新都会成功,或者两者都不成功。
更新:如果您有两个上下文,每个都不会意识到另一个上下文所做的更改 - 如果您尝试将一个上下文中的对象添加到另一个上下文中,那么您可以使用上下文。我会得到错误。
我怀疑你获得的超时时间是因为每个上下文都试图在事务中执行某些数据库操作,而第一个事务阻止了第二个事务。这不是你应该"结合"上下文 - 它表明你应该只有一个上下文。
您在问题中添加的引文说明工作单元。您的工作单元应逻辑上包括您对主数据和详细记录所做的所有更改。这就是重点。 您应该只有一个上下文! :-)但是,您可以在上下文中创建一个事务。
答案 1 :(得分:1)
如果我理解正确,您的Master类正在尝试执行两项任务:表示主数据记录并管理其持久性。同样的详细课程。以这种方式违背分离原则可能会引发这样的问题。
使用存储库和工作单元模式将帮助您将实体与管理事务的逻辑分开。请参阅MVC中存储库和工作单元模式的Tom Dykstra's excellent tutorial。