it-swarm.com.de

Verwende das Repository-Muster NICHT, sondern verwende das ORM wie es ist (EF)

Ich habe immer das Repository-Muster verwendet, aber für mein letztes Projekt wollte ich sehen, ob ich die Verwendung und die Implementierung von „Unit Of Work“ perfektionieren kann. Je mehr ich anfing zu graben, desto mehr stellte ich mir die Frage: "Brauche ich sie wirklich?"

Nun beginnt alles mit ein paar Kommentaren zu Stackoverflow mit einer Spur zu Ayende Rahiens Beitrag in seinem Blog, mit 2 spezifischen,

Dies könnte wahrscheinlich für immer und ewig besprochen werden und hängt von verschiedenen Anwendungen ab. Was ich gerne wüsste,

  1. wäre dieser Ansatz für ein Entity Framework-Projekt geeignet?
  2. befindet sich die Geschäftslogik mit diesem Ansatz noch in einer Serviceebene oder in Erweiterungsmethoden (wie unten erläutert, wird bei der Erweiterungsmethode die NHib-Sitzung verwendet)?

Das geht ganz einfach mit Erweiterungsmethoden. Sauber, einfach und wiederverwendbar.

public static IEnumerable GetAll(
    this ISession instance, Expression<Func<T, bool>> where) where T : class
{
    return instance.QueryOver().Where(where).List();
}

Muss ich mit diesem Ansatz und Ninject als DI das Context zu einer Schnittstelle machen und das in meine Steuerungen einspeisen?

89
Dejan.S

Ich bin viele Wege gegangen und habe viele Implementierungen von Repositories für verschiedene Projekte erstellt und ... Ich habe das Handtuch hineingeworfen und aufgegeben, hier ist der Grund.

Codierung für die Ausnahme

Codieren Sie für die Wahrscheinlichkeit von 1%, dass sich Ihre Datenbank von einer Technologie zur anderen ändert? Wenn Sie über den zukünftigen Zustand Ihres Unternehmens nachdenken und sagen, dass dies eine Möglichkeit ist, dann a) müssen sie eine Menge Geld haben, um sich eine Migration auf eine andere DB-Technologie leisten zu können, oder b) Sie wählen aus Spaß eine DB-Technologie oder c ) Bei der ersten Technologie, für die Sie sich entschieden haben, ist ein furchtbarer Fehler aufgetreten.

Warum die reiche LINQ-Syntax wegwerfen?

LINQ und EF wurden entwickelt, damit Sie ordentlich damit arbeiten können, um Objektgraphen zu lesen und zu durchlaufen. Es ist eine ungeheure Aufgabe, ein Repository zu erstellen und zu verwalten, das Ihnen die gleiche Flexibilität bietet. Meiner Erfahrung nach habe ich jedes Mal, wenn ich ein Repository erstellt habe, IMMER Geschäftslogik in der Repository-Ebene durchgesickert, um Abfragen leistungsfähiger zu machen und/oder zu reduzieren die Anzahl der Treffer in der Datenbank.

Ich möchte keine Methode für jede einzelne Permutation einer Abfrage erstellen, die ich schreiben muss. Ich könnte genauso gut gespeicherte Prozeduren schreiben. Ich möchte nicht, dass GetOrder, GetOrderWithOrderItem, GetOrderWithOrderItemWithOrderActivity, GetOrderByUserId und so weiter ... Ich möchte nur die Hauptentität erhalten und überqueren und füge den Objektgraphen hinzu, wie es mir gefällt.

Die meisten Beispiele für Repositories sind Bullshit

Solange Sie nichts wirklich Nacktes wie ein Blog oder etwas entwickeln, werden Ihre Abfragen niemals so einfach sein wie 90% der Beispiele, die Sie im Internet rund um das Repository-Muster finden. Das kann ich gar nicht genug betonen! Dies ist etwas, das man durch den Schlamm kriechen muss, um herauszufinden. Es wird immer diese eine Abfrage geben, die Ihr perfekt durchdachtes Repository/Ihre perfekt erstellte Lösung zerstört, und erst dann können Sie sich selbst ein zweites Bild machen und die technische Verschuldung/Erosion beginnt.

