62

と を組み合わせることは、現在かなり広く使用されているものですUnit of WorkRepository PatternMartin Fowlerが言うように、使用の目的は、リポジトリが実際にどのように機能するかを知らずに (永続的に無知である) 、ビジネス トランザクションUoWを形成することです。私は多くの実装を見直しました。特定の詳細 (具象/抽象クラス、インターフェイスなど) を無視すると、次のようになります。

public class RepositoryBase<T>
{
    private UoW _uow;
    public RepositoryBase(UoW uow) // injecting UoW instance via constructor
    {
       _uow = uow;
    }
    public void Add(T entity)
    {
       // Add logic here
    }
    // +other CRUD methods
}

public class UoW
{
    // Holding one repository per domain entity

    public RepositoryBase<Order> OrderRep { get; set; }
    public RepositoryBase<Customer> CustomerRep { get; set; }
    // +other repositories

    public void Commit()
    {
       // Psedudo code: 
       For all the contained repositories do:
           store repository changes.
    }
}

今私の問題:

UoWCommit変更を保存する public メソッドを公開します。また、各リポジトリには の共有インスタンスがあるためUoW、それぞれがUoW 上のRepositoryメソッドにアクセスできます。Commit1 つのリポジトリから呼び出すと、他のすべてのリポジトリにも変更が保存されます。したがって、結果として、トランザクションの概念全体が崩壊します。

class Repository<T> : RepositoryBase<T>
{
    private UoW _uow;
    public void SomeMethod()
    {
        // some processing or data manipulations here
        _uow.Commit(); // makes other repositories also save their changes
    }
}

これは許してはいけないと思います。UoW(ビジネス トランザクション)の目的を考慮して、メソッドは、ビジネス レイヤーなどのビジネス トランザクションCommitを開始した人にのみ公開する必要があります。私が驚いたのは、この問題に対処する記事が見つからなかったことです。それらのすべてで、注入されるレポから呼び出すことができます。Commit

PS:Commit開発者に電話をかけないように言うことはできますRepositoryが、信頼できるアーキテクチャは信頼できる開発者よりも信頼できます。

4

8 に答える 8

31

私はあなたの懸念に同意します。私は周囲の作業単位を持つことを好みます。作業単位を開く最も外側の関数は、コミットするか中止するかを決定する関数です。呼び出された関数は、アンビエント UoW がある場合は自動的にアンビエント UoW に登録するか、ない場合は新しい UoW を作成する作業単位スコープを開くことができます。

私が使用した の実装は、UnitOfWorkScopeどのように機能するかに大きく影響を受けていますTransactionScope。アンビエント/スコープ アプローチを使用すると、依存性注入の必要もなくなります。

クエリを実行するメソッドは次のようになります。

public static Entities.Car GetCar(int id)
{
    using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Reading))
    {
        return uow.DbContext.Cars.Single(c => c.CarId == id);
    }
}

書き込むメソッドは次のようになります。

using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Writing))
{
    Car c = SharedQueries.GetCar(carId);
    c.Color = "White";
    uow.SaveChanges();
}

uow.SaveChanges()これがルート (最も遠い) スコープである場合、呼び出しはデータベースへの実際の保存のみを行うことに注意してください。それ以外の場合は、ルート スコープが変更を保存できる「賛成票」と解釈されます。

の完全な実装は、 http UnitOfWorkScope: //coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/で入手できます。

于 2013-10-28T09:07:16.167 に答える
13

リポジトリを UoW のメンバーにします。リポジトリが UoW を「認識」しないようにします。UoW にトランザクションを処理させます。

于 2014-07-16T21:53:39.880 に答える
2

.NET では、通常、データ アクセス コンポーネントは自動的にアンビエント トランザクションに参加します。したがって、変更をトランザクション内で保存することは、変更を永続化するためにトランザクションをコミットすることから分離されます。

別の言い方をすれば、トランザクション スコープを作成すると、開発者が必要なだけ保存できるようになります。トランザクションがコミットされるまで、データベースの観察可能な状態は更新されません (観察可能な状態は、トランザクションの分離レベルによって異なります)。

これは、C# でトランザクション スコープを作成する方法を示しています。

using (TransactionScope scope = new TransactionScope())
{
    // Your logic here. Save inside the transaction as much as you want.

    scope.Complete(); // <-- This will complete the transaction and make the changes permanent.
}
于 2013-10-23T17:49:16.403 に答える
2

私も最近この設計パターンを調査しており、Unit Of Work と Generic Repository パターンを利用することで、Repository 実装の "Save Changes" という作業単位を抽出することができました。私のコードは次のとおりです。

public class GenericRepository<T> where T : class
{
  private MyDatabase _Context;
  private DbSet<T> dbset;

  public GenericRepository(MyDatabase context)
  {
    _Context = context;
    dbSet = context.Set<T>();
  }

  public T Get(int id)
  {
    return dbSet.Find(id);
  }

  public IEnumerable<T> GetAll()
  {
    return dbSet<T>.ToList();
  }

