L'OMI à la fois l' Repository
de l'abstraction et de l' UnitOfWork
d'abstraction ont une très précieux dans un véritable développement. Les gens diront sur les détails d'implémentation, mais tout comme il existe de nombreuses façons à la peau d'un chat, il existe de nombreuses façons de mettre en œuvre une abstraction.
Votre question est précisément de les utiliser ou ne pas utiliser et pourquoi.
Comme vous l'avez sans doute compris que vous avez déjà ces deux modèles construits dans le Cadre de l'Entité, DbContext
est le UnitOfWork
et DbSet
est le Repository
. Vous n'avez généralement pas besoin de test de l'unité de l' UnitOfWork
ou Repository
eux-mêmes qu'ils sont simplement de faciliter entre les classes et les données sous-jacentes de l'accès des implémentations. Ce que vous trouvez dans le besoin à faire, encore et encore, c'est se moquer de ces deux abstractions lors de tests unitaires de la logique de vos services.
Vous pouvez vous moquer, faux ou que ce soit avec les bibliothèques externes ajoutant des couches de code dépendances (que vous n'avez pas de contrôle) entre la logique de faire l'essai et de la logique en cours d'essai.
Ainsi, un point de détail, c'est que d'avoir votre propre abstraction pour UnitOfWork
et Repository
vous donne un maximum de flexibilité et un contrôle lorsque les moqueries des tests unitaires.
Très bien, mais pour moi, le véritable pouvoir de ces abstractions, c'est qu'ils fournissent un moyen simple d'appliquer la Programmation Orientée Aspects techniques et de respecter les principes SOLIDES.
Donc, vous avez votre IRepository
:
public interface IRepository<T>
where T : class
{
T Add(T entity);
void Delete(T entity);
IQueryable<T> AsQueryable();
}
Et sa mise en œuvre:
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();
}
}
Rien d'extraordinaire jusqu'à présent, mais maintenant, nous voulons ajouter un peu de connexion facile avec un enregistrement Décorateur.
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();
}
}
Tout est fait et sans modifier notre code existant. Il existe de nombreux autres transversale préoccupations, nous pouvons ajouter, tels que la gestion des exceptions, la mise en cache des données, validation des données, ou quoi que ce soit et tout au long de notre conception et le processus de construction de la chose la plus précieuse que nous avons qui nous permet d'ajouter des fonctionnalités simples, sans modification de notre code existant est notre IRepository
d'abstraction.
Maintenant, de nombreuses fois, j'ai vu cette question sur StackOverflow – "comment voulez-vous faire Entité Cadre de travail dans un environnement multi-clients?".
http://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+locataire
Si vous avez un Repository
d'abstraction, alors la réponse est "il est facile d'ajouter un décorateur"
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);
}
}
OMI, vous devez toujours placer une simple abstraction au-dessus de tout la 3ème partie de la composante qui sera référencée dans plus d'une poignée d'endroits. De ce point de vue un ORM est le candidat parfait qu'il est référencé dans de nombreux domaines de notre code.
La réponse qui normalement vient à l'esprit quand quelqu'un dit "pourquoi devrais-je avoir une abstraction (par exemple, Repository
) au cours de cette ou que la 3ème partie de la bibliothèque" est "pourquoi pas vous?"
P. S. les Décorateurs sont extrêmement simples à appliquer à l'aide d'un Conteneur IoC, comme SimpleInjector.
[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>)));
}
}
}