2016-08-09 4 views
2

私はアプリケーションに最小限の汎用リポジトリパターンを実装しようとしています。Entity Frameworkを使用した最小限のリポジトリ実装

public interface IRepository 
{ 
    IQueryable<TEntity> Query<TEntity>() 
     where TEntity: BaseEntity; 

    void Save<TEntity>(TEntity entity) 
     where TEntity : BaseEntity; 
} 

BaseEntityが、私は私のリポジトリに保存するすべてのオブジェクトの基本クラスです:私は、データを照会し、保存するための本当に小さなインタフェースを持って、私は作業の実装を見つけるためにしようとしていた

public abstract class BaseEntity 
{ 
    public Guid Id { get; set; }  
    public DateTime CreatedDate { get; set; } 
    public DateTime UpdatedDate { get; set; } 
} 

そのような単純なリポジトリのEntity Frameworkを使用していましたが、驚くほど見つけにくかった(人々はUnitOfWorkなどを使用しています。

は、だから私は、私が思い付くことができ、絶対に最小限の実装を作成しました。このような実装が正しいかどう

public class EfRepository : DbContext, IRepository 
{ 
    public IQueryable<TEntity> Query<TEntity>() where TEntity : BaseEntity 
    { 
     return this.Set<TEntity>(); 
    } 

    public void Save<TEntity>(TEntity entity) where TEntity : BaseEntity 
    { 
     if (entity.Id == default(Guid)) 
     { 
      entity.Id = Guid.NewGuid(); 
      this.Set<TEntity>().Add(entity); 
     } 
     else 
     { 
      this.Entry(entity).State = EntityState.Modified; 
     }  

     this.SaveChanges(); 
    } 

    public DbSet<User> Users { get; set; } // User is a subclass of BaseEntity 
    //Other DbSet's... 
} 

は今、私の質問です。私はEntity Frameworkの初心者で、パフォーマンスの問題や、そのようなリポジトリの使用中に間違っている可能性があることを心配しています。

注:私は2つの理由のために、このすべてをやろうとしている:私は私のユニットテストプロジェクトにリポジトリのモックを作成できるように、テストのため

  • それは私が可能です将来別のORMに切り替える必要があります。この移行をできるだけ簡単にしたいと考えています。
+0

軽いタッチでパフォーマンス上の懸念がある場合に、EFが使用するORMが適切かどうか検討する価値はあります。個人的な経験から私はEFが私が使っているプロジェクトではそれほどパフォーマンスが良いとは思っていませんでしたが、DBやコードに合わせてマッピングを維持するのはちょっとしたことです(選択するパス、コード、最初にDB)。 誰かが、ハイスループット環境で誰が働いているかは、誰でもEFが言及されるたびに肉体的に強くなることを知っています。 –

+5

質問は本当にです:EF *は既にリポジトリ( 'DbSet ')と作業単位( 'DbContext')パターンを実装しています。 –

+0

@marc_s、私は2つの理由があります:)私の編集を参照してください –

答えて

2

まず第一に、すべてのリポジトリは議論の余地があります。いろいろな理由で、強く反対する人がたくさんいますし、使用している人もいます(またはそれに慣れています)。インターネット上で賛否両論について絶えず話し合っている記事がたくさんあります。プロジェクトで実際にリポジトリパターンが必要かどうかは、あなたが決定することです。「C#でそれを行う方法」を尋ねたときに焦点を当てましょう。 "私はそれをすべきか?"

あなたのリポジトリの実装はDbContextです。つまり、複数のリポジトリ(2つ以上のエンティティタイプ)にまたがるトランザクションを効果的に作成することはできません。各リポジトリにはそれ自体がDbContext(コンテキストのまま)であるためです。フードの下でDbContextエンティティに加えられた変更を追跡します。複数のコンテキストがあり、同時に両方を保存しようとすると、互いに知りません。これで問題が残る - SaveChanges()の最初の呼び出しが成功し、2番目の呼び出しが最初の呼び出しをロールバックする方法が失敗した場合、問題が残る?それはすでに救われましたか?そこに作業単位が現れます。

だから最初は、リポジトリのインターフェースを必要とする - エンティティのコレクションとして機能:

public interface IRepository<TEntity> 
{ 
    TEntity Get(Expression<Func<TEntity, bool>> predicate); 
    IEnumerable<TEntity> GetAll(); 
    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate); 

    void Add(TEntity entity); 
    void AddAll(IEnumerable<TEntity> entities); 

    void Remove(TEntity entity); 
    void RemoveAll(IEnumerable<TEntity> entities); 
} 

、作業の単位:次のように

public interface IUnitOfWork : IDisposable 
{ 
    // Commit all the changes 
    void Complete(); 

    // Concrete implementation -> IRepository<Foo> 
    // Add all your repositories here: 
    IFooRepository Foos {get;} 
} 

基本クラスになります:

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class 
{ 
    protected DbContext Context { get; private set; } 

    public BaseRepository(DbContext dbContext) 
    { 
     Context = dbContext; 
    } 

    public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate) 
    { 
     return Context.Set<TEntity>().Where(predicate).FirstOrDefault(); 
    } 

    public virtual IEnumerable<TEntity> GetAll() 
    { 
     return Context.Set<TEntity>().ToList(); 
    } 

    public virtual IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate) 
    { 
     return Context.Set<TEntity>().Where(predicate).ToList(); 
    } 

    public void Add(TEntity entity) 
    { 
     var entry = Context.Entry(entity); 
     if(entry.State == EntityState.Detached) 
     { 
      Context.Set<TEntity>().Add(entity); 
     } 
     else 
     { 
      entry.State = EntityState.Modified; 
     } 
    } 

    public void AddAll(IEnumerable<TEntity> entities) 
    { 
     foreach(var entity in entities) 
     { 
      Add(entity); 
     } 
    } 

    public void Remove(TEntity entity) 
    { 
     var entry = Context.Entry(entity); 
     if (entry.State == EntityState.Detached) 
     { 
      Context.Set<TEntity>().Attach(entity); 
     } 
     Context.Entry<TEntity>(entity).State = EntityState.Deleted; 
    } 

    public void RemoveAll(IEnumerable<TEntity> entities) 
    { 
     foreach (var entity in entities) 
     { 
      Remove(entity); 
     } 
    } 

} 

および作業ユニットの実装:

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly ApplicationDbContext _dbContext; 
    private IFooRepository _fooRepo; 

    public UnitOfWork(ApplicationDbContext dbContext) 
    { 
     _dbContext = dbContext; 

     // Each repo will share the db context: 
     _fooRepo = new FooRepository(_dbContext); 
    } 


    public IFooRepository Foos 
    { 
     get 
     { 
      return _fooRepo; 
     } 
    } 

    public void Complete() 
    { 
     _dbContext.SaveChanges(); 
    } 

    public void Dispose() 
    { 
     _dbContext.Dispose(); 
    } 
} 
+0

_複数のリポジトリにまたがるトランザクションを効率的に作成できません_これは完全ではありません本当。私は 'TransactionScope'を作成し、それを必要なリポジトリ呼び出しの周りにラップすることができます。デフォルトでは(トランザクションスコープなし)、リポジトリの実装は 'Save'の呼び出しごとにすべての変更をコミットします。このデフォルトの動作はほとんどの場合に必要なものです。 –

+0

'Save()'は、どのリポジトリを呼び出すかにかかわらず、同じ 'DbContext'を使ってすべてのリポジトリからすべての変更されたエンティティを奪います。 –

関連する問題