Teste mich nicht, Bruder

Aber was ist mit Unit-Tests, wenn ich kein Repository habe? Wie werde ich verspotten? Ganz einfach, das tust du nicht. Betrachten wir es aus beiden Blickwinkeln:

Kein Repository - Sie können das DbContext mit einem IDbContext oder anderen Tricks verspotten, aber dann testen Sie wirklich die Einheit LINQ to Objects und nicht LINQ to Entities weil die Abfrage zur Laufzeit ermittelt wird ... OK, das ist also nicht gut! Nun ist es an der Integrationsprüfung, dies abzudecken.

Mit Repository - Sie können jetzt Ihre Repositorys verspotten und die dazwischen liegenden Layer einem Komponententest unterziehen. Großartig, oder? Nun, nicht wirklich ... Wie können Ihre Komponententests in den Fällen, in denen Sie Logik in die Repository-Ebene lecken müssen, um Abfragen performanter zu machen und/oder weniger Treffer in der Datenbank zu erzielen, dies abdecken? Es befindet sich jetzt in der Repo-Ebene und Sie möchten IQueryable<T> Nicht testen, oder? Seien wir auch ehrlich, Ihre Komponententests decken nicht die Abfragen ab, die eine Klausel .Where() und .Include() mit 20 Zeilen enthalten, und durchsuchen die Datenbank erneut, um all dies zu tun andere Sachen, bla, bla, bla sowieso, weil die Abfrage zur Laufzeit generiert wird. Da Sie ein Repository erstellt haben, um die Persistenz der oberen Ebenen nicht zu gefährden, werden Ihre Komponententests, wenn Sie jetzt Ihre Datenbanktechnologie ändern möchten, definitiv nicht die gleichen Ergebnisse zur Laufzeit garantieren, zurück zu den Integrationstests. Der ganze Sinn des Repositorys scheint also seltsam zu sein.

2 Cent

Wir verlieren bereits viel Funktionalität und Syntax bei der Verwendung von EF über einfache gespeicherte Prozeduren (Masseneinfügungen, Massenlöschungen, CTEs usw.), aber ich codiere auch in C #, sodass ich keine Binärdaten eingeben muss. Wir verwenden EF, um die Möglichkeit zu haben, verschiedene Anbieter zu verwenden und unter anderem auf nette Art und Weise mit Objektgraphen zu arbeiten. Bestimmte Abstraktionen sind nützlich und manche nicht.

96
Ryan

Das Repository-Muster ist eine Abstraktion . Ziel ist es, die Komplexität zu reduzieren und den Rest des Codes ignorant zu machen. Als Bonus können Sie Einheitentests anstelle von Integrationstests schreiben.

Das Problem ist, dass viele Entwickler den Zweck des Musters nicht verstehen und Repositorys erstellen, die persistenzspezifische Informationen an den Aufrufer weitergeben (normalerweise durch Offenlegen von IQueryable<T>). Dadurch erhalten sie keinen Vorteil gegenüber der direkten Verwendung des OR/M.

Aktualisieren Sie, um eine andere Antwort zu adressieren

Codierung für die Ausnahme

Bei der Verwendung von Repositorys geht es nicht darum, die Persistenztechnologie zu wechseln (d. H. Die Datenbank zu ändern oder stattdessen einen Webservice usw. zu verwenden). Es geht darum, Geschäftslogik von Persistenz zu trennen, um Komplexität und Kopplung zu reduzieren.

Unit-Tests gegen Integrationstests

Sie schreiben keine Komponententests für Repositorys. Zeitraum.

Durch die Einführung von Repositorys (oder einer anderen Abstraktionsschicht zwischen Persistenz und Geschäft) können Sie jedoch Komponententests für die Geschäftslogik schreiben. Sie müssen sich keine Sorgen machen, dass Ihre Tests aufgrund einer falsch konfigurierten Datenbank fehlschlagen.

Wie für die Abfragen. Wenn Sie LINQ verwenden, müssen Sie auch sicherstellen, dass Ihre Abfragen funktionieren, genau wie bei Repositorys. Und das geschieht mit Integrationstests.

Der Unterschied besteht darin, dass Sie, wenn Sie Ihr Geschäft nicht mit LINQ-Anweisungen verwechselt haben, zu 100% sicher sein können, dass Ihr Persistenzcode fehlerhaft ist und nichts anderes.

Wenn Sie Ihre Tests analysieren, werden Sie auch feststellen, dass sie viel sauberer sind, wenn Sie keine gemischten Bedenken haben (d. H. LINQ + Business-Logik).

Repository-Beispiele

Die meisten Beispiele sind Bullshit. das ist sehr wahr. Wenn Sie jedoch ein Designmuster googeln, werden Sie viele beschissene Beispiele finden. Dies ist kein Grund, die Verwendung eines Musters zu vermeiden.

Das Erstellen einer korrekten Repository-Implementierung ist sehr einfach. In der Tat müssen Sie nur eine einzige Regel befolgen:

Fügen Sie der Repository-Klasse erst dann etwas hinzu, wenn Sie es benötigen

Viele Programmierer sind faul und versuchen, ein generisches Repository zu erstellen und eine Basisklasse mit vielen Methoden zu verwenden, die sie brauchen. YAGNI. Sie schreiben die Repository-Klasse einmal und behalten sie so lange bei, wie die Anwendung läuft (kann Jahre dauern). Warum faulenzen? Halten Sie es ohne Vererbung der Basisklasse sauber. Dies erleichtert das Lesen und Warten erheblich.

(Die obige Aussage ist eine Richtlinie und kein Gesetz. Eine Basisklasse kann sehr gut motiviert sein. Denken Sie einfach darüber nach, bevor Sie sie hinzufügen, damit Sie sie aus den richtigen Gründen hinzufügen.)

Altes Zeug

Fazit:

Wenn es Ihnen nichts ausmacht, LINQ-Anweisungen in Ihrem Geschäftscode zu haben, oder wenn Sie sich nicht um Unit-Tests kümmern, sehe ich keinen Grund, Entity Framework nicht direkt zu verwenden.

Update

Ich habe sowohl über das Repository-Muster als auch darüber gebloggt, was "Abstraktion" wirklich bedeutet: http://blog.gauffin.org/2013/01/repository-pattern-done-right/

Update 2

Wie werden Sie für einen einzelnen Entitätstyp mit mehr als 20 Feldern eine Abfragemethode entwerfen, um eine Permutationskombination zu unterstützen? Sie möchten die Suche nicht nur nach Namen einschränken, sondern auch nach Navigationseigenschaften suchen, alle Bestellungen mit Artikeln mit einem bestimmten Preiscode auflisten und die Suche nach Navigationseigenschaften auf 3 Ebenen durchführen. Der ganze Grund, warum IQueryable erfunden wurde, war, in der Lage zu sein, eine beliebige Kombination von Suchen mit Datenbanken zu erstellen. Theoretisch sieht alles gut aus, aber das Bedürfnis des Benutzers gewinnt über der Theorie.

Nochmals: Eine Entität mit mehr als 20 Feldern ist falsch modelliert. Es ist eine GOTT-Entität. Mach es kaputt.

Ich behaupte nicht, dass IQueryable nicht zum Fragen gemacht wurde. Ich sage, dass es nicht für eine Abstraktionsebene wie das Repository-Muster geeignet ist, da es undicht ist. Es gibt keinen 100% vollständigen LINQ To SQL-Anbieter (wie EF).

