- Home
- LLBLGen Pro
- Architecture
UnitOfWork and Transactions
Joined: 19-Feb-2008
Version: 2.5 Final Runtime Library Version: 2.5.08.0122 Template: Self-Servicing .NET Version: 2.0 Database: SQL Server 2005
I have been using LLBLGen for quite a while now and have been able to most everything I need to do with it.
I am now working on a particular application that requires some more complicated transactions.
The trouble I am having is that this code run perfectly on the first save. But the second save times out because the framework tries to run a query after I have already committed a UnitOfWork but have not committed the transaction.
I have many foreign keys defined in my database. I would also like to note that the second commit only times out when this particular entity is updated.
I can make a change to the ShipmentHeaderEntity and it commits properly. It is only when I make a change to a ShipmentSupplyEntity that I am having problems. A ShipmentSupplyEntity has several foreign key relations.
I need everything in a single transaction.
I have read the documentation on the UnitOfWork class and didn't see anything to point me to a solution. Perhaps I missed something.
I am sorry for posting such a long snippet but I don't have much else for an example. Hopefully someone will see something that I am doing incorrectly.
Here is the extremely long example:
private void SaveEntity()
{
try
{
if ((this.shipmentHeaderEntityBindingSource.Position < 0) ||
(this.shipmentHeaderEntityBindingSource.Current == null))
{
return;
}
ShipmentHeaderEntity shipmentHeaderEntity = (ShipmentHeaderEntity)this.shipmentHeaderEntityBindingSource.Current;
shipmentHeaderEntity.SaveFields("Before_Save");
Transaction transaction = null;
try
{
transaction = new Transaction(IsolationLevel.ReadUncommitted, "SAVE_SHIPMENT");
UnitOfWork unitOfWork = new UnitOfWork();
OrderReleaseCollection orderReleaseCollection = new OrderReleaseCollection();
orderReleaseCollection.GetMulti(
(OrderReleaseFields.OrderNumber == shipmentHeaderEntity.OrderNumber),
0,
new SortExpression(OrderReleaseFields.OrderNumber | SortOperator.Ascending) &
(OrderReleaseFields.OrderLine | SortOperator.Ascending) &
(OrderReleaseFields.OrderRelease | SortOperator.Ascending)
);
foreach (OrderReleaseEntity orderReleaseEntity in orderReleaseCollection)
{
ShipmentDetailCollection shipmentDetailCollection = new ShipmentDetailCollection();
shipmentDetailCollection.GetMulti(
(ShipmentDetailFields.OrderNumber == orderReleaseEntity.OrderNumber) &
(ShipmentDetailFields.OrderLine == orderReleaseEntity.OrderLine) &
(ShipmentDetailFields.OrderRelease == orderReleaseEntity.OrderRelease) &
(ShipmentDetailFields.ShipmentNumber != shipmentHeaderEntity.ShipmentNumber),
0,
new SortExpression(ShipmentDetailFields.ShipmentNumber | SortOperator.Ascending) &
(ShipmentDetailFields.ShipmentLine | SortOperator.Ascending)
);
foreach (ShipmentDetailEntity shipmentDetailEntityTemp in shipmentHeaderEntity.ShipmentDetail)
{
if ((shipmentDetailEntityTemp.OrderNumber != orderReleaseEntity.OrderNumber) ||
(shipmentDetailEntityTemp.OrderLine != orderReleaseEntity.OrderLine) ||
(shipmentDetailEntityTemp.OrderRelease != orderReleaseEntity.OrderRelease))
{
continue;
}
if (!shipmentDetailCollection.Contains(shipmentDetailEntityTemp))
{
shipmentDetailCollection.Add(shipmentDetailEntityTemp);
}
}
decimal totalShippedFromInventory = 0;
decimal totalShippedFromJob = 0;
foreach (ShipmentDetailEntity shipmentDetailEntity in shipmentDetailCollection)
{
foreach (ShipmentSupplyEntity shipmentSupplyEntity in shipmentDetailEntity.ShipmentSupply)
{
switch (shipmentSupplyEntity.SupplyType)
{
case "I":
totalShippedFromInventory += shipmentSupplyEntity.Quantity;
break;
case "J":
totalShippedFromJob += shipmentSupplyEntity.Quantity;
break;
}
}
}
orderReleaseEntity.ShippedQuantity = (totalShippedFromInventory + totalShippedFromJob);
orderReleaseEntity.IsOpen = (orderReleaseEntity.ShippedQuantity < orderReleaseEntity.Quantity);
}
unitOfWork.AddCollectionForSave(orderReleaseCollection);
foreach (ShipmentDetailEntity shipmentDetailEntity in shipmentHeaderEntity.ShipmentDetail)
{
foreach (ShipmentSupplyEntity shipmentSupplyEntity in shipmentDetailEntity.ShipmentSupply)
{
if ((shipmentSupplyEntity.JobNumber == null) ||
(shipmentSupplyEntity.JobLine == null))
{
continue;
}
ShipmentSupplyCollection shipmentSupplyCollection = new ShipmentSupplyCollection();
shipmentSupplyCollection.GetMulti(
(ShipmentSupplyFields.JobNumber == shipmentSupplyEntity.JobNumber) &
(ShipmentSupplyFields.JobLine == shipmentSupplyEntity.JobLine) &
(ShipmentSupplyFields.ShipmentNumber != shipmentSupplyEntity.ShipmentNumber),
0,
new SortExpression(ShipmentSupplyFields.ShipmentNumber | SortOperator.Ascending) &
(ShipmentSupplyFields.ShipmentLine | SortOperator.Ascending) &
(ShipmentSupplyFields.ShipmentSupplyId | SortOperator.Ascending)
);
foreach (ShipmentDetailEntity shipmentDetailEntityTemp in shipmentHeaderEntity.ShipmentDetail)
{
foreach (ShipmentSupplyEntity shipmentSupplyEntityTemp in shipmentDetailEntityTemp.ShipmentSupply)
{
if ((shipmentSupplyEntityTemp.JobNumber != shipmentSupplyEntity.JobNumber) ||
(shipmentSupplyEntityTemp.JobLine != shipmentSupplyEntity.JobLine))
{
continue;
}
shipmentSupplyCollection.Add(shipmentSupplyEntityTemp);
}
}
decimal totalShippedQuantity = 0;
foreach (ShipmentSupplyEntity shipmentSupplyEntityTemp in shipmentSupplyCollection)
{
totalShippedQuantity += shipmentSupplyEntityTemp.Quantity;
}
JobDetailEntity jobDetailEntity = new JobDetailEntity((int)shipmentSupplyEntity.JobNumber, (int)shipmentSupplyEntity.JobLine);
if ((jobDetailEntity == null) ||
(jobDetailEntity.IsNew))
{
throw new Exception("Job detail line could not be found.");
}
jobDetailEntity.ShippedQuantity = totalShippedQuantity;
unitOfWork.AddForSave(jobDetailEntity, false);
}
}
PartTransactionCollection partTransactionCollection = new PartTransactionCollection();
foreach (ShipmentDetailEntity shipmentDetailEntity in shipmentHeaderEntity.ShipmentDetail)
{
foreach (ShipmentSupplyEntity shipmentSupplyEntity in shipmentDetailEntity.ShipmentSupply)
{
PartTransactionCollection partTransactionCollectionTemp = new PartTransactionCollection();
partTransactionCollectionTemp.GetMulti(
(PartTransactionFields.ShipmentNumber == shipmentSupplyEntity.ShipmentNumber) &
(PartTransactionFields.ShipmentLine == shipmentSupplyEntity.ShipmentLine) &
(PartTransactionFields.ShipmentSupplyId == shipmentSupplyEntity.ShipmentSupplyId),
0,
new SortExpression(PartTransactionFields.ShipmentNumber | SortOperator.Ascending) &
(PartTransactionFields.ShipmentLine | SortOperator.Ascending) &
(PartTransactionFields.ShipmentSupplyId | SortOperator.Ascending) &
(PartTransactionFields.TransactionId | SortOperator.Ascending)
);
ShipmentSupplyEntity shipmentSupplyEntityTemp = new ShipmentSupplyEntity(shipmentSupplyEntity.ShipmentNumber, shipmentSupplyEntity.ShipmentLine, shipmentSupplyEntity.ShipmentSupplyId);
if ((partTransactionCollectionTemp.Count <= 0) &&
(shipmentSupplyEntityTemp != null) &&
(!shipmentSupplyEntityTemp.IsNew))
{
throw new Exception("Part transaction record is missing for existing shipment supply.");
}
PartTransactionEntity partTransactionEntity = new PartTransactionEntity();
partTransactionEntity.Date = DateTime.Now;
partTransactionEntity.MachineName = this.MachineName;
partTransactionEntity.UserId = this.UserID;
partTransactionEntity.PartNumber = shipmentDetailEntity.PartNumber;
partTransactionEntity.RevisionId = (shipmentDetailEntity.RevisionId == "") ? null : shipmentDetailEntity.RevisionId;
partTransactionEntity.ShipmentNumber = shipmentSupplyEntity.ShipmentNumber;
partTransactionEntity.ShipmentLine = shipmentSupplyEntity.ShipmentLine;
partTransactionEntity.ShipmentSupplyId = shipmentSupplyEntity.ShipmentSupplyId;
decimal transactionQuantity = shipmentSupplyEntity.Quantity;
if ((shipmentSupplyEntityTemp != null) &&
(!shipmentSupplyEntityTemp.IsNew))
{
transactionQuantity -= shipmentSupplyEntityTemp.Quantity;
}
switch (shipmentSupplyEntity.SupplyType)
{
case "I":
partTransactionEntity.TransactionType = PartTransactionEntity.PartTransactionType.INV_TO_SHIP.ToString();
partTransactionEntity.LocationId = shipmentSupplyEntity.LocationId;
PartCountEntity partCountEntity = new PartCountEntity(shipmentDetailEntity.PartNumber, shipmentSupplyEntity.LocationId);
if ((partCountEntity == null) ||
(partCountEntity.IsNew))
{
throw new Exception("Part count record could not be found.");
}
if (transactionQuantity > partCountEntity.Quantity)
{
throw new Exception("Insufficient part count quantity.");
}
partTransactionEntity.StartingQuantity = partCountEntity.Quantity;
partTransactionEntity.TransactionQuantity = (transactionQuantity * -1);
partCountEntity.Quantity -= transactionQuantity;
partTransactionEntity.FinalQuantity = partCountEntity.Quantity;
unitOfWork.AddForSave(partCountEntity, false);
break;
case "J":
partTransactionEntity.TransactionType = PartTransactionEntity.PartTransactionType.JOB_TO_SHIP.ToString();
partTransactionEntity.JobNumber = shipmentSupplyEntity.JobNumber;
partTransactionEntity.JobLine = shipmentSupplyEntity.JobLine;
JobDetailEntity jobDetailEntity = new JobDetailEntity((int)shipmentSupplyEntity.JobNumber, (int)shipmentSupplyEntity.JobLine);
if ((jobDetailEntity == null) ||
(jobDetailEntity.IsNew))
{
throw new Exception("Job Line could not be found.");
}
if (transactionQuantity > jobDetailEntity.Wipquantity)
{
throw new Exception("Insufficient job line quantity.");
}
partTransactionEntity.StartingQuantity = jobDetailEntity.Wipquantity;
partTransactionEntity.TransactionQuantity = (transactionQuantity * -1);
jobDetailEntity.Wipquantity -= transactionQuantity;
partTransactionEntity.FinalQuantity = jobDetailEntity.Wipquantity;
unitOfWork.AddForSave(jobDetailEntity, false);
break;
default:
throw new Exception("Supply type is invalid.");
}
partTransactionCollection.Add(partTransactionEntity);
}
}
if (shipmentHeaderEntity.IsNew)
{
unitOfWork.AddForSave(shipmentHeaderEntity, true);
}
else
{
unitOfWork.AddForSave(shipmentHeaderEntity, false);
}
unitOfWork.Commit(transaction, false);
if (shipmentHeaderEntity.ShipmentDetail.RemovedEntitiesTracker != null)
{
foreach (ShipmentDetailEntity shipmentDetailEntity in shipmentHeaderEntity.ShipmentDetail.RemovedEntitiesTracker)
{
if (shipmentDetailEntity.ShipmentSupply.RemovedEntitiesTracker != null)
{
unitOfWork.AddCollectionForDelete(shipmentDetailEntity.ShipmentSupply.RemovedEntitiesTracker);
}
unitOfWork.AddCollectionForDelete(shipmentDetailEntity.ShipmentSupply);
}
unitOfWork.AddCollectionForDelete(shipmentHeaderEntity.ShipmentDetail.RemovedEntitiesTracker);
}
unitOfWork.Commit(transaction, false);
foreach (ShipmentDetailEntity shipmentDetailEntity in shipmentHeaderEntity.ShipmentDetail)
{
foreach (ShipmentSupplyEntity shipmentSupplyEntity in shipmentDetailEntity.ShipmentSupply)
{
unitOfWork.AddForSave(shipmentSupplyEntity);
// TIMES OUT HERE VVVVVVV
unitOfWork.Commit(transaction, false);
// TIMES OUT HERE ^^^^^^^
}
}
unitOfWork.AddCollectionForSave(shipmentHeaderEntity.ShipmentDetail, false);
unitOfWork.Commit(transaction, false);
unitOfWork.AddForSave(shipmentHeaderEntity, false);
unitOfWork.Commit(transaction, false);
unitOfWork.AddCollectionForSave(partTransactionCollection, false);
unitOfWork.Commit(transaction, true);
transaction.Dispose();
}
catch
{
if (transaction != null)
{
transaction.Rollback();
}
shipmentHeaderEntity.RollbackFields("Before_Save");
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
transaction = null;
}
}
}
catch (ORMEntityValidationException exception)
{
ErrorForm errorForm = new ErrorForm(
exception,
"Error saving shipmentHeader information..."
);
errorForm.ShowDialog(this);
errorForm.StartPosition = FormStartPosition.CenterScreen;;
errorForm.Dispose();
}
catch (Exception exception)
{
ErrorForm errorForm = new ErrorForm(
exception,
"Error saving shipmentHeader information..."
);
errorForm.ShowDialog(this);
errorForm.StartPosition = FormStartPosition.CenterScreen;;
errorForm.Dispose();
}
}
Joined: 08-Oct-2008
Just out of interest, why do you need to call this twice without committing the transaction after the first call, are you calling it on two seperate entities ?
If this is the case, could you make the UnitOfWork common to the two calls, and only call Commit on it and the transaction at the end of the process ?
The SQL Server activity manager should be able to tell you which tables are being locked (as this will be the cause of the timeout) so should give you some more information.
Matt
Joined: 19-Feb-2008
This may get called again if there is a change to the shipment. The initial save works fine.
But if there is an amendment to the shipment it times out. I know which table is causing the problem but I don't know how to keep the framework from running a query after the transaction has been partially committed.
unitOfWork.AddForSave(shipmentSupplyEntity);
// TIMES OUT HERE VVVVVVV
unitOfWork.Commit(transaction, false);
The second time this code get run it executes a query. I assume the query is to check a foreign key. The first time no query is executed.
Joined: 08-Oct-2008
Why do you need to keep calling commit on the UOW work during the process ?
unitOfWork.AddCollectionForSave(shipmentHeaderEntity.ShipmentDetail, false);
>>> unitOfWork.Commit(transaction, false);
unitOfWork.AddForSave(shipmentHeaderEntity, false);
>>> unitOfWork.Commit(transaction, false);
unitOfWork.AddCollectionForSave(partTransactionCollection, false);
>>> unitOfWork.Commit(transaction, true);
Just add all of the items to the UOW and call commit at the end
unitOfWork.AddCollectionForSave(shipmentHeaderEntity.ShipmentDetail, false);
unitOfWork.AddForSave(shipmentHeaderEntity, false);
unitOfWork.AddCollectionForSave(partTransactionCollection, false);
unitOfWork.Commit(transaction, true);