6

複数のデータソースを処理するための単一の作業単位(エンティティフレームワーク)の実装に関するオプションを考えています。私は暫定的なアプローチを思いついた-今のところ単一のコンテキストを扱っている-しかしそれは明らかに良い考えではない。

以下のコードを分析する場合、それは悪い実装だと思いますか?トランザクションスコープの存続期間は潜在的な問題ですか?

もちろん、トランザクションスコープを異なるコンテキストでラップすると、2番目のcontext.SaveChanges()が失敗した場合にカバーされます...

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Transactions;

    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                using(UnitOfWork unitOfWork = new UnitOfWork())
                {

                    var repository = new EmployeeRepository(unitOfWork);

                    var employee = repository.CreateOrGetEmployee("Whatever Name");

                    Console.Write(employee.Id);

                    unitOfWork.SaveChanges();
                }
            }
        }

        class UnitOfWork : IDisposable
        {
            TestEntities _context;
            TransactionScope _scope;
            public UnitOfWork()
            {
                _scope = new TransactionScope();
                _context = new TestEntities();
            }

            public void SaveChanges()
            {
                _context.SaveChanges();
                _scope.Complete();
            }

            public TestEntities Context
            {
                get
                {
                    return _context;
                }
            }

            public void Dispose()
            {
                _scope.Dispose();
                _context.Dispose();
            }
        }

        class EmployeeRepository
        {
            UnitOfWork _unitOfWork;

            public EmployeeRepository(UnitOfWork unitOfWork)
            {
                _unitOfWork = unitOfWork;
            }

            public Employee GetEmployeeById(int employeeId)
            {
                return _unitOfWork.Context.Employees.SingleOrDefault(e => e.Id == employeeId);
            }

            public Employee CreateEmployee(string fullName)
            {
                Employee employee = new Employee();
                employee.FullName = fullName;
                _unitOfWork.Context.SaveChanges();
                return employee;
            }

            public Employee CreateOrGetEmployee(string fullName)
            {
                var employee = _unitOfWork.Context.Employees.FirstOrDefault(e => e.FullName == fullName);
                if (employee == null)
                {
                    employee = new Employee();
                    employee.FullName = fullName;
                    this.AddEmployee(employee);
                }
                return employee;
            }

            public Employee AddEmployee(Employee employee)
            {
                _unitOfWork.Context.Employees.AddObject(employee);
                _unitOfWork.Context.SaveChanges();
                return employee;
            }
        }
    }
4

1 に答える 1

7

なぜTransactionScopeコンストラクターから始めるのですか?変更を保存するためにのみ必要です。

public void SaveChanges()
{
    // SaveChanges also uses transaction which uses by default ReadCommitted isolation
    // level but TransactionScope uses by default more restrictive Serializable isolation
    // level 
    using (var scope = new TransactionScope(TransactionScopeOption.Required,
                                            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        _context.SaveChanges();
        scope.Complete();
    }
}

より多くのコンテキストで作業単位を作成したい場合は、それらすべてのコンテキストを同じ作業単位クラスにラップするだけです。あなたSaveChangesはもう少し複雑になります:

public void SaveChanges()
{
    using (var scope = new TransactionScope(TransactionScopeOption.Required,
                                            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        _contextA.SaveChanges(SaveOptions.DetectChangesBeforeSave);
        _contextB.SaveChanges(SaveOptions.DetectChangesBeforeSave);
        scope.Complete();
        _contextA.AcceptAllChanges();
        _contextB.AcceptAllChanges(); 
    }
}

このバージョンでは、保存操作とコンテキストの内部状態のリセットが分離されています。その理由は、最初のコンテキストが変更を正常に保存したが、2番目のコンテキストが例外を発生させた場合、トランザクションはロールバックされるためです。そのため、最初のコンテキストですべての変更が受け入れられたものとしてすでにクリアされていることは望ましくありません(実行された変更に関する情報が失われ、それらを再度保存することはできません)。

于 2012-05-24T08:17:06.397 に答える