Sie haben alle implementierungsspezifische Dinge wie das Verwenden von Eifer/Faul-Laden oder das Ausführen von SQL "IN" -Anweisungen. Wenn Sie IQueryable im Repository verfügbar machen, muss der Benutzer all diese Dinge wissen. Somit ist der gesamte Versuch, die Datenquelle zu abstrahieren, ein völliger Misserfolg. Sie erhöhen lediglich die Komplexität, ohne den direkten Einsatz von OR/M zu beeinträchtigen.

Implementieren Sie das Repository-Muster entweder richtig oder verwenden Sie es überhaupt nicht.

(Wenn Sie wirklich mit großen Entitäten umgehen möchten, können Sie das Repository-Muster mit dem Spezifikationsmuster kombinieren. Dadurch erhalten Sie eine vollständige Abstraktion, die auch testbar ist.)

47
jgauffin

IMO haben sowohl die Abstraktion Repository als auch die Abstraktion UnitOfWork einen sehr wertvollen Platz in jeder sinnvollen Entwicklung. Die Leute streiten sich über Implementierungsdetails, aber so wie es viele Möglichkeiten gibt, eine Katze zu häuten, gibt es viele Möglichkeiten, eine Abstraktion zu implementieren.

Ihre Frage ist speziell zu verwenden oder nicht zu verwenden und warum.

Wie Sie zweifelsohne bemerkt haben, sind beide Muster bereits in Entity Framework integriert. DbContext ist die UnitOfWork und DbSet ist die Repository. Im Allgemeinen müssen Sie die UnitOfWork oder Repository nicht einzeln testen, da dies lediglich die Verbindung zwischen Ihren Klassen und den zugrunde liegenden Datenzugriffsimplementierungen erleichtert. Was Sie immer wieder tun müssen, ist, diese beiden Abstraktionen zu verspotten, wenn Sie die Logik Ihrer Dienste testen.

Sie können sich über externe Bibliotheken lustig machen, fälschen oder was auch immer, indem Sie Schichten von Codeabhängigkeiten (die Sie nicht kontrollieren) zwischen der Logik, die das Testen durchführt, und der Logik hinzufügen getestet werden.

Ein kleiner Punkt ist, dass Ihre eigene Abstraktion für UnitOfWork und Repository Ihnen maximale Kontrolle und Flexibilität beim Verspotten Ihrer Komponententests gibt.

Alles in allem, aber für mich besteht die wahre Stärke dieser Abstraktionen darin, dass sie eine einfache Möglichkeit bieten, aspektorientierte Programmiertechniken anzuwenden und die SOLID) - Prinzipien einzuhalten .

Sie haben also Ihre IRepository:

public interface IRepository<T>
    where T : class
{
    T Add(T entity);
    void Delete(T entity);
    IQueryable<T> AsQueryable();
}

Und seine Umsetzung:

public class Repository<T> : IRepository<T>
    where T : class
{
    private readonly IDbSet<T> _dbSet;
    public Repository(PPContext context) 
    {
        _dbSet = context.Set<T>();
    }

    public T Add(T entity)
    { 
        return _dbSet.Add(entity); 
    }

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

    public IQueryable<T> AsQueryable() 
    {
        return _dbSet.AsQueryable();
    }
}

Bisher nichts Außergewöhnliches, aber jetzt wollen wir ein wenig Protokollierung hinzufügen - einfach mit einer Protokollierung Decorator.

public class RepositoryLoggerDecorator<T> : IRepository<T>
    where T : class
{
    Logger logger = LogManager.GetCurrentClassLogger();
    private readonly IRepository<T> _decorated;
    public RepositoryLoggerDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() );
        T added = _decorated.Add(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        return added;
    }

    public void Delete(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        _decorated.Delete(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable();
    }
}

