AspNetCore 中间件 UserManager 依赖注入
AspNetCore Middleware UserManager Dependency Injection
我有一个多层应用程序,我开始在 ASP.NET Core 1.1 中编写它,我一直在学习它。我像以前在 Web API 中完成的应用程序一样组织它,我有主机服务(网络核心应用程序)、业务层和数据库之上的数据层。业务层和数据层是 net core 标准库,但是当我想添加 entity framework 时,我必须修改数据层以使其看起来像 net core 应用程序,所以现在我有 Startup.cs 和那里的配置。这使我能够配置 entity framework 服务并在数据层中创建迁移。但是现在我有一个问题,因为我想添加 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();
}
但现在我需要从 class 中使用 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();
}
现在的问题是,如何在Data Register方法中获取UserManager?这是一个简单的 class,它不是从控制器继承的,依赖注入不适用于此处示例中的构造函数
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
那么,我如何将启动中配置的 UserManager 传递给中间件中某个随机 class 的某处?我已经看到了这个 question,但是仅将 null 值传递给 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 方法?
我无法实例化它,也无法在构造函数上使用依赖注入,因为它只是循环我的问题。 @Set 提到
or pass UserManager userManager as parameter to method
pass it from where?
我想我已经很接近了,但是我遗漏了一些东西。
我试过使用
IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
但是 IServiceProvider 服务是空的,我也不知道从哪里得到它。
ASP.NET 核心中的 DI 对于控制器和非控制器 class 使用 "constructor injection" 方法相同。
您遇到问题,因为 Register
方法是 static
,因此无法访问实例 variables/properties。你需要
- 使
Register
方法非静态
- 或将
UserManager<ApplicationUser> userManager
作为参数传递给方法
一般来说,您应该避免将静态 classes 用于业务逻辑,因为它们无助于正确测试您的代码和产生代码耦合。通过 internet/SO 搜索,你会发现很多关于为什么静态不好的话题。
使用 DI 在您的控制器中获取 Data.Commands.ApplicationUsers
class 的实例。如果您的应用程序只需要此 class 的一个实例 - 请为其使用单例生命周期。
更新。再次,使用构造函数注入:修改你的 "Data Layer" class 以便它可以获得 IIdentityTransaction
的实例作为构造函数参数:
public class YourDataLayerClass : IYourDataLayerClass
{
private IIdentityTransaction _identityTransaction;
public YourDataLayerClass(IIdentityTransaction identityTransaction)
{
_identityTransaction = identityTransaction;
}
public void MethodWhereYouNeedToCallAdd()
{
_identityTransaction.Add(...);
}
}
与 IYourDataLayerClass
实例的想法相同:注册依赖项
services.AddScoped<IYourDataLayerClass, YourDataLayerClass>();
然后依赖于它的 class(在你的情况下是中间件,如果我理解正确的话)应该通过构造函数接收该实例:
public class YourMiddleware
{
private IYourDataLayerClass _yourDataLayerClass;
public YourMiddleware(IYourDataLayerClass yourDataLayerClass)
{
_yourDataLayerClass = yourDataLayerClass;
}
...
}
是的,你们很亲近。
首先,要么从 IdentityTransaction 构造函数中删除上下文参数,因为在您的代码中删除它似乎没有用。或者如果你打算以后使用它,在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容器中声明class,在业务class构造函数中添加IIdentityTransaction作为依赖,并将控制器的依赖项从 IIdentityTransaction 更新为 IBusinessLayer。
更精确。
services.AddScoped<IIdentityTransaction, IdentityTransaction>();
这段代码不注入实例或依赖项。它在 DI 容器中声明了一个接口及其关联的实现,因此可以在需要时稍后注入。当实际创建需要它们的对象时,将注入实际实例。 IE。控制器在实例化时会注入其依赖项。
IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
您在此处尝试执行的操作称为依赖定位器模式,通常被视为反模式。你应该坚持通过构造函数进行依赖注入,它更干净。
关键是在启动时声明DI容器中的所有东西,甚至是你自定义的business/data层classes,永远不要再自己实例化它们,并在任何[=中将它们声明为必需的依赖项31=]es 的构造函数需要它们。
我有一个多层应用程序,我开始在 ASP.NET Core 1.1 中编写它,我一直在学习它。我像以前在 Web API 中完成的应用程序一样组织它,我有主机服务(网络核心应用程序)、业务层和数据库之上的数据层。业务层和数据层是 net core 标准库,但是当我想添加 entity framework 时,我必须修改数据层以使其看起来像 net core 应用程序,所以现在我有 Startup.cs 和那里的配置。这使我能够配置 entity framework 服务并在数据层中创建迁移。但是现在我有一个问题,因为我想添加 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();
}
但现在我需要从 class 中使用 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();
}
现在的问题是,如何在Data Register方法中获取UserManager?这是一个简单的 class,它不是从控制器继承的,依赖注入不适用于此处示例中的构造函数 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
那么,我如何将启动中配置的 UserManager 传递给中间件中某个随机 class 的某处?我已经看到了这个 question,但是仅将 null 值传递给 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 方法?
我无法实例化它,也无法在构造函数上使用依赖注入,因为它只是循环我的问题。 @Set 提到
or pass UserManager userManager as parameter to method pass it from where?
我想我已经很接近了,但是我遗漏了一些东西。 我试过使用
IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
但是 IServiceProvider 服务是空的,我也不知道从哪里得到它。
ASP.NET 核心中的 DI 对于控制器和非控制器 class 使用 "constructor injection" 方法相同。
您遇到问题,因为 Register
方法是 static
,因此无法访问实例 variables/properties。你需要
- 使
Register
方法非静态 - 或将
UserManager<ApplicationUser> userManager
作为参数传递给方法
一般来说,您应该避免将静态 classes 用于业务逻辑,因为它们无助于正确测试您的代码和产生代码耦合。通过 internet/SO 搜索,你会发现很多关于为什么静态不好的话题。
使用 DI 在您的控制器中获取 Data.Commands.ApplicationUsers
class 的实例。如果您的应用程序只需要此 class 的一个实例 - 请为其使用单例生命周期。
更新。再次,使用构造函数注入:修改你的 "Data Layer" class 以便它可以获得 IIdentityTransaction
的实例作为构造函数参数:
public class YourDataLayerClass : IYourDataLayerClass
{
private IIdentityTransaction _identityTransaction;
public YourDataLayerClass(IIdentityTransaction identityTransaction)
{
_identityTransaction = identityTransaction;
}
public void MethodWhereYouNeedToCallAdd()
{
_identityTransaction.Add(...);
}
}
与 IYourDataLayerClass
实例的想法相同:注册依赖项
services.AddScoped<IYourDataLayerClass, YourDataLayerClass>();
然后依赖于它的 class(在你的情况下是中间件,如果我理解正确的话)应该通过构造函数接收该实例:
public class YourMiddleware
{
private IYourDataLayerClass _yourDataLayerClass;
public YourMiddleware(IYourDataLayerClass yourDataLayerClass)
{
_yourDataLayerClass = yourDataLayerClass;
}
...
}
是的,你们很亲近。
首先,要么从 IdentityTransaction 构造函数中删除上下文参数,因为在您的代码中删除它似乎没有用。或者如果你打算以后使用它,在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容器中声明class,在业务class构造函数中添加IIdentityTransaction作为依赖,并将控制器的依赖项从 IIdentityTransaction 更新为 IBusinessLayer。
更精确。
services.AddScoped<IIdentityTransaction, IdentityTransaction>();
这段代码不注入实例或依赖项。它在 DI 容器中声明了一个接口及其关联的实现,因此可以在需要时稍后注入。当实际创建需要它们的对象时,将注入实际实例。 IE。控制器在实例化时会注入其依赖项。
IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
您在此处尝试执行的操作称为依赖定位器模式,通常被视为反模式。你应该坚持通过构造函数进行依赖注入,它更干净。
关键是在启动时声明DI容器中的所有东西,甚至是你自定义的business/data层classes,永远不要再自己实例化它们,并在任何[=中将它们声明为必需的依赖项31=]es 的构造函数需要它们。