Antworten:
Repository Layer bietet Ihnen zusätzliche Abstraktionsebene über den Datenzugriff. Anstatt zu schreiben
var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();
Um ein einzelnes Element aus der Datenbank abzurufen, verwenden Sie die Repository-Schnittstelle
public interface IRepository<T>
{
IQueryable<T> List();
bool Create(T item);
bool Delete(int id);
T Get(int id);
bool SaveChanges();
}
und anrufen Get(id)
. Die Repository-Schicht macht grundlegende CRUD- Operationen verfügbar .
Die Serviceschicht macht die Geschäftslogik verfügbar, die das Repository verwendet. Ein Beispieldienst könnte folgendermaßen aussehen:
public interface IUserService
{
User GetByUserName(string userName);
string GetUserNameByEmail(string email);
bool EditBasicUserData(User user);
User GetUserByID(int id);
bool DeleteUser(int id);
IQueryable<User> ListUsers();
bool ChangePassword(string userName, string newPassword);
bool SendPasswordReminder(string userName);
bool RegisterNewUser(RegisterNewUserModel model);
}
Während die List()
Repository-Methode alle Benutzer zurückgibt, kann ListUsers()
IUserService nur diejenigen zurückgeben, auf die der Benutzer Zugriff hat.
In ASP.NET MVC + EF + SQL SERVER habe ich diesen Kommunikationsfluss:
Ansichten <- Controller -> Service-Schicht -> Repository-Schicht -> EF -> SQL Server
Service-Schicht -> Repository-Schicht -> EF Dieser Teil arbeitet mit Modellen.
Ansichten <- Controller -> Serviceebene Dieser Teil arbeitet mit Ansichtsmodellen.
BEARBEITEN:
Beispiel für den Ablauf für / Orders / ByClient / 5 (wir möchten die Bestellung für einen bestimmten Kunden sehen):
public class OrderController
{
private IOrderService _orderService;
public OrderController(IOrderService orderService)
{
_orderService = orderService; // injected by IOC container
}
public ActionResult ByClient(int id)
{
var model = _orderService.GetByClient(id);
return View(model);
}
}
Dies ist die Schnittstelle für den Bestellservice:
public interface IOrderService
{
OrdersByClientViewModel GetByClient(int id);
}
Diese Schnittstelle gibt das Ansichtsmodell zurück:
public class OrdersByClientViewModel
{
CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
IEnumerable<OrderViewModel> Orders { get; set; }
}
Dies ist eine Schnittstellenimplementierung. Es verwendet Modellklassen und Repository, um ein Ansichtsmodell zu erstellen:
public class OrderService : IOrderService
{
IRepository<Client> _clientRepository;
public OrderService(IRepository<Client> clientRepository)
{
_clientRepository = clientRepository; //injected
}
public OrdersByClientViewModel GetByClient(int id)
{
return _clientRepository.Get(id).Select(c =>
new OrdersByClientViewModel
{
Cient = new ClientViewModel { ...init with values from c...}
Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}
}
);
}
}
IRepository<>
um GenericRepository<>
in Ihrer IOC - Bibliothek. Diese Antwort ist sehr alt. Ich denke, die beste Lösung besteht darin, alle Repositorys in einer Klasse namens zu kombinieren UnitOfWork
. Es sollte ein Repository jedes Typs und eine aufgerufene Methode enthalten SaveChanges
. Alle Repositorys sollten einen EF-Kontext gemeinsam nutzen.
Wie Carnotaurus sagte, ist das Repository für die Zuordnung Ihrer Daten aus dem Speicherformat zu Ihren Geschäftsobjekten verantwortlich. Es sollte sowohl das Lesen als auch das Schreiben von Daten (Löschen, Aktualisieren) vom und zum Speicher behandeln.
Der Zweck der Serviceschicht besteht andererseits darin, die Geschäftslogik an einem einzigen Ort zusammenzufassen, um die Wiederverwendung von Code und die Trennung von Bedenken zu fördern. In der Praxis bedeutet dies für mich in der Praxis beim Erstellen von Asp.net MVC-Sites, dass ich diese Struktur habe
[Controller] ruft [Service (s)] auf, der [Repository (s)] aufruft]
Ein Prinzip, das ich als nützlich empfunden habe, besteht darin, die Logik in Controllern und Repositorys auf ein Minimum zu beschränken.
In Controllern liegt es daran, dass es mir hilft, trocken zu bleiben. Es kommt sehr häufig vor, dass ich die gleiche Filterung oder Logik an einer anderen Stelle verwenden muss, und wenn ich sie in den Controller einbaue, kann ich sie nicht wiederverwenden.
In Repositorys liegt es daran, dass ich meinen Speicher (oder ORM) ersetzen möchte, wenn etwas Besseres eintritt. Und wenn ich Logik im Repository habe, muss ich diese Logik neu schreiben, wenn ich das Repository ändere. Wenn mein Repository nur IQueryable zurückgibt und der Dienst andererseits die Filterung durchführt, muss ich nur die Zuordnungen ersetzen.
Zum Beispiel habe ich kürzlich mehrere meiner Linq-To-Sql-Repositorys durch EF4 ersetzt, und diejenigen, bei denen ich diesem Prinzip treu geblieben war, konnten innerhalb weniger Minuten ersetzt werden. Wo ich eine Logik hatte, war es stattdessen eine Frage der Stunden.
onBeforeBuildBrowseQuery
und den Abfrage-Generator verwenden können, um die Abfrage zu ändern.
Die akzeptierte Antwort (und hunderte Male positiv bewertet) weist einen großen Fehler auf. Ich wollte dies im Kommentar hervorheben, aber es wird nur in 30 Kommentaren vergraben, die hier darauf hinweisen.
Ich habe eine Unternehmensanwendung übernommen, die auf diese Weise erstellt wurde, und meine erste Reaktion war WTH ? ViewModels in der Service-Schicht? Ich wollte die Konvention nicht ändern, da sie jahrelang weiterentwickelt worden war, und fuhr daher mit der Rückgabe von ViewModels fort. Junge, es wurde zu einem Albtraum, als wir anfingen, WPF zu verwenden. Wir (das Entwicklerteam) sagten immer: Welches ViewModel? Der echte (der, den wir für die WPF geschrieben haben) oder der Dienst? Sie wurden für eine Webanwendung geschrieben und hatten sogar das IsReadOnly- Flag, um die Bearbeitung in der Benutzeroberfläche zu deaktivieren. Großer, großer Fehler und alles wegen eines Wortes: ViewModel !!
Bevor Sie denselben Fehler machen, sind neben meiner obigen Geschichte noch einige weitere Gründe aufgeführt:
Das Zurückgeben eines ViewModel von der Service-Schicht ist ein großes Nein Nein. Das ist wie zu sagen:
Wenn Sie diese Dienste nutzen möchten, verwenden Sie besser MVVM. Hier ist das ViewModel, das Sie verwenden müssen. Autsch!
Die Dienste gehen davon aus, dass sie irgendwo in einer Benutzeroberfläche angezeigt werden. Was ist, wenn es von einer Nicht-UI-Anwendung wie Webdiensten oder Windows-Diensten verwendet wird?
Das ist nicht einmal ein echtes ViewModel. Ein echtes ViewModel hat Beobachtbarkeit, Befehle usw. Das ist nur ein POCO mit einem schlechten Ruf. (Siehe meine Geschichte oben, warum Namen wichtig sind.)
Die konsumierende Anwendung ist besser eine Präsentationsebene (ViewModels werden von dieser Ebene verwendet) und sie versteht C # besser. Noch ein Autsch!
Bitte tu das nicht!
Normalerweise wird ein Repository als Gerüst verwendet, um Ihre Entitäten zu füllen. Eine Service-Schicht wird ausgehen und eine Anfrage stellen. Es ist wahrscheinlich, dass Sie ein Repository unter Ihre Service-Schicht stellen.
Die Repository-Schicht ist für den Zugriff auf die Datenbank implementiert und hilft bei der Erweiterung der CRUD-Operationen für die Datenbank. Während eine Serviceschicht aus der Geschäftslogik der Anwendung besteht und die Repository-Schicht verwenden kann, um bestimmte Logik zu implementieren, die die Datenbank betrifft. In einer Anwendung ist es besser, eine separate Repository- und Service-Schicht zu haben. Durch separate Repository- und Service-Schichten wird der Code modularer und die Datenbank von der Geschäftslogik entkoppelt.