AspNetCore中间件UserManager依赖注入

时间:2017-03-12 01:15:22

标签: asp.net asp.net-core asp.net-identity-3

我有一个多层应用程序,我开始在ASP.NET Core 1.1中编写,我还在学习。我像以前在Web API中完成的应用程序一样组织它,我有主机服务(网络核心应用程序),业务层和数据库之上的数据层。业务和数据层是网络核心标准库,但是当我想添加实体框架时,我不得不修改数据层看起来像网络核心应用程序,所以现在我有配置的Startup.cs。这使我能够配置实体框架服务并在数据层中创建迁移。但是现在我有一个问题,因为我想添加asp.net身份。网上的每个教程都是关于在一个项目中拥有所有内容的SPA。 我已经将身份添加到Startup.cs并且数据库生成良好

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration.GetConnectionString("DefaultConnection");
    services.AddEntityFramework(connectionString);
    services.AddMyIdentity();
    services.Configure<IdentityOptions>(options =>
    {
        // Password settings
        options.Password.RequireDigit = true;
        options.Password.RequiredLength = 8;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = true;
        options.Password.RequireLowercase = false;

        // Lockout settings
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
        options.Lockout.MaxFailedAccessAttempts = 10;


        // User settings
        options.User.RequireUniqueEmail = true;
    });
}
public void Configure(IApplicationBuilder app)
{
    app.UseIdentity();
}

但现在我需要从不是Controller的类中使用UserManager,我不知道如何处理依赖注入。 为了更好地解释,我的主机服务中有一个帐户控制器

[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody]RegisterUserDto dto)
{
    var result = await Business.Commands.Accounts.Register(dto);
    return Ok(result);
}

业务层只调用数据层

public async static Task<ResponseStatusDto> Register(RegisterUserDto dto)
{
    // some code here        
    var identityLogon = await Data.Commands.ApplicationUsers.Register(dto);
    // some code here as well

    return new ResponseStatusDto();
}

现在的问题是,如何在数据寄存器方法中获取UserManager?它是一个简单的类,它不从控制器继承,依赖注入不适用于构造函数,如此处的示例所示 Core Identity

public class AccountController : Controller
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IEmailSender _emailSender;
    private readonly ISmsSender _smsSender;
    private static bool _databaseChecked;
    private readonly ILogger _logger;

    public AccountController(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ISmsSender smsSender,
        ILoggerFactory loggerFactory)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _smsSender = smsSender;
        _logger = loggerFactory.CreateLogger<AccountController>();
    }

    //
    // GET: /Account/Login

那么,如何将Startup中配置的UserManager传递给中间件中的某个随机类呢?我已经看到了这个question,但只是将空值传递给UseManager构造函数的答案不起作用,我认为这也不错。

//根据Set的回答编辑

我删除了所有静态引用,但我仍然不在那里。我遵循了this依赖注入指令,但我不确定如何实例化并调用Add方法。

我创建了一个界面

public interface IIdentityTransaction
{
    Task<IdentityResult> Add(ApplicationUser appUser, string password);
}

并实施它

public class IdentityTransaction : IIdentityTransaction
{
    private readonly ApplicationDbContext _dbContext;

    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;


    public IdentityTransaction(ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        _roleManager = roleManager;
        _userManager = userManager;
        _dbContext = context;
    }

    public async Task<IdentityResult> Add(ApplicationUser applicationUser, string password)
    {
        return await _userManager.CreateAsync(applicationUser, password);
    }

}

然后我将它注入Startup.cs中的服务集合

services.AddScoped<IIdentityTransaction, IdentityTransaction>();

但如何从IdentityTransaction服务调用Add方法?

enter image description here

我无法实例化它,也不能在构造函数上使用依赖注入,因为它只是循环我的问题。 @Set提到

  

或将UserManager userManager作为参数传递给方法   从哪里传递?

我觉得我非常接近,但我错过了一些东西。 我尝试过使用

    IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();

但是IServiceProvider的服务是null,我不知道从哪里得到它。

2 个答案:

答案 0 :(得分:2)

ASP.NET Core中的DI使用“构造函数注入”方法对控制器和非控制器类的工作方式相同。

由于Register方法为static,因此无法访问实例变量/属性。你需要

  • 使Register方法非静态
  • 或将UserManager<ApplicationUser> userManager作为参数传递给方法

通常,您应该避免对业务逻辑使用静态类,因为它们无法正确测试代码并产生代码耦合。通过internet / SO搜索,你会发现很多主题为什么静态是坏的。

使用DI在控制器中获取Data.Commands.ApplicationUsers类的实例。如果您的应用程序只需要此类的一个实例 - 请使用单例生存期。

更新。再次,使用构造函数注入:修改“数据层”类,以便它可以获取IIdentityTransaction的实例作为构造函数参数:

public class YourDataLayerClass : IYourDataLayerClass
{
    private IIdentityTransaction _identityTransaction;
    public YourDataLayerClass(IIdentityTransaction identityTransaction)
    {
       _identityTransaction = identityTransaction;
    }

    public void MethodWhereYouNeedToCallAdd()
    {
        _identityTransaction.Add(...);
    }
}

IYourDataLayerClass实例的想法相同:注册依赖

services.AddScoped<IYourDataLayerClass, YourDataLayerClass>();

然后依赖于它的类(在你的情况下是中间件,如果我理解你的话)应该通过构造函数接收该实例:

public class YourMiddleware
{
    private IYourDataLayerClass _yourDataLayerClass;
    public YourMiddleware(IYourDataLayerClass yourDataLayerClass)
    {
       _yourDataLayerClass = yourDataLayerClass;
    }
    ...
}

答案 1 :(得分:1)

是的,你非常接近。

首先,要么从IdentityTransaction构造函数中移除context参数,就像在代码中剪切它似乎没用。或者如果您打算稍后使用它,请在DI容器中声明它:

services.AddScoped<ApplicationDbContext, ApplicationDbContext>();

其次,你只需要在控制器的构造函数中添加IIdentityTransaction作为依赖项,并从其依赖项中删除SignInManager和UserManager,因为最终你不会直接在控制器中使用它们:

public class AccountController : Controller
{
    private readonly IEmailSender _emailSender;
    private readonly ISmsSender _smsSender;
    private static bool _databaseChecked;
    private readonly ILogger _logger;
    IIdentityTransaction _identityTransaction;

    public AccountController(
        IEmailSender emailSender,
        ISmsSender smsSender,
        ILoggerFactory loggerFactory,
        IIdentityTransaction identityTransaction)
    {
        _emailSender = emailSender;
        _smsSender = smsSender;
        _logger = loggerFactory.CreateLogger<AccountController>();
        _identityTransaction = identityTransaction;
    }

如果在控制器,同一进程之间需要一个额外的业务层(IBusinessLayer),在启动时在DI容器中声明该类,则在业务类构造函数中添加IIdentityTransaction作为依赖项,并更新控制器的依赖项从IIdentityTransaction到IBusinessLayer。

更精确的几个。

services.AddScoped<IIdentityTransaction, IdentityTransaction>();

这段代码不会注入实例或依赖项。它在DI容器中声明了一个接口及其相关的实现,因此可以在需要时稍后注入。实际实例是在实际创建需要它们的对象时注入的。即控制器在实例化时会注入其依赖项。

 IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();

您在此处尝试执行的操作称为依赖关系定位器模式,通常被视为反模式。你应该通过构造函数坚持依赖注入,它更清洁。

关键是在启动时声明DI容器中的所有内容,即使是自定义的业务/数据层类,也不要再自己实例化它们,并在任何类中将它们声明为必需的依赖项&#39;需要它们的构造函数。