Отделение ASP.NET IdentityUser от других моих сущностей

11

У меня есть ProjectName.Coreбиблиотека, содержащая всю мою бизнес-логику, мои сущности и их поведение. В настоящее время нет никакого отношения к Entity Framework или любому другому DAL, потому что мне нравится держать эти вещи отделенными. Конфигурации Entity Framework (с использованием Fluent API) находятся в ProjectName.Infrastructureпроекте, поэтому он заботится о том, чтобы поместить мои объекты в EF. В основном я иду в направлении луковичной архитектуры.

Тем не менее, при добавлении структуры ASP.NET Identity в смесь я должен заставить свою ApplicationUserсущность наследовать от IdentityUserкласса, но мой ApplicationUserкласс имеет отношения к другим сущностям. Унаследовав от, IdentityUserя представляю ссылку на Entity Framework в моем проекте лица, единственное место, где я бы не хотел этого делать. Извлечение ApplicationUserкласса из проекта сущностей в Infrastructureпроект (поскольку он использует систему идентификации на основе Entity Framework) приведет к циклическим ссылкам, так что это тоже не тот путь.

Есть ли способ обойти это, чтобы я мог сохранить чистое разделение между двумя уровнями, кроме того, не используя ASP.NET Identity?

Стивен Тьюиссен
источник
1
Не можете ли вы создать интерфейс IApplicationUser в своем проекте Core и сохранить реализацию в инфраструктуре? По моему мнению, если вы не создаете API или вам не нужно поменять местами реализации интерфейса во время выполнения, просто храните весь код, не относящийся к пользовательскому интерфейсу, в одном проекте. Наличие множества разных проектов только увеличивает ваши умственные затраты и затраты на управление кодом без особой выгоды.
смертный человек

Ответы:

12

Вы можете создать класс User , который не имеет ничего общего с ASP.NET Identity в вашей основной библиотеке.

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

Если вы используете Entity Framework, создайте класс конфигурации для ваших сущностей (необязательно).

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

Вам также необходимо создать классы для Role, UserClaim и UserLogin . Вы можете назвать их как угодно, если вам не нравятся вышеперечисленные имена.

В веб-слое создайте класс с именем AppUser (или другое имя, если вы выберете). Этот класс должен реализовывать интерфейс ASP.NET Identity IUser <TKey> , где TKey - это тип данных для первичного ключа ( Guid в приведенном выше примере).

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

Измените все ссылки на UserManager в веб-проекте на UserManager <AppUser, Guid> .

Наконец, создайте свой собственный UserStore . По сути, пользовательское хранилище UserStore принимает объект AppUser , преобразует его в объект сущности User и сохраняет его. Пример одного из этих методов показан ниже:

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
}

Чтобы получить полное описание возможной реализации, нажмите здесь .

В конце концов, это ваш выбор. Измерьте количество усилий, которое потребуется вам для поддержки этой реализации, а не просто ссылки на инфраструктуру Identity в вашей базовой библиотеке. Лично я подумал о том, чтобы сделать это так, как я описал выше, однако я этого не сделал, потому что мне потенциально приходилось бы менять свой код каждый раз при обновлении платформы удостоверений ASP.NET.

Надеюсь, это поможет и ответит на ваш вопрос!

fbhdev
источник