/// <summary> /// Handler for validating Source Order records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> internal static void OnSourceOrderRowValidate(object sender, SourceOrderRowChangeEventArgs e) { Decimal currentQuantity; Decimal destinationOrderQuantity; DataModelTransaction dataModelTransaction; Decimal originalQuantity; Decimal sourceOrderQuantity; WorkingOrderRow workingOrderRow; // The Business Rules will be enforced on this Source Order. Note that it is locked at the point this handler is called. SourceOrderRow sourceOrderRow = e.Row; // The action on the row determines which rule to evaluate. switch (e.Action) { case DataRowAction.Change: // Reject the operation if the Working Order is overcommitted with a destination. Note that the order of the evaluation is important for // efficiency. That is, there is no need to sum up the Source and Destination Orders, which requires locking several records, if the Source // Order quantity has been incremented as there's no chance it will be over committed. originalQuantity = (Decimal)sourceOrderRow[DataModel.SourceOrder.OrderedQuantityColumn, DataRowVersion.Original]; currentQuantity = (Decimal)sourceOrderRow[DataModel.SourceOrder.OrderedQuantityColumn, DataRowVersion.Current]; if (originalQuantity < currentQuantity) { // A middle tier context is required to lock the rows so the quantities can be aggregated. dataModelTransaction = DataModelTransaction.Current; // Once its determined that the quantity has increased, the Working Order row will need to be locked to examine the aggregate values of the // source and Destination Orders. workingOrderRow = e.Row.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); // This is an illegal operation if the quantity sent to a destination is greater than the quantity ordered. sourceOrderQuantity = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); destinationOrderQuantity = WorkingOrder.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } } break; case DataRowAction.Delete: // A middle tier context is required to lock the rows so the quantities can be aggregated. dataModelTransaction = DataModelTransaction.Current; // The main idea of this trigger is to prevent the over commitment of a Working Order when a Source Order is deleted. At this point, the // Source Order is deleted, so the original record must be used to find the parent Working Order that is effected. sourceOrderRow = e.Row; try { DataModel.DataLock.EnterReadLock(); workingOrderRow = ((WorkingOrderRow)(sourceOrderRow.GetParentRow(DataModel.SourceOrder.WorkingOrderSourceOrderRelation, DataRowVersion.Original))); } finally { DataModel.DataLock.ExitReadLock(); } // Lock the working order row while some aggregate values are calculated. workingOrderRow.AcquireReaderLock(dataModelTransaction); // Reject the operation if the Working Order is overcommitted with a destination. sourceOrderQuantity = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); destinationOrderQuantity = WorkingOrder.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } break; } }
/// <summary> /// Handler for validating Destination Order records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> internal static void OnDestinationOrderRowValidate(object sender, DestinationOrderRowChangeEventArgs e) { Int32 currentStatusCode; Decimal destinationOrderQuantity; DataModelTransaction dataModelTransaction; Int32 previousStatusCode; Decimal sourceOrderQuantity; WorkingOrderRow workingOrderRow; // The Business Rules will be enforced on this Destination Order. Note that it is locked at the point this handler is called. DestinationOrderRow destinationOrderRow = e.Row; // The action on the row determines which rule to evaluate. switch (destinationOrderRow.RowState) { case DataRowState.Added: // This rule will reject the operation if the Working Order is overcommitted with a destination. dataModelTransaction = DataModelTransaction.Current; // This rule will throw an exception if the quantity sent to a destination is greater than the quantity ordered. The quantity ordered and // quantity sent can only be calcuated from the owning Working Order which must be locked in order to carry out the calculations. workingOrderRow = e.Row.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); sourceOrderQuantity = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); destinationOrderQuantity = WorkingOrder.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } break; case DataRowState.Modified: // Reject the operation if the Working Order is overcommitted with a destination. Note that the order of the evaluation is important for // efficiency. There is no need to sum up the source and Destination Orders if the quantity has been reduced as there's no chance it will be // over committed. Decimal originalQuantity = (Decimal)destinationOrderRow[DataModel.DestinationOrder.OrderedQuantityColumn, DataRowVersion.Original]; Decimal currentQuantity = (Decimal)destinationOrderRow[DataModel.DestinationOrder.OrderedQuantityColumn, DataRowVersion.Current]; if (originalQuantity < currentQuantity) { // Once it's determined that the order can be overcommitted, a middle tier context is required to lock the rows so the quantities can be // aggregated. dataModelTransaction = DataModelTransaction.Current; // This rule will throw an exception if the quantity sent to a destination is greater than the quantity ordered. The quantity ordered and // quantity sent can only be calcuated from the owning Working Order which must be locked in order to carry out the calculations. workingOrderRow = e.Row.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); sourceOrderQuantity = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); destinationOrderQuantity = WorkingOrder.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } } // This rule will examine the effects of a state change of the Destination Order. The stateChangeMatrix contains delegates to methods that // will determine what, if any, stats change should be applied to the Working Order due to the change in state of this Destination Order. previousStatusCode = Convert.ToInt32(StatusMap.FromId((Guid)destinationOrderRow[DataModel.DestinationOrder.StatusIdColumn, DataRowVersion.Original])); currentStatusCode = Convert.ToInt32(StatusMap.FromId((Guid)destinationOrderRow[DataModel.DestinationOrder.StatusIdColumn, DataRowVersion.Current])); if (previousStatusCode != currentStatusCode) { DestinationOrder.statusChangeMatrix[previousStatusCode, currentStatusCode](new Object[] { destinationOrderRow.WorkingOrderId }); } break; case DataRowState.Deleted: // This rule will examine the effects of a state change of the Destination Order. The stateChangeMatrix contains delegates to methods that // will determine what, if any, stats change should be applied to the Working Order due to the change in state of this Destination Order. previousStatusCode = Convert.ToInt32(StatusMap.FromId((Guid)destinationOrderRow[DataModel.DestinationOrder.StatusIdColumn, DataRowVersion.Original])); currentStatusCode = (Int32)Status.Deleted; Guid workingOrderId = (Guid)destinationOrderRow[DataModel.DestinationOrder.WorkingOrderIdColumn, DataRowVersion.Original]; DestinationOrder.statusChangeMatrix[previousStatusCode, currentStatusCode](new Object[] { workingOrderId }); break; } }