web-dev-qa-db-fra.com

Identité ASP.NET avec référentiel et unité de travail

J'apprends les modèles de référentiel et d'unité de travail dans l'application ASP.NET MVC 5 avec Entity Framework 6.

J'avais déjà lu beaucoup de tutoriels et d'articles, mais presque tous sont contradictoires. Certains disent que les modèles de référentiel et d'unité de travail sont bons, d'autres disent que DbContext est déjà un référentiel et une unité de travail, d'autres disent quelque chose de similaire, mais offrent une approche complètement différente. J'ai essayé toutes ces différentes approches (enfin, peut-être pas toutes) et j'ai toujours du mal à déterminer quelle approche est la plus correcte.

Ce que j'ai actuellement c'est:

  • IRepository et GenericRepository implémentant IRepository
  • IUnitOfWork et UnitOfWork implémentant IUnitOfWork
  • IDbContext et MyDbContext hérités d'IdentityDbContext et implémentant IDbContext

Je ne sais pas si j'ai besoin de coller le code pour cela, je pense que c'est assez générique et le problème n'est pas avec Repository/UnitOfWork en tant que tel. Le problème que j'ai est d'utiliser des classes d'identité ASP.NET en combinaison avec mes référentiels et mon unité de travail. Je partage la même base de données pour l'adhésion et pour toutes les autres données - et je pense que c'est un scénario courant. Je ne trouve pas la bonne solution, comment puis-je instancier des classes d'identité ASP.NET à l'aide de mes référentiels.

UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(_DBCONTEXT_);
this.UserManager = new UserManager<ApplicationUser>(store);

Que dois-je mettre à la place de DBCONTEXT , afin qu'il partage le même DbContext avec mon UnitOfWork? Ou comment cela peut-il être fait d'une autre manière pour que l'identité ASP.NET fonctionne avec UnitOfWork?

J'ai essayé d'exposer DbContext en tant que propriété publique de la classe UnitOfWork, quelque chose comme:

UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(this.unitOfWork.MyDbContext);

Cependant, je ne pense pas que ce soit correct - cela ne fonctionne pas avec l'interface IDbContext personnalisée et rend le code non bon pour les tests unitaires.

J'ai également essayé d'implémenter CustomUserStore et CustomRoleStore - en général cela a fonctionné, mais pendant que je le testais, il fallait implémenter de plus en plus de méthodes. Cette solution semble trop compliquée - j'espère vraiment qu'il devrait y avoir un moyen plus simple.

34
Aleksanderis

"Un problème à prendre en compte est que la classe UserStore ne fonctionne pas correctement lors de l'utilisation du modèle de conception d'unité de travail. Plus précisément, UserStore appelle SaveChanges dans presque tous les appels de méthode par défaut, ce qui facilite la validation prématurée d'une unité de travail . Pour modifier ce comportement, modifiez l'indicateur AutoSaveChanges sur le UserStore. "

var store = new UserStore<ApplicationUser>(new ApplicationDbContext());
store.AutoSaveChanges = false;

De Scott Allen: http://odetocode.com/blogs/scott/archive/2014/01/03/asp-net-identity-with-the-entity-framework.aspx

4
stuartdotnet

J'ai trouvé une sorte de solution, qui semble assez générique, mais je ne sais toujours pas si elle est vraiment bonne et ne rompt pas les principes du modèle Repository/UnitOfWork.

J'ai ajouté la méthode générique GetDbContext () à mon IUnitOfWork:

public interface IUnitOfWork : IDisposable
{
   void Save();    
   IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;    
   TContext GetDbContext<TContext>() where TContext : DbContext, IDbContext;
}

Son implémentation dans la classe UnitOfWork:

public class UnitOfWork<TContext> : IUnitOfWork where TContext : IDbContext, new()
{
    private IDbContext dbContext;

    public UnitOfWork()
    {
        this.dbContext = new TContext();
    }

    public T GetDbContext<T>() where T : DbContext, IDbContext
    {
        return this.dbContext as T;
    }

    ...
}

Comment il est utilisé dans un contrôleur, initialisant UserManager:

public class AccountController : ControllerBase
{
    private readonly IUnitOfWork unitOfWork;

    public UserManager<ApplicationUser> UserManager { get; private set; }

    public AccountController()
        : this(new UnitOfWork<MyDbContext>())
    {
    }

    public AccountController(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;    
        UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(unitOfWork.GetDbContext<MyDbContext>());
        this.UserManager = new UserManager<ApplicationUser>(store);
    }

    ...
}

Je soupçonne que GetDbContext () sera utilisé juste pour contourner certaines difficultés avec ASP.Identity, donc ce n'est pas si mal ..

4
Aleksanderis

Si vous utilisez le référentiel et le modèle UnitofWork, vous l'utilisez peut-être avec DDD (Domain Driven Design) où vous déclarez IRepository ou IUnitofWork dans Core project avec tous les autres modèles de domaine et classes abstraites.

Vous créez maintenant Projet d'infrastructure qui implémente ces interfaces dans le projet Core en utilisant un objet d'accès aux données concret pour cette instance Entity Framework. donc DbContext est bien là mais oui ne l'exposez pas à la couche de présentation. Donc, à un moment donné, si vous souhaitez modifier EF en un autre ORM, cela sera plus facile sans toucher à la couche de présentation où vous placez vos classes d'identité distinctes du projet Data Access ou Infrastructure. Et bien sûr, vous pouvez utiliser le conteneur IOC pour instancier ces référentiels concrets de l'infrastructure dans la couche Contrôleurs de présentation).

2
sir4ju1