web-dev-qa-db-ja.com

DIにカスタムUserStoreとUserManagerを登録する方法

これが私の設定です:

public class ApplicationUser : IdentityUser<Guid>
{
}
public class ApplicationRole : IdentityRole<Guid>
{
}
public class ApplicationUserLogin : IdentityUserLogin<Guid>
{
}
public class ApplicationUserClaim : IdentityUserClaim<Guid>
{
}
public class ApplicationRoleClaim : IdentityRoleClaim<Guid>
{
}

これが私のUserStoreの定義です

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, MyContext, Guid>
{
    public ApplicationUserStore(MyContext context, IdentityErrorDescriber describer = null)
        : base(context, describer)
    {
    }
}

これが私のUserManagerの定義です

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IEnumerable<IUserTokenProvider<ApplicationUser>> tokenProviders,
        ILoggerFactory logger, IHttpContextAccessor contextAccessor)
        : base(
            store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
            tokenProviders, logger, contextAccessor)
    {
    }
}

これが私のDbContextの定義です:

public class MyContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

そして、これが私のStartup.csです

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.Get("Data:DbConnection")));

        services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<MyContext, Guid>()
            .AddUserStore<ApplicationUserStore>()
            .AddRoleStore<ApplicationRoleStore>()
            .AddUserManager<ApplicationUserManager>()
            .AddRoleManager<ApplicationRoleManager>()
            .AddDefaultTokenProviders();

        var builder = new ContainerBuilder();
        builder.Populate(services);
        var container = builder.Build();
        return container.Resolve<IServiceProvider>();
    }

このコンストラクターの依存関係は機能します。

public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)

これはしません:

public AccountController(ApplicationUserManager userManager, SignInManager<ApplicationUser> signInManager)

誰かが私が間違っていることについて考えを持っていますか?

14
JuChom

DIは一般に、インターフェース主導の開発を目的としています。 .AddUserManager<ApplicationUserManager>()は、サービスインターフェイスではなく、実装UserManager<>を指定します。つまり、UserManager<ApplicationUser>を取得して、その方法でのみ使用することを期待しています。 ApplicationUserManagerが表示されます。

ApplicationUserManagerで使用したい追加のメソッドがあると想定しています。 そうでない場合依存関係コンストラクターをその動作方法で使用し、インターフェイス駆動型開発を楽しんでください。そうであれば、次のようになります。 3つのオプション:

  1. 継承ではなく構成を介して拡張を使用します。UserManager<>から継承するのではなく、ApplicationUserManagerをラッパークラスとして記述します。コンストラクタに含めることができます。これにより、ApplicationUserManager内で必要なすべての機能が提供されます。

  2. DIフレームワークにそのまま追加します。UserManager<>には実際の状態がないため、これは思ったほど難しくありません。

    services.AddScoped<ApplicationUserManager>();
    

    ここでの欠点は、ユーザーのスコープに実際に2つのUserManager<>オブジェクトがあることです。その結果、いくつかの非効率性が生じる可能性があります。現在のコードの状態からすると、そうではないと思います。

  3. 拡張メソッドとして記述します。UserManager<>の基本機能だけでなく、多数の依存関係がある場合、これは非常に複雑になる可能性があります。

10
Matt DeKrey

現在ASP.NET Core 1.1を使用しており、この動作は修正されています。

自分のUserManagerとUserStoreを簡単に実装でき、bootstrapアプリは次のようになります:

// identity models
services
    .AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
    .AddUserManager<ApplicationUserManager>()
    .AddUserStore<ApplicationUserStore>()
    .AddDefaultTokenProviders();

userManagerとUserStoreの両方を問題なくコントローラーに挿入します。

public AccountController(
    IIdentityServerInteractionService interaction,
    IClientStore clientStore,
    IHttpContextAccessor httpContextAccessor,
    ApplicationUserManager userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)
{
    _interaction = interaction;
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
    _account = new AccountService(_interaction, httpContextAccessor, clientStore);
}
5
Raffaeu

私はこれを思いついた:

// Extract IApplicationUserManager interface with all methods you are using
public class ApplicationUserManager : UserManager<ApplicationUser>, IApplicationUserManager

// Register your custom user manager
services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddUserManager<ApplicationUserManager>();

// Return same scoped instance whenever you injecting your custom user manager into any constructor
services.AddScoped<IApplicationUserManager>(s => s.GetService<ApplicationUserManager>());
0