Oddzielanie ASP.NET IdentityUser od moich innych podmiotów

11

Mam ProjectName.Corebibliotekę zawierającą całą moją logikę biznesową oraz moje podmioty i ich zachowanie. Obecnie nie ma żadnego związku z Entity Framework lub jakimkolwiek innym DAL, ponieważ lubię, aby te rzeczy były oddzielone. Konfiguracje Entity Framework (przy użyciu Fluent API) znajdują się w ProjectName.Infrastructureprojekcie, który zajmuje się popychaniem moich jednostek do EF. Zasadniczo zmierzam w kierunku architektury podobnej do cebuli.

Jednak podczas dodawania frameworku tożsamości ASP.NET do miksu muszę sprawić, by moja ApplicationUserjednostka dziedziczyła po IdentityUserklasie, ale moja ApplicationUserklasa ma relacje z innymi jednostkami. Dziedzicząc po IdentityUser, wprowadzam odniesienie do Entity Framework w moim projekcie encji, jedynym miejscu, w którym nie chciałbym tego robić. Wyciągnięcie ApplicationUserklasy z projektu encji do Infrastructureprojektu (ponieważ korzysta ona z systemu tożsamości opartego na Entity Framework) spowoduje powstanie referencji cyklicznych, więc nie jest to również dobra droga.

Czy jest jakiś sposób, aby to obejść, dzięki czemu mogę zachować czystą separację między dwiema warstwami poza tym, że nie używam tożsamości ASP.NET?

Steven Thewissen
źródło
1
Czy nie możesz utworzyć interfejsu IApplicationUser w swoim głównym projekcie i zachować implementację w infrastrukturze? Moim zdaniem, o ile nie tworzysz interfejsu API lub nie musisz wymieniać implementacji interfejsu w czasie wykonywania, po prostu przechowuj cały kod inny niż interfejs użytkownika w jednym projekcie. Posiadanie wielu różnych projektów po prostu zwiększa koszty zarządzania pamięcią i kodem bez większych korzyści.
mortalapeman

Odpowiedzi:

12

Możesz utworzyć klasę użytkownika , która nie ma nic wspólnego z tożsamością ASP.NET w bibliotece podstawowej.

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

Jeśli używasz Entity Framework, utwórz klasę konfiguracji dla swoich encji (opcjonalnie).

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

Będziesz musiał także utworzyć klasy dla Roli, UserClaim i UserLogin . Możesz nazwać je, jak chcesz, jeśli nie lubisz powyższych nazwisk.

W warstwie internetowej utwórz klasę o nazwie AppUser (lub inną nazwę, jeśli wybierzesz). Ta klasa powinna implementować interfejs ASP.NET Identity IUser <TKey> , gdzie TKey jest typem danych dla klucza podstawowego ( Guid w powyższym przykładzie).

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

Zmień wszystkie odniesienia do UserManager w projekcie internetowym na UserManager <AppUser, Guid> .

Na koniec utwórz własny magazyn użytkownika . Zasadniczo niestandardowy magazyn użytkownika pobierze obiekt AppUser , przekształci go w obiekt encji użytkownika , a następnie utrwali . Przykład jednej z tych metod pokazano poniżej:

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
}

Aby uzyskać pełny opis możliwej implementacji, kliknij tutaj .

W końcu to twój wybór. Zmierz wysiłek włożony w utrzymanie tej implementacji, a nie tylko odwołanie się do struktury tożsamości w bibliotece Core. Osobiście myślałem o zrobieniu tego w sposób opisany powyżej, jednak tego nie zrobiłem, ponieważ potencjalnie musiałbym zmieniać kod przy każdej aktualizacji środowiska tożsamości ASP.NET.

Mam nadzieję, że to pomoże i odpowie na twoje pytanie!

fbhdev
źródło