web-dev-qa-db-ja.com

ASP.NET IdentityUserを他のエンティティから分離する

ProjectName.Coreライブラリには、すべてのビジネスロジックとエンティティ、およびそれらの動作が含まれています。 Entity Frameworkや他のDALとは何の関係もありません。それらを分離しておくのが好きだからです。 Entity Frameworkの構成(Fluent APIを使用)はProjectName.Infrastructureプロジェクト。これにより、エンティティをEFにプッシュすることができます。基本的には、オニオンのようなアーキテクチャの方向に進んでいます。

ただし、ASP.NET Identityフレームワークをミックスに追加する場合、ApplicationUserエンティティをIdentityUserクラスから継承する必要がありますが、ApplicationUserクラスは他のエンティティと関係があります。 IdentityUserからの継承では、エンティティプロジェクトにEntity Frameworkへの参照を導入しています。これは、そうしたくない場所の1つです。 Entity FrameworkベースのIDシステムを使用しているため、ApplicationUserクラスをエンティティプロジェクトからInfrastructureプロジェクトにプルすると、循環参照が発生するため、これを行う方法ではありませんどちらかに行きます。

これを回避する方法はありますか?ASP.NET Identityを使用しない以外に、2つのレイヤー間のクリーンな分離を維持できますか?

11

コアライブラリにASP.NET Identityとは関係のないserクラスを作成できます。

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のクラスも作成する必要があります。上記の名前が気に入らない場合は、好きな名前を付けることができます。

Webレイヤーで、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; }
}

Webプロジェクト内のUserManagerへのすべての参照をserManager <AppUser、Guid>に変更します。

最後に、独自のserStoreを作成します。基本的に、カスタムUserStoreはAppUserオブジェクトを受け取り、それをserエンティティオブジェクトに変換して永続化します。これらのメソッドの1つの例を以下に示します。

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 Identityフレームワークが更新されるたびにコードを変更する必要がある可能性があるため、そうしませんでした。

うまくいけば、これがあなたの質問に役立ち、答えます!

12
fbhdev