Alles erledigt und ohne Änderung an unserem bestehenden Code . Es gibt zahlreiche andere Querschnittsthemen, die wir hinzufügen können, wie beispielsweise Ausnahmebehandlung, Daten-Caching, Datenvalidierung oder was auch immer und während unseres gesamten Entwurfs- und Erstellungsprozesses das Wertvollste, das uns das Hinzufügen ermöglicht Einfache Funktionen, ohne unseren vorhandenen Code zu ändern, sind unsere IRepository - Abstraktion .

Jetzt habe ich diese Frage bei StackOverflow schon oft gesehen: "Wie lässt sich Entity Framework in einer Umgebung mit mehreren Mandanten einsetzen?".

https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant

Wenn Sie eine Repository - Abstraktion haben, lautet die Antwort: "Es ist einfach, einen Dekorateur hinzuzufügen."

public class RepositoryTennantFilterDecorator<T> : IRepository<T>
    where T : class
{
    //public for Unit Test example
    public readonly IRepository<T> _decorated;
    public RepositoryTennantFilterDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        return _decorated.Add(entity);
    }

    public void Delete(T entity)
    {
        _decorated.Delete(entity);
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable().Where(o => true);
    }
}

IMO sollten Sie immer eine einfache Abstraktion über alle Komponenten von Drittanbietern platzieren, auf die an mehr als einer Handvoll Stellen verwiesen wird. Aus dieser Perspektive ist ein ORM der perfekte Kandidat, da in so vielen unserer Codes darauf verwiesen wird.

Die Antwort, die normalerweise einfällt, wenn jemand sagt: "Warum sollte ich eine Abstraktion (z. B. Repository) über diese oder jene Bibliothek eines Drittanbieters haben?"

P.S. Dekoratoren lassen sich mit einem IoC-Container wie SimpleInjector sehr einfach anwenden.

[TestFixture]
public class IRepositoryTesting
{
    [Test]
    public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
    {
        Container container = new Container();
        container.RegisterLifetimeScope<PPContext>();
        container.RegisterOpenGeneric(
            typeof(IRepository<>), 
            typeof(Repository<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryLoggerDecorator<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryTennantFilterDecorator<>));
        container.Verify();

        using (container.BeginLifetimeScope())
        {
            var result = container.GetInstance<IRepository<Image>>();

            Assert.That(
                result, 
                Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>)));
            Assert.That(
                (result as RepositoryTennantFilterDecorator<Image>)._decorated,
                Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>)));
        }
    }
}
24
qujck

Erstens ist EF, wie in einer Antwort vorgeschlagen, selbst ein Repository-Muster. Es ist nicht erforderlich, eine weitere Abstraktion zu erstellen, nur um sie als Repository zu bezeichnen.

Mockable Repository für Unit Tests, brauchen wir es wirklich?

Wir lassen EF kommunizieren, um die Datenbank in Komponententests zu testen, um unsere Geschäftslogik direkt mit der SQL-Testdatenbank zu vergleichen. Ich sehe keinen Vorteil darin, ein Repository-Muster zu verspotten. Was ist wirklich falsch daran, Komponententests gegen eine Testdatenbank durchzuführen? Da es sich um Bulk-Operationen handelt, sind diese nicht möglich, und am Ende schreiben wir unformatiertes SQL. SQLite im Speicher ist der perfekte Kandidat für Komponententests mit einer realen Datenbank.

Unnötige Abstraktion

Möchten Sie ein Repository erstellen, damit Sie EF in Zukunft problemlos durch NHbibernate usw. oder etwas anderes ersetzen können? Hört sich toll an, aber ist es wirklich kostengünstig?

Linq beendet Unit-Tests?

Ich werde gerne Beispiele sehen, wie es töten kann.

Abhängigkeitsinjektion, IoC

Wow, das sind großartige Wörter, die in der Theorie sicher großartig aussehen, aber manchmal muss man sich zwischen großartigem Design und großartiger Lösung entscheiden. Wir haben das alles genutzt und haben am Ende alles in den Müll geworfen und einen anderen Ansatz gewählt. Size vs Speed ​​(Größe des Codes und Geschwindigkeit der Entwicklung) ist im wirklichen Leben von enormer Bedeutung. Benutzer benötigen Flexibilität, es ist ihnen egal, ob Ihr Code in Bezug auf DI oder IoC hervorragend gestaltet ist.

