Séparer ASP.NET IdentityUser de mes autres entités

11

J'ai une ProjectName.Corebibliothèque contenant toute ma logique métier et mes entités et leur comportement. Il n'y a actuellement aucune relation avec Entity Framework ou tout autre DAL parce que j'aime garder ces choses séparées. Les configurations Entity Framework (à l'aide de l'API Fluent) résident dans un ProjectName.Infrastructureprojet afin de prendre soin de pousser mes entités dans EF. Fondamentalement, je vais dans le sens d'une architecture de type oignon.

Cependant, lors de l'ajout du cadre d'identité ASP.NET dans le mélange, je dois faire ApplicationUserhériter mon entité de la IdentityUserclasse, mais ma ApplicationUserclasse a des relations avec d'autres entités. En héritant de IdentityUserj'introduis une référence à Entity Framework dans mon projet d'entités, le seul endroit où je ne voudrais pas le faire. Le ApplicationUserretrait de la classe du projet d'entités et dans le Infrastructureprojet (car il utilise un système d'identité basé sur Entity Framework) entraînera des références circulaires, ce n'est donc pas la voie à suivre non plus.

Existe-t-il un moyen de contourner cela afin que je puisse garder la séparation nette entre les deux couches en plus de ne pas utiliser ASP.NET Identity?

Steven Thewissen
la source
1
Vous ne pouvez pas créer une interface IApplicationUser dans votre projet Core et conserver l'implémentation dans Infrastructure? À mon avis, sauf si vous créez une API ou si vous devez échanger des implémentations d'interface au moment de l'exécution, conservez tout votre code non UI dans un seul projet. Avoir un tas de projets différents ajoute simplement à vos frais généraux de gestion mentale et de code sans beaucoup d'avantages.
mortalapeman

Réponses:

12

Vous pouvez créer une classe d' utilisateurs qui n'a rien à voir avec l'identité ASP.NET dans votre bibliothèque principale.

public class User {
    public Guid UserId { get; set; }
    public string UserName { get; set; }
    public string EmailAddress { get; set; }
    public string EmailAddressConfirmed { get; set; }
    public string PhoneNumber { get; set; }
    public string PhoneNumberConfirmed { get; set; }
    public string PasswordHash { get; set; }
    public string SecurityStamp { get; set; }

    ...

    public virtual ICollection<Role> Roles { get; set; }
    public virtual ICollection<UserClaim> UserClaims { get; set; }
    public virtual ICollection<UserLogin> UserLogins { get; set; }
}

Si vous utilisez Entity Framework, créez une classe de configuration pour vos entités (facultatif).

internal class UserConfiguration : EntityTypeConfiguration<User>
{
    internal UserConfiguration()
    {
        ToTable("User");

        HasKey(x => x.UserId)
            .Property(x => x.UserId)
            .HasColumnName("UserId")
            .HasColumnType("uniqueidentifier")
            .IsRequired();

        Property(x => x.PasswordHash)
            .HasColumnName("PasswordHash")
            .HasColumnType("nvarchar")
            .IsMaxLength()
            .IsOptional();

        Property(x => x.SecurityStamp)
            .HasColumnName("SecurityStamp")
            .HasColumnType("nvarchar")
            .IsMaxLength()
            .IsOptional();

        Property(x => x.UserName)
            .HasColumnName("UserName")
            .HasColumnType("nvarchar")
            .HasMaxLength(256)
            .IsRequired();

        // EmailAddress, PhoneNumber, ...

        HasMany(x => x.Roles)
            .WithMany(x => x.Users)
            .Map(x =>
            {
                x.ToTable("UserRole");
                x.MapLeftKey("UserId");
                x.MapRightKey("RoleId");
            });

        HasMany(x => x.UserClaims)
            .WithRequired(x => x.User)
            .HasForeignKey(x => x.UserId);

        HasMany(x => x.UserLogins)
            .WithRequired(x => x.User)
            .HasForeignKey(x => x.UserId);
    }
}

Vous devez également créer des classes pour Role, UserClaim et UserLogin . Vous pouvez les nommer comme vous le souhaitez si vous n'aimez pas les noms ci-dessus.

Dans la couche Web, créez une classe appelée AppUser (ou un autre nom si vous le souhaitez). Cette classe doit implémenter l' interface ASP.NET Identity IUser <TKey> , où TKey est le type de données pour la clé primaire ( Guid dans l'exemple ci-dessus).

public class AppUser : IUser<Guid>
{
    public AppUser()
    {
        this.Id = Guid.NewGuid();
    }

    public AppUser(string userName)
        : this()
    {
        this.UserName = userName;
    }

    public Guid Id { get; set; }
    public string UserName { get; set; }
    public string EmailAddress { get; set; }
    public string EmailAddressConfirmed { get; set; }
    public string PhoneNumber { get; set; }
    public string PhoneNumberConfirmed { get; set; }
    public string PasswordHash { get; set; }
    public string SecurityStamp { get; set; }
}

Remplacez toutes les références à UserManager dans le projet Web par UserManager <AppUser, Guid> .

Enfin, créez votre propre UserStore . Essentiellement, le UserStore personnalisé prendra l' objet AppUser , le convertira en objet d'entité User , puis le conservera. Un exemple de l'une de ces méthodes est présenté ci-dessous:

public class UserStore : 
    IUserLoginStore<AppUser, Guid>, 
    IUserClaimStore<AppUser, Guid>, 
    IUserRoleStore<AppUser, Guid>, 
    IUserPasswordStore<AppUser, Guid>, 
    IUserSecurityStampStore<AppUser, Guid>, 
    IUserStore<AppUser, Guid>, 
    IDisposable
{
    private User MapFromAppUser(AppUser appUser)
    {
        if (appUser == null)
            return null;

        var userEntity = new User();

        PopulateUser(userEntity, appUser);

        return userEntity;
    }

    private void PopulateUser(User user, AppUser appUser)
    {
        user.UserId = appUser.Id;
        user.UserName = appUser.UserName;
        user.EmailAddress = appUser.EmailAddress;
        user.EmailAddressConfirmed = appUser.EmailAddressConfirmed;
        user.PhoneNumber = appUser.PhoneNumber;
        user.PhoneNumberConfirmed = appUser.PhoneNumberConfirmed;
        user.PasswordHash = appUser.PasswordHash;
        user.SecurityStamp = appUser.SecurityStamp;

        // First name, last name, ... 
    }

    #region IUserStore<AppUser, Guid> Members

    public Task CreateAsync(AppUser appUser)
    {
        if (appUser == null)
            throw new ArgumentNullException("appUser");

        var userEntity = MapFromAppUser(appUser);

        // Persist the user entity to database using a data repository.
        // I'll leave this to you.
    }

    ...

    #endregion
}

Pour obtenir une description complète d'une implémentation possible, cliquez ici .

En fin de compte, c'est votre choix. Mesurez la quantité d'efforts qu'il vous faudrait pour maintenir cette implémentation au lieu de simplement référencer le cadre d'identité dans votre bibliothèque principale. Personnellement, j'ai pensé à le faire comme je l'ai décrit ci-dessus, mais je ne l'ai pas fait car je devrais potentiellement changer mon code chaque fois que le framework d'identité ASP.NET est mis à jour.

J'espère que cela aide et répond à votre question!

fbhdev
la source