2013-08-01 7 views
7

Um entwerfen Zugriff auf die Objekte in der Datenbank zur Verfügung zu stellen, ich eine Schnittstelle geschaffen für alle Teammitglieder wie dieses (vereinfachtes Beispiel) verwendet werden:Wie eine Datenbank-Schnittstelle

public interface IDatabase 
{ 
    ObservableCollection<Animal> Animals{ get; } 
} 

I didn‘ t will das Team für den Zugriff auf Interna wie der Datenbankkontext oder einige oracle-Objekte (Kapselung) ...

ich zwei spezifische Klassen implementiert im wirklichen Leben verwendet wird und für Unit-Tests:

public class TestDatabase : IDatabase 
{ } 

public class OracleDatabase : IDatabase 
{ } 

Nach einiger Nutzung der Teammitglieder für immer mehr Funktionen fragen, und ich habe Methoden meine Schnittstelle hinzuzufügen:

public interface IDatabase 
{ 
    ObservableCollection<Animal> Animals{ get; } 
    ObservableCollection<Animal> Animals(Gender gender); 
    ObservableCollection<Animal> Animals(Gender gender, Race race); 
} 

Einige der Filter- und Sortier Sachen könnten natürlich durch den Entwickler selbst durchgeführt werden, aber Es befindet sich besser in der Datenbank.


Mein Problem ist jetzt, dass meine Schnittstelle explodiert ist, wird es jeden Tag mehr spezialisierte Funktionen bekommen, ist es alles andere als stabil ist und hält die ganze Zeit ändern.

Ist mein Design von Anfang an fehlerhaft?

einige Ideen, die Frage zu lösen:

  1. die Datenbank Kontextobjekt für alle Entwickler aussetzen (schlecht glaube ich)
  2. eine Funktion hinzufügen, die eine Linq-Abfrage
+0

Wenn Sie mehr und mehr hinzufügen, werden Sie wahrscheinlich feststellen, dass Sie schließlich das Datenbankkontextobjekt, das Sie ursprünglich verbergen wollten (möglicherweise mit neuen Namen), erneut implementieren.Es wird manchmal (in manchen Zusammenhängen) als der "innere Plattformeffekt" bezeichnet. –

Antwort

4

Sie versuchen, die Repository/UnitOfWork-Muster neu zu erfinden, und Sie tun es nicht ganz richtig.

A würde richtiger Ansatz, um diese Nähe:

// shared between repositories 
public interface IGenericRepository<T> 
{ 
    T CreateNew(); 

    void Delete(T item); 
    void Update(T item); 
    void Insert(T item); 

    IEnumerable<T> FindAll(); 
    T FindOne(int id); 
} 

// specific repositories 
public interface IAnimalRepository : IGenericRepository<Animal> 
{ 
    IEnumerable<Animal> FindByNumberOfLegs(int NumberOfLegs); 
    // ... anything specific follows 
} 

public interface IHumanRepository : IGenericRepository<Human> 
{ 
    IEnumerable<Human> FindByGender(Gender gender); 
    // ... specific repository logic follows 
} 

// unit of work - a service for clients 
public interface IUnitOfWork : IDisposable 
{ 
    IAnimalRepository AnimalRepository { get; } 
    IHumanRepository HumanRepository { get; } 
    // .. other repositories follow 

    void SaveChanges(); 
} 

Auf diese Weise Ihrer Service-Schicht auf der Repository-Schicht abhängt und Sie können zwischen Implementierungen leicht, zum Beispiel für Komponententests wechseln. Ihre Kunden schreiben

// example code 
using (IUnitOfWork uow = new YourImplementationOfUnitOfWork()) 
{ 
    var animals = uow.AnimalRepository.FindByNumberOfLegs(3); 

    var person = uow.HumanRepository.CreateNew(); 
    person.Name = "John"; 
    uow.HumanRepository.Insert(person); 

    uow.SaveChanges(); 
} 

Wenn Sie planen, die Anzahl der Methoden zu beschränken, können Sie einfach ändern leicht die Repository-Schnittstelle:

// shared between repositories 
public interface IGenericRepository<T> 
{ 
    T CreateNew(); 

    void Delete(T item); 
    void Update(T item); 
    void Insert(T item); 

    IQueryable<T> Query { get; } 
} 

Auf diese Weise Ihre Kunden LINQ verwenden können:

// example code 
using (IUnitOfWork uow = new YourImplementationOfUnitOfWork()) 
{ 
    var animals = uow.AnimalRepository.Query.Where(a => a.NumberOfLegs == 3); 

    var person = uow.HumanRepository.CreateNew(); 
    person.Name = "John"; 
    uow.HumanRepository.Insert(person); 

    uow.SaveChanges(); 
} 
2

Könnte ich akzeptiert schlage vor, das Schnittstellen-Trennprinzip anzuwenden? d.h. Trennen Sie Ihre Schnittstellen in logische Gruppen. Dadurch können die Benutzer Ihrer Schnittstelle auch keine Teile davon implementieren, die sie nicht benötigen. Die Stabilität sollte ebenfalls steigen, da Sie mehr diskrete Teile testbaren Codes haben.

+0

Ich denke, das löst mein grundlegendes Problem nicht, es macht es nur erträglicher. – nabulke

+0

Ich kann Ihr Design nicht kommentieren, ohne die Projektstruktur selbst zu sehen, aber ich nehme an, dass Sie die konkreten Klassen basierend auf den Schnittstellen in einer Datenschicht implementieren. Wenn dies der Fall ist, wird der Grund dafür, dass der Datenbankkontext oder -parameter mit einer LINQ-Abfrage offen gelegt wird, der Grund für das Vorhandensein einer separaten DL-Schicht sein. Es würde am Ende für alle frei sein ohne jegliche Trennung von Sorgen. – Damon