Es sei denn, Sie erstellen Visual Studio.

All diese großartigen Designs werden benötigt, wenn Sie ein komplexes Programm wie Visual Studio oder Eclipse erstellen, das von vielen Leuten entwickelt wird und das hochgradig anpassbar sein muss. Alle großartigen Entwicklungsmuster wurden nach Jahren der Entwicklung sichtbar, die diese IDEs durchlaufen haben, und sie haben sich an einem Ort entwickelt, an dem all diese großartigen Designmuster so wichtig sind. Wenn Sie jedoch eine einfache webbasierte Gehaltsabrechnung oder eine einfache Geschäftsanwendung durchführen, ist es besser, dass Sie sich in Ihrer Entwicklung im Laufe der Zeit weiterentwickeln, anstatt Zeit für die Erstellung für Millionen Benutzer zu investieren, bei denen sie nur für Hunderte von Benutzern bereitgestellt wird.

Repository als gefilterte Ansicht - ISecureRepository

Andererseits sollte das Repository eine gefilterte Ansicht von EF sein, die den Zugriff auf Daten durch Anwenden des erforderlichen Füllers basierend auf dem aktuellen Benutzer/der aktuellen Rolle schützt.

Dies macht das Repository jedoch noch komplizierter, da es in einer riesigen Codebasis endet, die gewartet werden muss. Am Ende erstellen die Benutzer unterschiedliche Repositorys für unterschiedliche Benutzertypen oder Kombinationen von Entitätstypen. Nicht nur das, wir haben auch viele DTOs.

Die folgende Antwort ist eine Beispielimplementierung von Filtered Repository, ohne eine ganze Reihe von Klassen und Methoden zu erstellen. Möglicherweise wird die Frage nicht direkt beantwortet, es kann jedoch hilfreich sein, eine Frage abzuleiten.

Haftungsausschluss: Ich bin Autor des Entity REST SDK.

http://entityrestsdk.codeplex.com

Unter Berücksichtigung der obigen Punkte haben wir ein SDK entwickelt, das ein Repository für gefilterte Ansichten basierend auf SecurityContext erstellt, das Filter für CRUD-Vorgänge enthält. Und nur zwei Arten von Regeln vereinfachen komplexe Operationen. Der erste ist der Zugriff auf die Entität und der andere die Lese-/Schreibregel für die Eigenschaft.

Der Vorteil ist, dass Sie keine Geschäftslogik oder Repositorys für verschiedene Benutzertypen neu schreiben, sondern lediglich den Zugriff blockieren oder ihnen gewähren.

public class DefaultSecurityContext : BaseSecurityContext {

  public static DefaultSecurityContext Instance = new DefaultSecurityContext();

  // UserID for currently logged in User
  public static long UserID{
       get{
             return long.Parse( HttpContext.Current.User.Identity.Name );
       }
  }

  public DefaultSecurityContext(){
  }

  protected override void OnCreate(){

        // User can access his own Account only
        var acc = CreateRules<Account>();

        acc.SetRead( y => x=> x.AccountID == UserID ) ;
        acc.SetWrite( y => x=> x.AccountID == UserID );

        // User can only modify AccountName and EmailAddress fields
        acc.SetProperties( SecurityRules.ReadWrite, 
              x => x.AccountName,
              x => x.EmailAddress);

        // User can read AccountType field
        acc.SetProperties<Account>( SecurityRules.Read, 
              x => x.AccountType);

        // User can access his own Orders only
        var order = CreateRules<Order>();
        order.SetRead( y => x => x.CustomerID == UserID );

        // User can modify Order only if OrderStatus is not complete
        order.SetWrite( y => x => x.CustomerID == UserID 
            && x.OrderStatus != "Complete" );

        // User can only modify OrderNotes and OrderStatus
        order.SetProperties( SecurityRules.ReadWrite, 
              x => x.OrderNotes,
              x => x.OrderStatus );

        // User can not delete orders
        order.SetDelete(order.NotSupportedRule);
  }
}

