web-dev-qa-db-ja.com

汎用リポジトリー・パターン+ EFおよび作業単位

私はこのリポジトリパターンに不慣れで、インターネット全体で多くのリポジトリパターン+ UoW実装を見てきましたが、どちらが正しいかについて結論を出すことができません。多くのリンクを通過した後、私は1つを実装することができました。

次の点を考慮してください

  • SOLID原則を満たす必要があります
  • テスト可能であること
  • フレームワークから独立している
  • DBから独立する

ここに実装のコードがあります

ジェネリックIRepository

 public interface IRepository<T> where T : class
    {

        void Add(T entity);

        void Update(T entity);

        void Delete(T entity);

        T GetByKey(object id);

    }

RepositoryBase

public abstract class RepositoryBase<D, T> : IRepository<T> where T : class where D : BaseDbContext
{


    private D dataContext;
    private readonly IDbSet<T> dbSet;

    protected IDbFactory<D> DbFactory
    {
        get;
        private set;
    }

    protected D DbContext
    {
        get { return dataContext ?? (dataContext = DbFactory.Init()); }
    }


    protected RepositoryBase(IDbFactory<D> dbFactory)
    {

        DbFactory = dbFactory;
        dbSet = DbContext.Set<T>();

    }


    #region Implementation
    public virtual void Add(T entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Update(T entity)
    {
        dbSet.Attach(entity);
        DbContext.Entry(entity).State = EntityState.Modified;
    }

    public virtual void Delete(T entity)
    {
        dbSet.Remove(entity);
    }


    public T GetByKey(object id)
    {
        return dbSet.Find(id);
    }


    #endregion

}

IUnitofWork、UnitOfWork

public interface IUnitOfWork<D> where D : BaseDbContext
    {
        void Commit();
    }



public class UnitOfWork<D> : IUnitOfWork<D> where D : BaseDbContext, new()
{
    private readonly IDbFactory<D> dbFactory;
    private D dbContext;

    public UnitOfWork(IDbFactory<D> dbFactory)
    {
        this.dbFactory = dbFactory;
    }

    public D DbContext
    {
        get { return dbContext ?? (dbContext = dbFactory.Init()); }
    }

    public void Commit()
    {
        DbContext.SaveChanges();
    }
}

IDBFactory、DBFactory

public interface IDbFactory<D> where D : BaseDbContext
{
    D Init();
}




public class DbFactory<D> : Disposable, IDbFactory<D> where D : BaseDbContext, new()
        {
            D dbContext;
            public D Init()
            {
                return dbContext ?? (dbContext = new D());
            }
            protected override void DisposeCore()
            {
                if (dbContext != null)
                    dbContext.Dispose();
            }
        }

BaseDbContext

public abstract class BaseDbContext : DbContext
{
public BaseDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
        {

        }

}

ProjectDbContext

 public partial class ProjectDbContext : BaseDbContext
    {
        public ProjectDbContext()
            : base("name=ProjectDbContext")
        {
            Database.SetInitializer<ProjectDbContext>(null);
        }


    }

使用例

コントローラ

 public class StudentsController : BaseController
    {

        private IStudentBusiness objstudentbusiness;
        public StudentsController(IStudentBusiness rstudentbusiness)
        {
            objstudentbusiness = rstudentbusiness;
        }


        public JsonResult LoadStudents()
        {

                var data = objstudentbusiness.ListStudents();
                var jsonResult = Json(data, JsonRequestBehavior.AllowGet);
                return jsonResult;

        }

    }

IStudentBAL、StudentBAL

 public interface IStudentBAL
    {
        void SaveStudent(StudentDto student);
        List<StudentDto> ListStudents();
    }


public class StudentBAL : BusinessBase, IStudentBAL
{

    private readonly IStudentRepository objStudentRepository;
    private readonly IUnitOfWork<ProjectDbContext> objIUnitOfWork;