  public IEnumerable<T> Where(Expression<Func<T>, bool>> predicate)
  {
    return dbSet.Where(predicate);
  }
  ...
  ...
}

基本的には、データ コンテキストを渡し、エンティティ フレームワークの dbSet メソッドを使用して基本的な Get、GetAll、Add、AddRange、Remove、RemoveRange、および Where を実行するだけです。

次に、これらのメソッドを公開するジェネリック インターフェイスを作成します。

public interface <IGenericRepository<T> where T : class
{
  T Get(int id);
  IEnumerable<T> GetAll();
  IEnumerabel<T> Where(Expression<Func<T, bool>> predicate);
  ...
  ...
}

ここで、エンティティ フレームワーク内のエンティティごとにインターフェイスを作成し、IGenericRepository から継承して、インターフェイスが継承されたリポジトリ内に実装されたメソッド シグネチャを持つことを期待できるようにします。

例:

public interface ITable1 : IGenericRepository<table1>
{
}

すべてのエンティティでこの同じパターンに従います。また、エンティティに固有の関数シグネチャをこれらのインターフェイスに追加します。これにより、リポジトリは GenericRepository メソッドとインターフェイスで定義されたカスタム メソッドを実装する必要があります。

リポジトリについては、このように実装します。

public class Table1Repository : GenericRepository<table1>, ITable1
{
  private MyDatabase _context;

  public Table1Repository(MyDatabase context) : base(context)
  {
    _context = context;
  }
} 

上記のリポジトリの例では、table1 リポジトリを作成し、「table1」タイプの GenericRepository を継承してから、ITable1 インターフェイスから継承しています。これにより、一般的な dbSet メソッドが自動的に実装されるため、カスタム リポジトリ メソッドがあればそれのみに集中できます。dbContext をコンストラクターに渡すと、dbContext もベースの Generic Repository に渡す必要があります。

ここから、Unit of Work リポジトリーとインターフェースを作成します。

public interface IUnitOfWork
{
  ITable1 table1 {get;}
  ...
  ...
  list all other repository interfaces here.

  void SaveChanges();
} 

public class UnitOfWork : IUnitOfWork
{
  private readonly MyDatabase _context;
  public ITable1 Table1 {get; private set;}

  public UnitOfWork(MyDatabase context)
  {
    _context = context; 

    // Initialize all of your repositories here
    Table1 = new Table1Repository(_context);
    ...
    ...
  }

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

システム内の他のすべてのコントローラーが継承するカスタム コントローラーでトランザクション スコープを処理します。このコントローラーは、デフォルトの MVC コントローラーを継承します。

public class DefaultController : Controller
{
  protected IUnitOfWork UoW;

  protected override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    UoW = new UnitOfWork(new MyDatabase());
  }

  protected override void OnActionExecuted(ActionExecutedContext filterContext) 
  {
    UoW.SaveChanges();
  }
}

このようにコードを実装することによって。アクションの開始時にサーバーにリクエストが送信されるたびに、新しい UnitOfWork が作成され、すべてのリポジトリが自動的に作成され、コントローラーまたはクラスの UoW 変数からアクセスできるようになります。これにより、リポジトリから SaveChanges() も削除され、UnitOfWork リポジトリ内に配置されます。最後に、このパターンは、依存性注入を介してシステム全体で単一の dbContext のみを利用できます。

単一のコンテキストでの親/子の更新が懸念される場合は、ストアド プロシージャを更新、挿入、および削除関数に利用し、エンティティ フレームワークをアクセス メソッドに利用できます。

于 2016-09-12T11:11:27.957 に答える
0

はい、この質問は私にとって懸念事項です。これが私がそれを処理する方法です。

まず第一に、私の理解では、Domain Model は Unit of Work について知っているべきではありません。ドメイン モデルは、トランザクション ストレージの存在を意味しないインターフェイス (または抽象クラス) で構成されます。実際、ストレージの存在についてはまったく知りませんしたがって、ドメインモデルという用語。

作業単位は、ドメイン モデルの実装レイヤーに存在します。これは私の用語で、Data Access Layer を組み込んで Domain Model インターフェイスを実装するレイヤーを意味します。通常、私は ORM を DAL として使用するため、組み込みの UoW が付属しています (保留中の変更をコミットするための Entity Framework の SaveChanges または SubmitChanges メソッド)。しかし、それは DAL に属し、発明者の魔法は必要ありません。

一方、「DALへの変更のコミット」の部分を抽象化する必要があるため、ドメインモデル実装レイヤーに必要なUoWを参照しています。そのためには、Anders Abel のソリューション (再帰的スロープ) を使用します。

  • 集約がスコープのイニシエーターである場合は、集約を 1 つのトランザクションとして保存することをサポートする必要があります。
  • 集約がスコープのイニシエーターではなく、スコープの一部である場合は、トランザクションの一部として集約の保存をサポートする必要があります。
于 2013-10-30T21:03:14.020 に答える