A-t-on besoin d’une Business Layer avec Entity Framework ? (4)

A-t-on besoin d’une Business Layer avec Entity Framework ?

Sommaire : Partie 1Partie 2Partie 3Partie 4 – Partie 5

Bon... Et l'Unit of Work pour Entity Framework ?

Dans l’article précédent, nous avons développé nos Repositories. Il est maintenant temps de passer à l’Unit of Work. Celui-ci nous servira à garantir la cohérence de nos données. Tout ce qui se passe à l’intérieur d’une instance de notre Unit of Work forme un tout cohérent. Dans une application Web, nous allons tendre à avoir des ensembles d’opérations les plus atomiques possibles. C’est pourquoi, nous n’aurons jamais des quantités astronomiques d’opérations durant la vie de ces instances. Dans un projet de type batch, les choses seraient différentes. Gardons à l’esprit que les implémentations des patterns peuvent fortement différer en fonction du besoin fonctionnel et de l’architecture physique de l’application. Il est d’ailleurs parfaitement envisageable de voir cohabiter dans une même application plusieurs types d’implémentation du Repository et de l’Unit of Work si nécessaire.

Revenons-en à nos moutons. Nous cherchons à avoir un Unit of Work qui a un comportement similaire au DbContext d’Entity Framework. Ce dernier est en effet simple à utiliser :

using (var context = new MonDbContext())
{
  context.Publishers.Add(marvel);
  context.Author.Add(frankmiller);
  context.BDs.Add(batmanYearOne);
}

Notre Unit of Work implémentera donc IDisposable, afin de pouvoir être appelé avec un simple using. Nous avons également vu que nous injections notre classe BedethequeContext dans nos Repositories via le constructeur, créant une dépendance forte avec Entity Framework. Nous allons corriger cela. Nous allons plutôt vouloir injecter notre propre Unit of Work. Le code de la classe UnitofWork est la suivante :

using Bedetheque.DAL;
using System;
namespace Bedetheque.Business
{
 public class UnitOfWork : IDisposable
 {
 private readonly BedethequeContext bedethequeContext;
 public UnitOfWork() { bedethequeContext = new Bedetheque(); }
 public UnitOfWork(BedethequeContext context) { bedethequeContext = context; }
 internal BedethequeContext BedethequeContext { get { return bedethequeContext; } }
 public void Save() { bedethequeContext.SaveChanges(); }
 public void Dispose() { bedethequeContext.Dispose(); }
 }
}

C’est tout ? C’est tout. L’Unit of Work est en fait une surcouche à DbContext. Il possède donc une propriété publique BedethequeContext avec uniquement un getter. Le constructeur par défaut créé une nouvelle instance de ce dernier. On s’ouvre également la possibilité de l’injecter à des fins de test. Nous y reviendrons.

La méthode Save() ne fait qu’appeler le SaveChanges() de DbContext. Et enfin, la méthode Dispose désinstancie le contexte pour pouvoir implémenter simplement IDisposable.

La classe AbstractEntityRepository<T> évolue légèrement :

using CaveAVin.DAL;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace CaveAVin.Business
{
 public abstract class AbstractEntityRepository<T> : IRepository<T> where T:class
 {
 private DbSet<T> _dbSet; private CaveAVinContext _caveAVinContext;
 public AbstractEntityRepository(UnitOfWork unitOfWork) { _caveAVinContext = unitOfWork.CaveAVinContext; _dbSet = _caveAVinContext.Set<T>(); }
 public void Add(T entity) { _dbSet.Add(entity); }
 public void Update(T entity) { _caveAVinContext.Entry(entity).State = System.Data.EntityState.Modified; }
 public void Delete(T entity) { _dbSet.Remove(entity); }
 public T GetById(int id) { return _dbSet.Find(id); }
 public IQueryable<T> Get() { return _dbSet; }
 }
}

La seule différence est que l’on injecte désormais un UnitofWork via le constructeur, plutôt qu’un DbContext.

Enfin, voici comment on effectue les opérations sur la base de données :

IEnumerable<Author> result;

using (UnitOfWork uow = new UnitOfWork())
{
 var authorRepository = new AuthorEntityRepository(uow);
 result = authorRepository.Get().Where(a => a.Name == "Frank Miller");
}

Nous y voilà. La couche de présentation ne voit plus du tout Entity Framework. Dans le prochain article, nous nous attaquerons à la testabilité de notre infrastructure. En effet, le grand avantage de ce découplage est de pouvoir nous affranchir de la base de données à des fins de test. Encore un peu de travail, nous y sommes presque.

Pas de commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *