Separare ASP.NET IdentityUser dalle altre mie entità


11

Ho una ProjectName.Corebiblioteca contenente tutta la mia logica aziendale, le mie entità e il loro comportamento. Al momento non esiste alcuna relazione con Entity Framework o altri DAL perché mi piace mantenere separate queste cose. Le configurazioni Entity Framework (usando l'API Fluent) risiedono in un ProjectName.Infrastructureprogetto in modo che si occupi di spingere le mie entità in EF. Fondamentalmente vado nella direzione di un'architettura simile a una cipolla.

Tuttavia, quando si aggiunge il framework di identità ASP.NET nel mix, devo rendere la mia ApplicationUserentità ereditata dalla IdentityUserclasse ma la mia ApplicationUserclasse ha relazioni con altre entità. In eredità da IdentityUsersto introducendo un riferimento a Entity Framework nel mio progetto di entità, l'unico posto in cui non vorrei farlo. ApplicationUserEstrarre la classe dal progetto entità e nel Infrastructureprogetto (poiché utilizza un sistema di identità basato su Entity Framework) si tradurrà in riferimenti circolari, quindi non è nemmeno la strada da percorrere.

C'è un modo per aggirare questo in modo che io possa mantenere la netta separazione tra i due livelli oltre a non usare ASP.NET Identity?


1
Non è possibile creare un'interfaccia IApplicationUser nel progetto Core e mantenere l'implementazione in Infrastruttura? A mio avviso, a meno che non si stia creando un'API o non sia necessario scambiare le implementazioni dell'interfaccia in fase di esecuzione, è sufficiente conservare tutto il codice non UI in un progetto. Avere un sacco di progetti diversi non fa che aumentare il tuo sovraccarico di gestione mentale e di codice senza molti benefici.
mortalapeman

Risposte:


12

È possibile creare una classe utente che non ha nulla a che fare con l'identità ASP.NET nella libreria 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; }
}

Se si utilizza Entity Framework, creare una classe di configurazione per le entità (facoltativo).

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);
    }
}

Dovresti anche creare classi per Role, UserClaim e UserLogin . Puoi nominarli come preferisci se non ti piacciono i nomi sopra.

Nel livello Web, crea una classe chiamata AppUser (o un altro nome se lo desideri). Questa classe dovrebbe implementare l' interfaccia <TKey> di ASP.NET Identity IUser , dove TKey è il tipo di dati per la chiave primaria ( Guid nell'esempio precedente).

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; }
}

Modificare tutti i riferimenti a UserManager nel progetto Web in UserManager <AppUser, Guid> .

Infine, crea il tuo UserStore . In sostanza, lo UserStore personalizzato prenderà l' oggetto AppUser , lo convertirà in un oggetto entità User , quindi lo persisterà. Di seguito è mostrato un esempio di uno di questi metodi:

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
}

Per ottenere una descrizione completa di una possibile implementazione, fai clic qui .

Alla fine, è una tua scelta. Misura la quantità di sforzo necessaria per mantenere questa implementazione rispetto al semplice riferimento al framework Identity nella tua libreria Core. Personalmente, ho pensato di farlo nel modo che ho descritto sopra, tuttavia, non l'ho fatto perché potrei potenzialmente cambiare il mio codice ogni volta che il framework di identità ASP.NET viene aggiornato.

Spero che questo aiuti e risponda alla tua domanda!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.