我正在尝试创建一个封装4个数据库表插入和2个更新的事务。
我“大部分”都在工作。我的意思是,如果我在这6个db交互中的任何一个中得到错误,则先前的回滚发生...除了第一个。第一个是对Header表的插入...后续插入到详细信息表甚至是另一个头表等等...所有回滚...但是如果在回滚后检查表,则所有表都没有记录,除了第一个。
//Create receipt, ic, printq; update pod, poh
public List<ActionConfirmation<int>> CreateReceipt(
IEnumerable<ReceiptDetailPalletListViewModel> viewModelList,
int intUserId,
int intFacilityId,
int intLocationId
)
{
var dbContext = new InventoryMgmtContext();
//Opening connection
dbContext.Database.Connection.Open();
int intReceiptHdrId = 0;
int intICHdrId = 0;
var results = new List<ActionConfirmation<int>>();
foreach (ReceiptDetailPalletListViewModel viewModel in viewModelList)
{
if (viewModel.ReceivedQty > 0)
{
using (TransactionScope transaction = new TransactionScope())
{
//Create Receipt Header
ActionConfirmation<int> rcptHdrResult = CreateReceiptHeader(
dbContext,
intUserId,
intFacilityId); <===== This Tran never rolls back. Insert occured.
results.Add(rcptHdrResult);
if (!rcptHdrResult.WasSuccessful) //Recp Hdr create failed.
{
CloseFailedTrans(dbContext, transaction);
return results;
}
intReceiptHdrId = rcptHdrResult.Value;
//Create new ICHeader
ActionConfirmation<int> icHdrResult = CreateICHeader(
dbContext,
intUserId,
intFacilityId,
intLocationId,
intReceiptHdrId
);
results.Add(icHdrResult);
if (!icHdrResult.WasSuccessful)
{
CloseFailedTrans(dbContext, transaction);
return results;
}
intICHdrId = icHdrResult.Value;
//Create new ICDetail
ActionConfirmation<int> icDtlResult = CreateICDetail(
dbContext,
intICHdrId,
viewModel.ItemId,
viewModel.PODetailId,
viewModel.ReceivedQty,
intUserId
);
results.Add(icDtlResult);
if (!icDtlResult.WasSuccessful)
{
CloseFailedTrans(dbContext, transaction);
return results;
}
//Create new Recpt Detail
ActionConfirmation<int> rcptDtlResult = CreateReceiptDetail(
dbContext,
intReceiptHdrId,
viewModel.PODetailId,
viewModel.ReceivedQty,
intUserId
);
results.Add(rcptDtlResult);
if (!rcptDtlResult.WasSuccessful)
{
CloseFailedTrans(dbContext, transaction);
return results;
}
//Update PO Detail qty and Header status
List<ActionConfirmation<int>> poResults = UpdatePODetail(
dbContext,
viewModel.PODetailId,
viewModel.ReceivedQty,
intUserId
);
foreach (ActionConfirmation<int> poResult in poResults)
{
results.Add(poResult);
if (!poResult.WasSuccessful)
{
CloseFailedTrans(dbContext, transaction);
return results;
}
}
//Create new Print Q
ActionConfirmation<int> printqResult = CreatePrintQRecords(
dbContext,
intICHdrId,
intFacilityId,
intUserId
);
results.Add(printqResult);
if (!printqResult.WasSuccessful)
{
CloseFailedTrans(dbContext, transaction);
return results;
}
//Everything inserted correctly
CloseSuccessTrans(dbContext, transaction);
} //using statement
} //if rcv qty > 0
} // for each loop
dbContext.Database.Connection.Dispose();
return results;
}
以下是与交易相关的方法:
// Close DB Connections and transaction
private void CloseFailedTrans(InventoryMgmtContext dbContext, TransactionScope transaction)
{
//TODO: logging
CloseTrans(dbContext, transaction);
}
// Close DB Connections and transaction
private void CloseSuccessTrans(InventoryMgmtContext dbContext, TransactionScope transaction)
{
transaction.Complete();
CloseTrans(dbContext, transaction);
}
// Close DB Connections and transaction
private void CloseTrans(InventoryMgmtContext dbContext, TransactionScope transaction)
{
transaction.Dispose();
}
以下是执行插入的方法之一的示例。它们都遵循相同的模式:
//Create Receipt Header
private ActionConfirmation<int> CreateReceiptHeader(
InventoryMgmtContext dbContext,
int intUserId,
int intFacilityId
)
{
//var repository = new Repository<ReceiptHeader>(dbContext);
var repository = new ReceiptHeaderRepository(dbContext);
//Create new Receipt Header
ReceiptHeader rcptHdr = new ReceiptHeader()
{
FacilityId = intFacilityId,
StatusId = 1,
CreatedById = intUserId,
CreatedOn = DateTime.Now,
ModifiedById = intUserId,
ModifiedOn = DateTime.Now
};
return repository.Insert(rcptHdr);
}
这是存储库插入方法:
public virtual ActionConfirmation<int> Insert(TRepository entity)
{
try
{
_dataContext.Entry(entity).State = System.Data.EntityState.Added;
_dataContext.SaveChanges();
return CRUDMessage(true, "saved", entity);
}
catch (Exception ex)
{
return CRUDMessage(false, "save", entity, ex);
}
}
答案 0 :(得分:1)
您不需要TransactionScope
。只需创建一个当前启动TransactionScope
的新上下文。为了使这项工作更好,您需要删除多个退出点(return
),并在结束时只调用SaveChanges()
并捕获异常。这也将清理您的代码并使其更易于维护(多个退出点被视为反模式)。
只有SaveChanges()
,而不提供其他内容,提交对数据库的更改。 SaveChanges()
管理自己的交易:它会保存全部或全部保存。
答案 1 :(得分:1)
自.Net 4以来,如果在对SaveChanges()的单次调用中触发了所有Db更改,则实体框架将决定它们实际执行的顺序。
执行的一般顺序是DELETE,INSERT,最后是UPDATE。
如果插页的顺序无关紧要,例如没有外键类型约束,那么单个SaveChanges()将正常工作。
如果插入的顺序很重要,则无法强制EF更改执行的语句。
我建议的第一个解决方案是对SaveChanges()进行多次调用,每次调用SaveChanges()后,上下文都会忘记它的更改,因此无法使用RollBack。
我建议的第二个解决方案是提供回滚功能将使用TransactionScope并使用SaveChanges(false)(false使对象上下文记住它的更改,从而使回滚成为可能)或savechanges()使用保存选项(新方法)这样做。)
e.g。
var scope = new TransactionScope(
TransactionScopeOption.RequiresNew,
// we will allow volatile data to be read during transaction
new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable }
);
using (scope)
{
// Create new contexts for each operation
Entities myEntities = new Entities();
Entities myEntities2 = new Entities();
// Do stuff with the contexts
// Insert into myEntities then call myEntities.SaveChanges(false);
// Insert into myEntities2 then call myEntities.SaveChanges(false);
scope.Complete();
myEntities.Context.AcceptAllChanges();
myEntities2.Context.AcceptAllChanges();
}