Trennen von ASP.NET IdentityUser von meinen anderen Entitäten

11

Ich habe eine ProjectName.CoreBibliothek, die meine gesamte Geschäftslogik und meine Entitäten sowie deren Verhalten enthält. Derzeit gibt es keinerlei Beziehung zu Entity Framework oder einem anderen DAL, da ich diese Dinge gerne getrennt halte. Die Entity Framework-Konfigurationen (unter Verwendung der Fluent-API) befinden sich in einem ProjectName.InfrastructureProjekt, sodass meine Entitäten in EF verschoben werden. Grundsätzlich gehe ich in Richtung einer zwiebelartigen Architektur.

Wenn ich jedoch das ASP.NET-Identitätsframework zum Mix hinzufüge, muss ich meine ApplicationUserEntität von der IdentityUserKlasse erben lassen, aber meine ApplicationUserKlasse hat Beziehungen zu anderen Entitäten. Beim Erben von führe IdentityUserich in meinem Entities-Projekt einen Verweis auf Entity Framework ein, den einen Ort, an dem ich dies nicht tun möchte. Das Herausziehen der ApplicationUserKlasse aus dem Entities-Projekt in das InfrastructureProjekt (da ein auf Entity Framework basierendes Identitätssystem verwendet wird) führt zu Zirkelverweisen, sodass dies auch nicht der richtige Weg ist.

Gibt es eine Möglichkeit, dies zu umgehen, damit ich die saubere Trennung zwischen den beiden Ebenen beibehalten kann, ohne ASP.NET Identity zu verwenden?

Steven Thewissen
quelle
1
Können Sie in Ihrem Kernprojekt keine IApplicationUser-Schnittstelle erstellen und die Implementierung in Infrastructure beibehalten? Meiner Meinung nach, wenn Sie keine API erstellen oder Schnittstellenimplementierungen zur Laufzeit austauschen müssen, behalten Sie einfach Ihren gesamten Nicht-UI-Code in einem Projekt. Wenn Sie eine Reihe verschiedener Projekte haben, erhöht sich Ihr Aufwand für das mentale und Code-Management ohne großen Nutzen.
Mortalapeman

Antworten:

12

Sie können eine Benutzerklasse erstellen , die nichts mit ASP.NET Identity in Ihrer Kernbibliothek zu tun hat.

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

Wenn Sie Entity Framework verwenden, erstellen Sie eine Konfigurationsklasse für Ihre Entitäten (optional).

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

Sie müssten auch Klassen für Role, UserClaim und UserLogin erstellen . Sie können sie beliebig benennen, wenn Ihnen die oben genannten Namen nicht gefallen.

Erstellen Sie in der Webebene eine Klasse namens AppUser (oder einen anderen Namen, wenn Sie dies wünschen). Diese Klasse sollte die ASP.NET Identity IUser <TKey> -Schnittstelle implementieren, wobei TKey der Datentyp für den Primärschlüssel ist ( Guid im obigen Beispiel).

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

Ändern Sie alle Verweise auf UserManager im Webprojekt in UserManager <AppUser, Guid> .

Erstellen Sie schließlich Ihren eigenen UserStore . Im Wesentlichen nimmt der benutzerdefinierte UserStore das AppUser- Objekt auf, konvertiert es in ein User- Entity-Objekt und behält es dann bei. Ein Beispiel für eine dieser Methoden ist unten dargestellt:

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
}

Klicken Sie hier, um eine vollständige Beschreibung einer möglichen Implementierung zu erhalten .

Am Ende haben Sie die Wahl. Messen Sie den Aufwand, den Sie für die Wartung dieser Implementierung benötigen, anstatt nur auf das Identity-Framework in Ihrer Core-Bibliothek zu verweisen. Persönlich habe ich darüber nachgedacht, es so zu machen, wie ich es oben beschrieben habe, aber ich habe es nicht getan, weil ich möglicherweise meinen Code jedes Mal ändern müsste, wenn das ASP.NET Identity Framework aktualisiert wird.

Hoffentlich hilft das und beantwortet Ihre Frage!

fbhdev
quelle