Utiliser Entity Framework avec Castle Windsor

J'utilise L'approche Entity Framework database-first pour générer un modèle DbContext / POCO pour une application MVC. Je veux éviter d'avoir des dépendances sur DbContext dans mes contrôleurs pour me permettre de passer à un autre fournisseur de persistance comme j'en ai besoin (par exemple à des fins de test unitaire).

Pour ce faire, je veux utiliser le conteneur IoC Castle Windsor. Je prévois d'enregistrer DbContext en tant que service IUnitOfWork et d'enregistrer un service irepository générique, dont les implémentations I utilisera pour accéder et travailler avec des racines agrégées dans le modèle.

Je suis nouveau à Windsor et je n'ai pas été en mesure de trouver beaucoup d'informations sur son utilisation avec EF, et j'ai quelques questions:

  • est-ce une approche raisonnable si je veux découpler EF de l'application?
  • Comment installer / enregistrer les services iunitofwork et generic IRepository?
24
demandé sur Paul Taylor 2013-03-27 12:32:33

1 réponses

Donc, quelques conclusions. J'ai pensé que j'écrirais ceci au cas où il serait utile à quelqu'un d'autre essayant d'utiliser / unit test EF, Windsor et MVC ensemble.

Tout d'abord, comme DbContext implémente à la fois le référentiel et L'Unité de travail, vous devez déterminer si ces implémentations serviront ou si vous devez créer les vôtres.

J'ai choisi de créer mon propre référentiel, en suivant le modèle DDD: un par Racine d'agrégat. Les raisons: pour encapsuler le code de requête, pour empêchez-le de fuir dans la couche d'application et de pouvoir se moquer plus facilement lors du test des contrôleurs d'application. J'ai créé un référentiel Générique basé sur IRepository<TEntity>. Il y a beaucoup d'exemples. J'ai trouvé cela un bon: http://architects.dzone.com/articles/implementing-repository

D'autre part, j'ai décidé de supprimer le service IUnitOfWork, en optant pour l'implémentation par défaut à la place. Cependant, j'ai créé une abstraction IDbContext (Je ne sais pas pourquoi Microsoft ne l'a pas fait eux-mêmes), de sorte que je pouvais me moquer du DbContext lors du test des services de référentiel.

J'ai donné à IDbContext uniquement les membres de DbContext que je voulais utiliser dans le référentiel. Donc:

public interface IDbContext: IDisposable
{
    Database Database { get; }
    DbEntityEntry Entry(object entity);
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveChanges();
}

J'ai ensuite créé une installation et un programme D'installation de Windsor pour mes services IDbContext et IRepository:

public class EntityFrameworkFacility: AbstractFacility
{
    protected override void Init()
    {
        Kernel.Register(Component.For<IDbContext>()
                                 .ImplementedBy<MyEntities>()
                                 .LifestylePerWebRequest(),
                        Component.For(typeof(IRepository<>))
                                 .ImplementedBy(typeof(Repository<>))
                                 .LifestylePerWebRequest());
    }
}

public class PersistenceInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<EntityFrameworkFacility>();
    }
}

La dernière étape consistait à étendre la classe de contexte Entity Framework pour implémenter IDbContext et à masquer la méthode Set () Pour renvoyer IDbSet plutôt que DbSet:

public partial class MyEntities : IDbContext
{
    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }
}

Avec tout cela en place (et l'enregistrement ControllerFactory illustré dans les documents Windsor), il devient trivial d'amener Windsor à injecter des objets IRepository (ou IDbContext) dans les constructeurs de contrôleurs, comme requis:

public ControllerBase(IRepository<Contact> repo)
{
    _repo = repo;
}

Dans les tests unitaires du référentiel, une instance réelle du référentiel peut être sauvegardée avec un IDbContext fictif:

mocks = new MockRepository();
context = mocks.StrictMock<IDbContext>();
repo = new Repository<Contact>(context);

Dans les tests unitaires du contrôleur, un référentiel fictif peut être utilisé:

mocks = new MockRepository();
repo = mocks.StrictMock<IRepository<Contact>>();
ContactController controller = new ContactController(repo);
25
répondu Paul Taylor 2013-03-29 10:57:54