Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

Business und Persistence Methoden wie implementieren? (Aufteilung Business/Service/DAO/NHibernate)
Powerslave
myCSharp.de - Member



Dabei seit:
Beiträge: 557

Themenstarter:

Business und Persistence Methoden wie implementieren? (Aufteilung Business/Service/DAO/NHibernate)

beantworten | zitieren | melden

Moin,

kurze Frage weil ich hier immer darüber stolpere.

Meine Persistenzschicht besteht aus einem GenericDao<T> und konkreten Dao's die davon ableiten.

Nehmen wir mal an, im UI habe ich die Möglichkeit nach Mitarbeitern zu suchen die mit einem bestimmten Buchstaben anfangen.

Was ist hier nun Best Practise? Ich benötige ja sowohl eine Service als auch eine Dao-Methode.

Natürlich könnte z.B. _employeeService.GetEmployeesStartsWith("A") intern nur _employeeDao.GetEmployeesStartsWith("A") aufrufen die wiederrum per NHibernate den Query absetzt.

Aber das ist doch unschön.

Wie löst ihr sowas?

Danke.
Achtung! - Hinter dir ist ein dreiköpfiger Affe!
private Nachricht | Beiträge des Benutzers
m0rius
myCSharp.de - Member

Avatar #avatar-3125.png


Dabei seit:
Beiträge: 1043

beantworten | zitieren | melden

Hallo Powerslave,

für mich gehören Methoden wie _employeeService.GetEmployeesStartingWith("A") ausschließlich in den Service-Layer. Wenn du den Methodenaufruf lediglich an den DataAccess-Layer durchreichst, musst du beim Auswechseln der Datenbanktechnologie all diese Methoden neu implementieren, was unnötig ist.
Stattdessen implementiere ich im DataAccess-Layer lediglich grundlegende Methoden, so zum Beispiel _employeeDao.GetAllEmployees() oder _employeeDao.GetEmployee(int employeeID), und lasse die Logik im Service-Layer — da, wo sie hingehört.

m0rius
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von m0rius am .
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
private Nachricht | Beiträge des Benutzers
Powerslave
myCSharp.de - Member



Dabei seit:
Beiträge: 557

Themenstarter:

beantworten | zitieren | melden

Hallo,

das heißt du hättest deinen Datenzugriff direkt im ServiceLayer? Dieser sollte eigentlich nicht wissen, wie die Daten geholt werden.
Achtung! - Hinter dir ist ein dreiköpfiger Affe!
private Nachricht | Beiträge des Benutzers
m0rius
myCSharp.de - Member

Avatar #avatar-3125.png


Dabei seit:
Beiträge: 1043

beantworten | zitieren | melden

Hallo Powerslave,

nein, der Datenzugriff liegt lediglich im DataAccess-Layer — dafür existiert dieser ja gekapselt. In meinen Projekten habe ich Repositories und Services, die folgendermaßen strukturiert sind:
  • Repositories unterstützen üblicherweise nur die Methoden Retrieve(), RetrieveAll(), Save() und Delete().
  • Services hingegen besitzen die genannten 4 Methoden, die die Aufrufe einfach nur an die Repositories durchreichen, sowie weitere Methoden, die Anwendungslogik enthalten, wie zum Beispiel RetrieveRandom() (oder wie in deinem Fall GetEmployeesStartingWith("A")).
Ein EmployeeService könnte dann z.B. so aussehen:

public class EmployeeService : IEmployeeService
{
    private readonly IEmployeeRepository _employeeRepository;

    // Hier injizierst du ein Repository für deine spezifische Datenbanktechnologie
    public EmployeeService(IEmployeeRepository employeeRepository)
    {
        _employeeRepository = employeeRepository;
    }

    // Diese Methode wird lediglich durchgereicht ...
    public IQueryable<Employee> RetrieveAll()
    {
        return _employeeRepository.RetrieveAll();
    }

    // Diese Methode wird lediglich durchgereicht ...
    public Employee Retrieve(int employeeID)
    {
        return _employeeRepository.Retrieve(employeeID);
    }

    // Diese Methode wird lediglich durchgereicht ...
    public void Save(Employee employee)
    {
        return _employeeRepository.Save(employee);
    }

    // Diese Methode wird lediglich durchgereicht ...
    public void Delete(Employee employee)
    {
        return _employeeRepository.Delete(employee);
    }