    public StudentBAL(IStudentRepository rIStudentRepository, IUnitOfWork<ProjectDbContext> rIUnitOfWork)
    {
        try
        {
            objStudentRepository = rIStudentRepository;
            objIUnitOfWork = rIUnitOfWork;
        }
        catch (Exception ex)
        {

            Log.Error(ex);
        }

    }

    public List<StudentDto> ListStudents()
    {
        try
        {
            var tusrs = objStudentRepository.ListStudents() ?? new List<StudentDto>();
            return tusrs;
        }
        catch (Exception ex)
        {
            Log.Error(ex);

        }
        return new List<StudentDto>();
    }
}

IStudentRepository、StudentRepository

 public interface IStudentRepository
    {
        void SaveStudent(Student Student);
        StudentDto GetStudentByName(StudentDto Studentname);
        Student GetStudentByID(int Studentid);
        List<StudentDto> ListStudents();
    }

public class StudentRepository : RepositoryBase<ProjectDbContext, Student>, IStudentRepository
{
    public StudentRepository(IDbFactory<ProjectDbContext> dbFactory) : base(dbFactory)
    {
    }
    public List<StudentDto> ListStudents()
    {

            var students = (from t in DbContext.Students

                        select new StudentDto
                        {
                           // all the required properties
                        }).ToList();


            return students;

    }
}
  • 依存性注入はAutoFacを使用して行われます
  • ログのコードは省略されます

これは良い実装のように思えるか、何か不足していますか?

正確性、効率、および提案に関して提供できる私の実装に関するフィードバックをいただければ幸いです。だからここに私の質問があります

