我有一个多层应用程序,我开始在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方法?
我无法实例化它,也不能在构造函数上使用依赖注入,因为它只是循环我的问题。 @Set提到
或将UserManager userManager作为参数传递给方法 从哪里传递?
我觉得我非常接近,但我错过了一些东西。 我尝试过使用
IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
但是IServiceProvider的服务是null,我不知道从哪里得到它。
答案 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;需要它们的构造函数。