Diese LINQ-Regeln werden für jeden Vorgang anhand der SaveChanges-Methode für die Datenbank ausgewertet, und diese Regeln fungieren als Firewall vor der Datenbank.

11
Akash Kava

Es gibt eine Menge Debatten darüber, welche Methode richtig ist, also sehe ich sie als akzeptabel an, also verwende ich immer diejenige, die mir am besten gefällt (die kein Repository ist, UoW).

In EF wird UoW über DbContext implementiert und die DbSets sind Repositories.

Was die Arbeit mit der Datenschicht angeht, arbeite ich direkt am DbContext-Objekt. Bei komplexen Abfragen werden Erweiterungsmethoden für die Abfrage erstellt, die wiederverwendet werden können.

Ich glaube, Ayende hat auch einige Posts darüber, wie schlecht es ist, CUD-Operationen zu abstrahieren.

Ich erstelle immer eine Schnittstelle und habe meinen Kontext davon geerbt, damit ich einen IoC-Container für DI verwenden kann.

6
Josh

Für mich ist es eine einfache Entscheidung mit relativ wenigen Faktoren. Die Faktoren sind:

  1. Repositorys sind für Domänenklassen.
  2. In einigen meiner Apps stimmen Domänenklassen mit meinen DAL-Klassen überein, in anderen nicht.
  3. Wenn sie gleich sind, stellt mir EF bereits Repositories zur Verfügung.
  4. EF bietet Lazy Loading und IQueryable. Ich mag diese.
  5. Das Abstrahieren/"Fassadieren"/Neuimplementieren des Repository über EF bedeutet normalerweise den Verlust von Lazy und IQueryable

Also, wenn meine App # 2 nicht rechtfertigen kann, getrennte Domain- und Datenmodelle, dann kümmere ich mich normalerweise nicht um # 5.

1
Chalky

Linq ist heutzutage ein 'Repository'.

ISession + Linq ist bereits das Repository, und Sie benötigen weder GetXByY Methoden noch QueryData(Query q) Generalisierung. Da ich etwas paranoid gegenüber der DAL-Nutzung bin, bevorzuge ich immer noch die Repository-Schnittstelle. (Unter dem Gesichtspunkt der Wartbarkeit müssen wir auch noch eine Fassade über bestimmte Datenzugriffsschnittstellen haben).

Hier ist das Repository, das wir verwenden - es entkoppelt uns von der direkten Verwendung von nhibernate, bietet jedoch eine linq-Schnittstelle (als ISession-Zugriff in Ausnahmefällen, die eventuell überarbeitet werden müssen).

class Repo
{
    ISession _session; //via ioc
    IQueryable<T> Query()
    {
        return _session.Query<T>();
    }
}
1
mikalai

Was für EF am häufigsten zutrifft, ist kein Repository-Muster. Es handelt sich um ein Fassadenmuster (Zusammenfassung der Aufrufe von EF-Methoden in einfachere, benutzerfreundlichere Versionen).

EF wendet das Repository-Muster (und auch das Muster der Arbeitseinheit) an. Das heißt, EF ist derjenige, der die Datenzugriffsebene abstrahiert, sodass der Benutzer keine Ahnung hat, dass er mit SQL Server zu tun hat.

Und dabei sind die meisten "Repositories" über EF nicht einmal gute Fassaden, da sie einfach einzelnen Methoden in EF zugeordnet sind, sogar bis zu dem Punkt, dass sie die gleichen Signaturen haben.

