Memisahkan ASP.NET IdentityUser dari entitas saya yang lain

11

Saya memiliki ProjectName.Coreperpustakaan yang berisi semua logika bisnis dan entitas saya serta perilaku mereka. Saat ini tidak ada hubungan apa pun dengan Entity Framework atau DAL lainnya karena saya suka memisahkan hal-hal itu. Konfigurasi Entity Framework (menggunakan API Lancar) berada di ProjectName.Infrastructureproyek sehingga mengurus mendorong entitas saya ke EF. Pada dasarnya saya akan ke arah arsitektur seperti bawang.

Namun, ketika menambahkan kerangka ASP.NET Identity ke dalam campuran, saya harus membuat ApplicationUserentitas saya mewarisi dari IdentityUserkelas tetapi ApplicationUserkelas saya memiliki hubungan dengan entitas lain. Dalam mewarisi dari IdentityUsersaya memperkenalkan referensi ke Kerangka Entitas dalam proyek entitas saya, satu tempat di mana saya tidak ingin melakukannya. Menarik ApplicationUserkelas keluar dari proyek entitas dan ke dalam Infrastructureproyek (karena menggunakan sistem identitas berbasis Entity Framework) akan menghasilkan referensi melingkar, jadi itu juga bukan cara untuk pergi.

Apakah ada cara untuk mengatasi hal ini sehingga saya dapat menjaga pemisahan yang bersih antara kedua lapisan selain tidak menggunakan ASP.NET Identity?

Steven Thewissen
sumber
1
Tidak bisakah Anda membuat antarmuka IApplicationUser dalam proyek Core Anda dan tetap menerapkannya dalam Infrastruktur? Menurut pendapat saya, kecuali Anda membuat API atau Anda perlu menukar implementasi antarmuka saat runtime, cukup simpan semua kode non UI Anda dalam satu proyek. Memiliki banyak proyek yang berbeda hanya menambah overhead manajemen mental dan kode Anda tanpa banyak manfaat.
mortalapeman

Jawaban:

12

Anda bisa membuat kelas pengguna yang tidak ada hubungannya dengan ASP.NET Identity di pustaka inti Anda.

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

Jika Anda menggunakan Entity Framework, buat kelas konfigurasi untuk entitas Anda (opsional).

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

Anda juga harus membuat kelas untuk Peran, UserClaim, dan UserLogin . Anda dapat memberi nama mereka apa pun yang Anda pilih jika Anda tidak menyukai nama-nama di atas.

Di lapisan web, buat kelas yang disebut AppUser (Atau nama lain jika Anda memilih). Kelas ini harus mengimplementasikan antarmuka ASP.NET Identity IUser <TKey> , di mana TKey adalah tipe data untuk kunci utama ( Guid dalam contoh di atas).

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

Ubah semua referensi ke UserManager di proyek web ke UserManager <AppUser, Guid> .

Terakhir, buat UserStore Anda sendiri . Pada dasarnya, UserStore kustom akan mengambil objek AppUser , mengubahnya menjadi objek entitas Pengguna , lalu bertahan. Contoh dari salah satu metode ini ditunjukkan di bawah ini:

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
}

Untuk mendapatkan deskripsi lengkap tentang kemungkinan implementasi, klik di sini .

Pada akhirnya, itu pilihan Anda. Ukur jumlah upaya yang diperlukan bagi Anda untuk mempertahankan implementasi ini versus hanya merujuk kerangka kerja Identity di perpustakaan Core Anda. Secara pribadi, saya berpikir untuk melakukannya dengan cara yang saya jelaskan di atas, namun, saya tidak melakukannya karena saya berpotensi harus mengubah kode saya setiap kali kerangka ASP.NET Identity diperbarui.

Semoga ini membantu dan menjawab pertanyaan Anda!

fbhdev
sumber