  • これは疎結合ですか?
  • それは何か漏れやすい抽象化を持っていますか、そしてなぜですか?
  • EFからMySQL dbに切り替えるには何を行う必要がありますか。また、変更を実装するためにどれくらいの労力が必要ですか
  • このパターンは、SOLID princples、LawまたはDemeter、またはオブジェクト指向の法則のいずれかを違反していますか?
  • このパターンには、不要なコードの冗長性がありますか?
  • EFを使用するこのアーキテクチャは、100以上のドメインエンティティを含むプロジェクトと、各エンティティが少なくとも10を超えるフィールドを持つプロジェクトでどのように拡張するか。後でメンテナンスの悪夢になるのでしょうか?

-すべての批判は大歓迎です!!

7
Codebadger

SOLID原則を満たす必要があります

称賛に値する目標ですが、ソフトウェアの機能要件と非機能要件のいずれかを満たすのに役立ちますか?

SOLID原則の目的は、より良いコードを書くのを助けることであり、それ自体が目標、要件、または指標になることではありません。もしあなたがプロジェクトの測定に時間を費やしているならコードのSOLID原則への厳密な準拠を観察し、分類することにより、進捗状況を確認します。間違っています。

テスト可能であること

称賛に値する目標でもあります。しかし、率直に言って、このコードのほとんどは単なる配管であり、単体テストの観点からは特に興味深いものではありません。コードを単純化することでテストが容易になり、テストのほとんどはとにかく統合テストになります。

フレームワーク/データベースから独立している

この要件の値は、後でフレームワークを変更することがわかっている場合を除き、議論の余地があります。すでにお気づきのとおり、この種の柔軟性を実現するには、実装を分離するために多くの規律が必要であり、このレベルの厳密さはコストに見合わない場合があります。

ソフトウェアプロジェクトの大部分は、フレームワークやDBの決定を変更することはありません。公平ではありますが、私の最後の仕事で、ORMは2〜3回交換されました(データベースは変更されませんでした。プロジェクトの存続期間中、SQL Serverのままでした)。

依存性注入はAutoFacを使用します

必要に応じて後で変更できるように、DIコンテナにInterfaceを含めましたか?この可能性は、フレームワークまたはDBを変更するよりも可能性が高いことに注意してください。

ロギングコードは省略されています

必要に応じて後で変更できるように、ロギング実装にInterfaceを含めましたか?この可能性は、フレームワークまたはDBを変更するよりも可能性が高いことに注意してください。

伐採の横断的な影響を検討しましたか?すべてのクラスがILoggerインターフェースへの参照を受け取りますか、それとも例外処理戦略を採用しましたか?

これは疎結合ですか?

はいといいえ。インターフェイスをどこで使用しましたか?はい。基本クラスを継承したり、具体的な参照を取得したりした場所はどこですか?いいえ。RepositoryBaseは、プロバイダー固有の汎用リポジトリーを実装する場所だと思います。 DB/ORMを変更するときに書き直してもかまわない限り、これは完全に有効な手法です。

それは漏れやすい抽象化を持っていますか、そしてなぜですか?

EF自体が漏れやすい抽象化です。 BaseRepositoryの実装でEFとの間でやり取りするエンティティには、あらゆる種類のリークが含まれますが、それを回避する唯一の方法は、EFエンティティにマップする独自のエンティティセットを提供して、それを分離することです。結果として生じる変更追跡問題の管理は複雑になります。それはおそらく問題を起こす価値はありません。

EFからMySQL dbに切り替えるには何を行う必要がありますか。また、変更を実装するためにどれくらいの労力が必要ですか

EFとMySQLは比較できません。 EFは、オブジェクトリレーショナルマッパーおよび作業単位の実装です。 MySQLはリレーショナルデータベースシステムです。 MySQLはEFではなく、SQL Serverの代替です。 EFは通常、SQLサーバーと組み合わせて使用​​されますが、プロバイダーは他のデータベースでも使用できるため、オプションがあります。

ただし、データベースに完全に依存しないようにするには、ANSI SQLに準拠する必要があります。つまり、SQL固有のパフォーマンスの向上など、選択したデータベースのベンダー固有の機能をすべて犠牲にすることになります。おそらくストアドプロシージャを完全に避けたいでしょう。

このパターンは、SOLIDの原則、法則、デメテル、またはオブジェクト指向法則のいずれかを破っていますか?

私はこれに基づいてあなたのコードを評価しませんでした。設計目標に基づいて、設計アプローチ(およびコード)が合理的に聞こえると言うだけで十分です。

このパターンには、不要なコードの冗長性がありますか?

あなたの設計要件に基づいて、私が見ることはできません。

EFを使用したこのアーキテクチャは、100以上のドメインエンティティを含むプロジェクトと、各エンティティが少なくとも10を超えるフィールドを持つプロジェクトでどのように拡張するか。後でメンテナンスの悪夢になるのでしょうか?

私はこの多くのエンティティでEFをうまく使用しました。 EFは、データベースファーストの設計を行うことを前提として、データベースから必要なエンティティークラスを作成するのに役立ちます。複数のソフトウェア開発者がいる環境(および複数の環境(開発、ステージング、本番))で物事をまっすぐに保つのは難しい場合があります。注意しないと、フィールドのnull可能性が1つでも下がると、運用システム全体がダウンする可能性があります。完全に有効なC#であるため、コンパイラーはそれをキャッチできません。

他のいくつかの考え

投稿したコードはCRUD操作のみをサポートしています。アプリケーションの80%はCreate、Read、Update、およびDeleteメソッドを使用するだけで問題なく機能する可能性がありますが、他の20%は、適切に実行され、ユースケースに適していると予想される場合、カスタムSQLを必要とします。この可能性を説明しましたか?

EFやnHibernateなどのヘビー級の1つではなく、DapperのようなマイクロORMの使用を検討してください。 Dapperは、必要なすべてのCRUD操作を提供し、カスタムSQLが示されている場合にEFのExecute SQLメソッドと実質的に同じであり、そのままの状態でEFよりもはるかに優れたパフォーマンスを持つExecute SQLメソッドを提供します。

8
Robert Harvey