Die beiden Gründe für das Anwenden dieses sogenannten "Repository" -Musters über EF bestehen darin, ein einfacheres Testen zu ermöglichen und eine Teilmenge von "vorgefertigten" Aufrufen an EF einzurichten. Nicht schlecht für sich, aber eindeutig kein Repository.

1
Aratirn

Im Repository (oder wie auch immer man es nennt) geht es mir derzeit hauptsächlich darum, die Persistenzschicht zu abstrahieren.

Ich verwende es gekoppelt mit Abfrageobjekten, sodass ich in meinen Anwendungen keine Kopplung mit einer bestimmten Technologie habe. Außerdem erleichtert es das Testen erheblich.

Also neige ich dazu

public interface IRepository : IDisposable
{
    void Save<TEntity>(TEntity entity);
    void SaveList<TEntity>(IEnumerable<TEntity> entities);

    void Delete<TEntity>(TEntity entity);
    void DeleteList<TEntity>(IEnumerable<TEntity> entities);

    IList<TEntity> GetAll<TEntity>() where TEntity : class;
    int GetCount<TEntity>() where TEntity : class;

    void StartConversation();
    void EndConversation();

    //if query objects can be self sustaining (i.e. not need additional configuration - think session), there is no need to include this method in the repository.
    TResult ExecuteQuery<TResult>(IQueryObject<TResult> query);
}

Fügen Sie möglicherweise asynchrone Methoden mit Rückrufen als Delegaten hinzu. Das Repo ist einfach zu implementieren allgemein, sodass ich keine Zeile der Implementierung von App zu App berühren kann. Nun, das stimmt zumindest, wenn ich NH benutze. Ich habe es auch mit EF gemacht, aber ich hasse EF. 4. Das Gespräch ist der Beginn einer Transaktion. Sehr cool, wenn einige Klassen die Repository-Instanz gemeinsam nutzen. Für NH entspricht ein Repo in meiner Implementierung einer Sitzung, die bei der ersten Anforderung geöffnet wird.

Dann die Abfrageobjekte

public interface IQueryObject<TResult>
{
    /// <summary>Provides configuration options.</summary>
    /// <remarks>
    /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository.
    /// If not used through a repository, it can be useful as a configuration option.
    /// </remarks>
    void Configure(object parameter);

    /// <summary>Implementation of the query.</summary>
    TResult GetResult();
}

Für die configure benutze ich in NH nur um die ISession zu übergeben. In EF macht das mehr oder weniger keinen Sinn.

Eine Beispielabfrage wäre .. (NH)

public class GetAll<TEntity> : AbstractQueryObject<IList<TEntity>>
    where TEntity : class
{
    public override IList<TEntity> GetResult()
    {
        return this.Session.CreateCriteria<TEntity>().List<TEntity>();
    }
}

Um eine EF-Abfrage durchführen zu können, müsste sich der Kontext in der Basis Abstract befinden, nicht in der Sitzung. Aber natürlich wäre das ifc dasselbe.

Auf diese Weise werden die Abfragen selbst gekapselt und sind leicht zu testen. Das Beste ist, dass mein Code nur auf Schnittstellen basiert. Alles ist sehr sauber. Domain- (Geschäfts-) Objekte sind genau das, z. Es gibt keine Vermischung von Verantwortlichkeiten, wie bei Verwendung des aktiven Datensatzmusters, das kaum testbar ist und den Datenzugriffscode (Abfragecode) im Domänenobjekt mischt, und dabei Vermischung von Belangen (Objekt, das sich selbst abruft?). Es steht weiterhin jedem frei, POCOs für die Datenübertragung zu erstellen.

Alles in allem bietet dieser Ansatz viel Wiederverwendung und Einfachheit von Code, da nichts verloren geht, was ich mir vorstellen kann. Irgendwelche Ideen?

Vielen Dank an Ayende für seine großartigen Beiträge und sein anhaltendes Engagement. Es sind seine Ideen hier (Abfrageobjekt), nicht meine.

1
h.alex