Réponses:
Repository Layer vous offre un niveau supplémentaire d'abstraction sur l'accès aux données. Au lieu d'écrire
var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();
pour obtenir un seul élément de la base de données, vous utilisez l'interface du référentiel
public interface IRepository<T>
{
IQueryable<T> List();
bool Create(T item);
bool Delete(int id);
T Get(int id);
bool SaveChanges();
}
et appelez Get(id)
. La couche de référentiel expose les opérations CRUD de base .
La couche de service expose la logique métier, qui utilise le référentiel. Un exemple de service pourrait ressembler à ceci:
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);
}
Alors que la List()
méthode du référentiel renvoie tous les utilisateurs, ListUsers()
de IUserService ne peut en renvoyer que ceux, l'utilisateur a accès à.
Dans ASP.NET MVC + EF + SQL SERVER, j'ai ce flux de communication:
Vues <- Contrôleurs -> Couche de service -> Couche de référentiel -> EF -> SQL Server
Couche de service -> Couche de référentiel -> EF Cette partie fonctionne sur les modèles.
Vues <- Contrôleurs -> Couche de service Cette partie fonctionne sur les modèles de vue.
ÉDITER:
Exemple de flux pour / Orders / ByClient / 5 (nous voulons voir la commande pour un client spécifique):
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);
}
}
Ceci est l'interface pour le service de commande:
public interface IOrderService
{
OrdersByClientViewModel GetByClient(int id);
}
Cette interface renvoie le modèle de vue:
public class OrdersByClientViewModel
{
CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
IEnumerable<OrderViewModel> Orders { get; set; }
}
Il s'agit de l'implémentation de l'interface. Il utilise des classes de modèles et un référentiel pour créer un modèle de vue:
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<>
à GenericRepository<>
votre bibliothèque du CIO. Cette réponse est très ancienne. Je pense que la meilleure solution est de combiner tous les référentiels dans une classe appelée UnitOfWork
. Il doit contenir un référentiel de chaque type et une méthode appelée SaveChanges
. Tous les référentiels doivent partager un contexte EF.
Comme l'a dit Carnotaurus, le référentiel est responsable du mappage de vos données du format de stockage vers vos objets métier. Il doit gérer à la fois la lecture et l'écriture des données (suppression, mise à jour également) depuis et vers le stockage.
Le but de la couche de service, d'autre part, est d'encapsuler la logique métier dans un seul endroit pour promouvoir la réutilisation du code et la séparation des préoccupations. Ce que cela signifie généralement pour moi dans la pratique lors de la création de sites Asp.net MVC, c'est que j'ai cette structure
[Le contrôleur] appelle [Service (s)] qui appelle [référentiel (s)]
Un principe que j'ai trouvé utile est de garder la logique au minimum dans les contrôleurs et les référentiels.
Dans les contrôleurs, c'est parce que cela m'aide à me garder SEC Il est très courant que je doive utiliser le même filtrage ou la même logique ailleurs et si je l'ai placé dans le contrôleur, je ne peux pas le réutiliser.
Dans les référentiels, c'est parce que je veux pouvoir remplacer mon stockage (ou ORM) quand quelque chose de mieux se présente. Et si j'ai une logique dans le référentiel, je dois réécrire cette logique lorsque je change le référentiel. Si mon référentiel ne renvoie que IQueryable et que le service effectue le filtrage en revanche, je n'aurai besoin que de remplacer les mappages.
Par exemple, j'ai récemment remplacé plusieurs de mes référentiels Linq-To-Sql par EF4 et ceux où j'étais resté fidèle à ce principe pourraient être remplacés en quelques minutes. Là où j'avais une certaine logique, c'était plutôt une question d'heures.
onBeforeBuildBrowseQuery
et peuvent utiliser le générateur de requêtes pour modifier la requête.
La réponse acceptée (et votée des centaines de fois) présente un défaut majeur. Je voulais le souligner dans le commentaire, mais cela sera simplement enterré là-bas dans 30 commentaires, donc soulignant ici.
J'ai repris une application d'entreprise qui a été construite de cette façon et ma première réaction a été WTH ? ViewModels dans la couche de service? Je ne voulais pas changer la convention parce que des années de développement s'étaient écoulées, alors j'ai continué à renvoyer ViewModels. Boy, cela s'est transformé en cauchemar lorsque nous avons commencé à utiliser WPF. Nous (l'équipe de développeurs) disions toujours: quel ViewModel? Le vrai (celui que nous avons écrit pour le WPF) ou celui des services? Ils ont été écrits pour une application Web et avaient même l' indicateur IsReadOnly pour désactiver la modification dans l'interface utilisateur. Majeur, défaut majeur et tout cela à cause d'un seul mot: ViewModel !!
Avant de faire la même erreur, voici quelques autres raisons en plus de mon histoire ci-dessus:
Renvoyer un ViewModel à partir de la couche de service est un énorme non non. C'est comme dire:
Si vous souhaitez utiliser ces services, il vaut mieux utiliser MVVM et voici le ViewModel que vous devez utiliser. Aie!
Les services supposent qu'ils seront affichés quelque part dans une interface utilisateur. Que faire s'il est utilisé par une application non UI telle que des services Web ou des services Windows?
Ce n'est même pas un vrai ViewModel. Un vrai ViewModel a une observabilité, des commandes, etc. C'est juste un POCO avec un mauvais nom. (Voir mon histoire ci-dessus pour savoir pourquoi les noms sont importants.)
L'application consommatrice est mieux une couche de présentation (les ViewModels sont utilisés par cette couche) et elle comprend mieux C #. Un autre Aïe!
S'il vous plaît, ne faites pas ça!
Habituellement, un référentiel est utilisé comme échafaudage pour peupler vos entités - une couche de service sortira et émettra une demande. Il est probable que vous placiez un référentiel sous votre couche de service.
La couche de référentiel est implémentée pour accéder à la base de données et permet d'étendre les opérations CRUD sur la base de données. Alors qu'une couche de service consiste en la logique métier de l'application et peut utiliser la couche référentiel pour implémenter une certaine logique impliquant la base de données. Dans une application, il est préférable d'avoir une couche de référentiel et une couche de service distinctes. Le fait d'avoir des couches de référentiel et de service séparées rend le code plus modulaire et dissocie la base de données de la logique métier.