web-dev-qa-db-ja.com

データアクセス層のデザインパターン

宿題だと感じるかもしれませんが、ごめんなさい。検索しましたが、適切な答えが見つかりませんでした。

だから私の質問は:

私にはいくつかのクラスがあり、各クラスには保存するメソッドがあります。そこで、データベース処理用に別のクラスを作成しました。

namespace HospitalMgt.Data
{
    public static class DBConnection
    {
        public static string constr = "Data Source=ABD;Initial Catalog=HospitalMgt;User Id=sa;Password=123";
        public static SqlConnection con;
      //  public static SqlCommand com;

        public static SqlConnection OpenConnection()
        {
            con= new SqlConnection(constr);
            con.Open();
            return con;
        }

    }
}

ただし、すべてのクラスをDBConnectionクラスで実装するのは適切ではないと思います。

私の質問:

  1. この問題を克服するのに適したデザインパターンは何ですか?
  2. DBConnectionをクラスとして作成することをお勧めしますか? (またはそれはインターフェースである必要があります)

Factoryメソッドを使用したDAレイヤーに関する記事をいくつか見つけましたが、私の知識によれば、そのパターンは私の状況に適していません。

12
DevT

通常、既存のフレームワークを使用できない場合は、リポジトリパターンとアクティブパターンの両方を使用します。

簡単にするために、リポジトリパターンのみを使用できます。私は通常、次のように定義します。

//  Define a generic repository interface
public interface IRepository<Key, E> where E:IEntity<Key>>{
    void Add(E entity);
    void AddRange(IEnumerable<E> entities);
    IEntity<Key> Get(Key key);
    IEnumerable<E> GetRange(IEnumerable<Key> keys);
    IEnumerable<E> GetAll();
    //  ..., Update, Delete methods
}

//  Create an abstract class that will encapsulate the generic code
public abstract class Repository<K, E> where E:IEntity<K>>:IRepository<K, E>{

    protected Repository(/*parameter you may need to implement the generic methods, like a ConnectionFactory,  table name, entity type for casts, etc */){}

    public override void Insert(IEntity<Key> entity){
        //  do the insert, treat exceptions accordingly and encapsulate them in your own and more concise Exceptions, etc
    }
    //  ...
}

//  Create the entities classes, one for each table, that will represent a row of that table
public class Car: IEntity<String>{/* Properties */}

//  Create a specific repository for each table
//  If the table have a composed key, just create a class representing it
public CarRepository: Repository<String, Car>{

    public CarRepository(){/* pass the base parameters */}

    // offer here your specific operations to this table entity
    public IEnumerable<Car> GetByOwner(PersonKey ownerKey){
        //  do stuff
    }
}

これで、データベースを操作するのに十分なツールができましたが、必要に応じて、アクティブパターンを使用できます。簡単な例:

public class Person:IEntity<PersonKey>{
    public PersonKey Key{get;}
    public IEnumerable<Car> OwnedCars{
        get{
            CarRepository rep = DBSingletons.Cars;
            return rep.GetByOwner(this.Key);
        }
        set{
            //  do stuff
        }
    }
}

明らかに、独自の実装を行うときは、特に異なるエンティティリポジトリ間で、トランザクションをうまく利用するスレッドセーフを考慮する必要があります。

//  simple example
ITransaction t = TransactionFactory.GetNewTransaction();
t.begin();
try{
    //  create person entity
    personRepository.Add(person, t);
    //  create cars assigned to person
    carRepository.AddRange(cars, t);
    t.commit();
}catch(Exception){
    t.rollback();
}

特に最も一般的なソリューションを開発しようとすると、非常に複雑になる可能性があるため、本当に独自のDALを作成したいことを確認してください。

13
João Simões

ORM、Entity Framework、またはNHibernateを使用するとうまくいくことをお勧めします。そうすれば、dbコンテキストについて心配したり、SQLステートメントを作成したりする必要はありません。

5

まず、記事 Jeremy Millerによるデータ永続性のデザインパターン をお勧めします。

いくつかのデータアクセス層パターンがあります。

  1. アクティブレコードパターンwiki詳細情報 )。
  2. リポジトリパターン詳細情報 )。
5
Sergey Brunov

このすべての一般的な操作には、RepositoryBaseを使用することをお勧めします。データアクセスにORMを使用する場合は、ジェネリック型リポジトリに基づくリポジトリの実装を検討することをお勧めします。

これはそれについての良い記事です:

http://lostechies.com/jimmybogard/2009/09/03/ddd-repository-implementation-patterns/

2

それは古すぎますが、ちょうどこの質問に出くわし、私の考えを投稿することに抵抗できませんでした。

Repository with nitOfWork with some descentORMが良いアプローチであることがわかりました。これにより、ほとんどの問題が最小限に抑えられます。

上記のリンクで言及されているUoWは、リポジトリに挿入できます。これにより、使用の柔軟性が高まります。また、すべてのDB通信コードは1か所に一元化されています。例は完全ではありませんが、スタートアップポイントです。

上記のリンクに記載されているリポジトリパターンは、実際には一般的な基本クラスです。派生する具象リポジトリごとに新しいクラスを作成できます。

ジェネリックリポジトリはアンチパターンと見なされます。それを説明するインターネット上の記事はたくさんあります。

ジェネリックリポジトリがアンチパターンである理由

  1. リポジトリはモデル化されているドメインの一部であり、そのドメインはジェネリックではありません。
    • すべてのエンティティを削除できるわけではありません。
    • すべてのエンティティを追加できるわけではありません
    • すべてのエンティティにリポジトリがあるわけではありません。
    • クエリは大きく異なります。リポジトリAPIは、エンティティ自体と同じくらい一意になります。
    • GetById()の場合、識別子のタイプは異なる場合があります。
    • 特定のフィールド(DML)を更新することはできません。
  2. 一般的なクエリメカニズムはORMの責任です。
    • ほとんどのORMは、GenericRepositoryによく似た実装を公開しています。
    • リポジトリは、ORMによって公開されている汎用クエリメカニズムを使用して、エンティティのSPECIFICクエリを実装する必要があります。
  3. 複合キーを操作することはできません。
  4. とにかくサービスのDALロジックをリークします。
    • パラメータとして受け入れる場合の述語基準は、サービス層から提供する必要があります。これがORM固有のクラスである場合、ORMをサービスにリークします。

これらを読むことをお勧めします( 12 、、 45 )ジェネリックリポジトリがアンチパターンである理由を説明する記事。

解決策:

  1. 具体的なリポジトリにラップされた抽象的な汎用リポジトリを作成します。このようにして、パブリックインターフェイスを制御できますが、汎用リポジトリからのコード再利用の利点があります。
  2. ジェネリックリポジトリを使用しますが、継承の代わりにコンポジションを使用し、コントラクトとしてドメインに公開しないでください。

いずれの場合も、ジェネリックリポジトリを呼び出し元のコードに公開しないでください。また、具体的なリポジトリからIQueryableを公開しないでください。

1
Amit Joshi