2

4つのデータベーステーブルの挿入と2つの更新をカプセル化するトランザクションを作成しようとしています。

私は「ほとんど」働いています。つまり、これらの6 dBの相互作用のいずれかでエラーが発生した場合、最初のロールバックを除いて、以前のロールバックが発生します。最初はヘッダーテーブルへの挿入です...後続の詳細テーブルや別のヘッダーテーブルへの挿入など...すべてロールバック...ただし、ロールバック後にテーブルを調べると、すべてのテーブルにレコードがありません、最初のものを除いて。

//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();
        }

これは、挿入を行うメソッドの1つの例です。それらはすべて同じパターンに従います。

//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);
            }
        }
4

2 に答える 2

1

は必要ありませんTransactionScope。現在開始している新しいコンテキストを作成するだけですTransactionScope。これをうまく機能させるには、複数の出口点(return)を削除し、最後に1回呼び出しSaveChanges()て、例外をキャッチする必要があります。これにより、コードがクリーンアップされ、保守が容易になります(複数の出口点はアンチパターンと見なされます)。

データベースSaveChanges()への変更をコミットするのは、他には何もありません。SaveChanges()独自のトランザクションを管理します。すべてを保存するか、何も保存しません。

于 2013-01-25T19:40:59.537 に答える
1

.Net 4以降、すべてのDb変更がSaveChanges()への1回の呼び出しで実行される場合、EntityFrameworkはそれらが実際に実行される順序を決定します。

実行の一般的な順序は、DELETE、INSERT、最後にUPDATEです。

挿入の順序が重要でない場合、たとえば外部キータイプの制約がない場合は、単一のSaveChanges()が正常に機能します。

挿入の順序が重要な場合、EFに実行される順序ステートメントを変更させる方法はありません。

私が提案する最初の解決策は、SaveChanges()を複数回呼び出すことです。これにより、SaveChanges()を呼び出すたびに、コンテキストが変更を忘れてしまい、RollBackが不可能になります。

私が提案する2番目の解決策は、Rollback機能を提供することです。TransactionScopeを使用してSaveChanges(false)(falseはオブジェクトコンテキストに変更を記憶させ、ロールバックを可能にします)またはsavechanges()と保存オプション(新しい方法)を使用します。 )。

例えば

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();
}
于 2013-02-12T16:32:56.973 に答える