webentwicklung-frage-antwort-db.com.de

Entwurfsmuster für die Datenzugriffsebene

Sie könnten das Gefühl haben, dass dies Hausaufgaben sind, das tut mir leid. Ich habe gesucht, aber keine richtige Antwort gefunden.

Meine Frage lautet also:

Ich habe mehrere Klassen und jede Klasse hat eine Methode zum Speichern. Also habe ich eine eigene Klasse für das Datenbankhandling erstellt.

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;
        }

    }
}

Ich halte es jedoch nicht für angemessen, alle Klassen mit einer DBConnection-Klasse zu implementieren.

Meine Frage :

  1. Welches Entwurfsmuster ist geeignet, um dieses Problem zu lösen?
  2. Ist es empfehlenswert, DBConnection als Klasse zu erstellen? (Oder sollte es eine Schnittstelle sein)

Ich habe einige Artikel über DA-Ebenen mit der Factory-Methode gefunden, aber meines Wissens entspricht dieses Muster nicht meiner Situation.

11
DevT

Wenn ich kein vorhandenes Framework verwenden kann, verwende ich normalerweise das Repository- und das Active-Muster.

Der Einfachheit halber können Sie nur das Repository-Muster verwenden. Normalerweise definiere ich es so:

//  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
    }
}

Sie haben jetzt genug Werkzeuge, um die Datenbank zu manipulieren, aber wenn Sie möchten, können Sie das aktive Muster verwenden. Ein einfaches Beispiel:

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
        }
    }
}

Offensichtlich müssen Sie bei Ihren eigenen Implementierungen die Threadsicherheit berücksichtigen, um die Transaktionen optimal zu nutzen, insbesondere in verschiedenen Entitätsrepositorys.

//  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();
}

Stellen Sie nur sicher, dass Sie wirklich eine eigene DAL erstellen möchten, da diese möglicherweise extrem komplex ist und insbesondere versucht, die allgemeinste Lösung zu entwickeln.

11
João Simões

Ich schlage vor, ein ORM, Entity Framework oder NHibernate zu verwenden. Dann müssen Sie sich nicht um einen Datenbankkontext kümmern oder SQL-Anweisungen erstellen.

5

Zunächst möchte ich Ihnen den Artikel Design Patterns for Data Persistence von Jeremy Miller empfehlen.

Es gibt einige Datenzugriffsschichtmuster:

  1. Aktives Aufzeichnungsmuster ( Wiki , Detaillierte Informationen ).
  2. Repository-Muster ( Detaillierte Informationen ).
4
Sergey Brunov

Ich empfehle Ihnen, für all diese allgemeinen Operationen eine RepositoryBase zu verwenden. Wenn Sie sich für die Verwendung eines ORM für den Datenzugriff entscheiden, sollten Sie an eine Implementierung von Repositorys denken, die auf einem Repository vom Typ Generic basieren.

Hier ist ein guter Artikel darüber:

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

2

Es ist zu alt, aber kam gerade um diese Frage und konnte nicht widerstehen, meine Gedanken zu posten.

Ich fand Repository mit UnitOfWork mit einer gewissen Abstammung ORM ist ein guter Ansatz. Dies minimiert die meisten Probleme.

Die im obigen Link erwähnte UOW kann in das Repository eingefügt werden. Das erhöht die Flexibilität der Nutzung. Außerdem ist der gesamte DB-Kommunikationscode an einem Ort zentralisiert. Das Beispiel ist nicht vollständig, sondern ein Startpunkt.

Das oben erwähnte Repository-Muster ist eine generische Basisklasse. Sie können für jedes von ihm abgeleitete konkrete Repository eine neue Klasse erstellen.

Das generische Repository wird als Antimuster betrachtet. Es gibt viele Artikel im Internet, die es erklären.

Warum ist generisches Repository Anti-Pattern?

  1. Ein Repository ist ein Teil der zu modellierenden Domain und diese Domain ist nicht generisch.
    • Nicht jede Entität kann gelöscht werden.
    • Nicht jede Entität kann hinzugefügt werden
    • Nicht jede Entität verfügt über ein Repository.
    • Die Abfragen variieren stark. Die Repository-API wird so eindeutig wie die Entität selbst.
    • Für GetById() können die Bezeichnertypen unterschiedlich sein.
    • Aktualisieren bestimmter Felder (DML) nicht möglich.
  2. Der generische Abfragemechanismus liegt in der Verantwortung eines ORM.
    • Die meisten ORMs stellen eine Implementierung bereit, die dem generischen Repository sehr ähnlich ist.
    • Repositorys sollten die SPECIFIC-Abfragen für Entitäten mithilfe des generischen Abfragemechanismus implementieren, der von ORM verfügbar gemacht wird.
  3. Arbeiten mit zusammengesetzten Tasten ist nicht möglich.
  4. Die DAL-Logik in den Diensten wird ohnehin gelöscht.
    • Prädikatkriterien, die Sie als Parameter akzeptieren, müssen von der Service-Schicht bereitgestellt werden. Wenn es sich um eine ORM-spezifische Klasse handelt, wird ORM an Services weitergeleitet.

Ich schlage vor, Sie lesen diese ( 1 , 2 , 3 , 4 , 5 ) Artikel, in denen erklärt wird, warum das generische Repository ein Anit-Muster ist.

Lösung:

  1. Schreiben Sie ein abstraktes generisches Repository, das von einem konkreten Repository umschlossen wird. Auf diese Weise können Sie die öffentliche Schnittstelle steuern, haben aber dennoch den Vorteil der Wiederverwendung von Code, der aus dem generischen Repository stammt.
  2. Verwenden Sie ein generisches Repository, verwenden Sie jedoch die Komposition anstelle der Vererbung und machen Sie es nicht als Vertrag für die Domäne verfügbar.

Setzen Sie das generische Repository in keinem Fall aufrufendem Code aus. Stellen Sie IQueryable auch nicht in konkreten Repositorys zur Verfügung.

1
Amit Joshi