    // Diese Methode enthält Logik, die unabhängig von der Datenbanktechnologie ist.
    // Sie benötigt lediglich eine funktionierende RetrieveAll()-Methode.
    public IQueryable<Employee> RetrieveAllStartingWith(string value)
    {
        return RetrieveAll()
            .Where(employee => employee.StartsWith(value))
            .AsQueryable();
    }
}
Der Vorteil bei dieser Architektur liegt unter anderem darin, dass die Logik von RetrieveAllStartingWith(string value) unabhängig von der Datenquelle ist. Wechselt du also die Technologie, die du zum Persistieren deiner Daten verwendest , musst du lediglich ein neues Repository mit den 4 genannten Repository-Methoden implementieren. Der EmployeeService funktioniert danach immer noch einwandfrei, da sich an der Logik von RetrieveAllStartingWith(string value) ja nichts geändert hat.

m0rius
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von m0rius am .
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
private Nachricht | Beiträge des Benutzers
Powerslave
myCSharp.de - Member



Dabei seit:
Beiträge: 557

Themenstarter:

beantworten | zitieren | melden

Danke für dein Beispiel,

vielleicht war das "StartsWith"-Beispiel ein schlechtes, da man wie du es eben gezeigt hast, die Einschränkung auch über den Code implementieren kann.

Sagen wir, ich müsste die Einschränkung über ein Query machen, dann wäre es ja (auch in deinem Fall) wieder nur ein Delegieren zum DataLayer.
Achtung! - Hinter dir ist ein dreiköpfiger Affe!
private Nachricht | Beiträge des Benutzers
winSharp93
myCSharp.de - Experte

Avatar #avatar-2918.png


Dabei seit:
Beiträge: 6155
Herkunft: Stuttgart

beantworten | zitieren | melden

Hallo Powerslave,

IQueryable<T> ist dein Freund, wenn du mit dem Entity Framework arbeiten kannst.

Diskussionen dazu, ob es wirklich im Repository sein sollte, gibt's unzählige. Google einfach mal nach "repository iqueryable" und wäge die Argumente ab.

Siehe z.B. How can I write a clean Repository without exposing IQueryable to the rest of my application? sowie Repository Pattern - POCOs or IQueryable?.
private Nachricht | Beiträge des Benutzers
Cat
myCSharp.de - Member

Avatar #avatar-3070.jpg


Dabei seit:
Beiträge: 790

beantworten | zitieren | melden

Hi Powerslave,

ich weiß nicht, ob du mit
Zitat von "Powerslave"
Sagen wir, ich müsste die Einschränkung über ein Query machen, dann wäre es ja (auch in deinem Fall) wieder nur ein Delegieren zum DataLayer.
Linq (bzw. IQueryable) falsch verstanden hast.
Der Compiler erzeugt aus der Where-Bedingung intern einfach einen ExpressionTree, welcher dann vom Linq-Provider aufgelöst wird, d.h. die Query findet real wirklich auf der Datenbank statt (d.h. es wird ein SQL-Command mit WHERE erzeugt).

Dabei gehe ich davon aus, daß du NHibernate Linq benutzt (bzw. benutzen wirst).
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Cat am .
private Nachricht | Beiträge des Benutzers
Powerslave
myCSharp.de - Member



Dabei seit:
Beiträge: 557

Themenstarter:

beantworten | zitieren | melden

Ja, ich möchte gerne Linq mit NHibernate verwenden.

Kann gut sein, dass ich es noch nicht ganz verstanden habe.

Heißt das, das die Linq-Queries nicht ins Dao gehören, sondern eher in ein Repository bzw. direkt in den ServiceLayer (wenn ich IQueryable verwende)?
Achtung! - Hinter dir ist ein dreiköpfiger Affe!
private Nachricht | Beiträge des Benutzers
winSharp93
myCSharp.de - Experte

Avatar #avatar-2918.png


Dabei seit:
Beiträge: 6155
Herkunft: Stuttgart

beantworten | zitieren | melden

Zitat von Powerslave
Heißt das, das die Linq-Queries nicht ins Dao gehören, sondern eher in ein Repository bzw. direkt in den ServiceLayer (wenn ich IQueryable verwende)?
Genau - insbesondere für etwas komplexere Fälle (also nicht gerade GetAll und GetById - die würde ich weiterhin direkt in den Datenzugriff packen).

Manchmal bietet sich auch die Verwendung des Specification Patterns an - siehe Entity Framework 4 POCO, Repository and Specification Pattern
private Nachricht | Beiträge des Benutzers