I have a multi-layer application that I started writing in ASP.NET Core 1.1 which I'm still learning along the way. I have organized it like previous apps I've done in the Web API, I have host service (net core app), business layer and data layer that is above database. Business and data layers were net core standard libraries, but when I wanted to add entity framework I had to modify data layer to look like net core app, so now I have Startup.cs with configurations there. That allowed me to configure entity framework service and to create migrations in the data layer. But now I have a problem as I wanted to add asp.net identity. Every tutorial on the net is about SPAs that have everything in one project. I have added identity to Startup.cs and database is generated well
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();
}
but now I need to use UserManager from a class that is not a Controller and I don't know how to deal with dependency injection. To explain better, I have an Account controller in my Host Service
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody]RegisterUserDto dto)
{
var result = await Business.Commands.Accounts.Register(dto);
return Ok(result);
}
Business layer just calls the Data layer
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();
}
Now the question is, how do I get UserManager in the Data Register method? It's a simple class, it doesn't inherit from a controller, dependency injection is not working for constructors like in the examples found here 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
So, how do I pass UserManager that is configured in Startup to some random class somewhere in the middleware? I have seen this question, but the answer to just pass null values to UseManager constructor is not working nor I think it's good.
//EDIT as per Set's answer
I have removed all static references, but I'm still not quite there. I have followed this dependency injection instructions, but I'm not sure how to instantiate and call Add method.
I have created an interface
public interface IIdentityTransaction
{
Task<IdentityResult> Add(ApplicationUser appUser, string password);
}
and implemened it
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);
}
}
then I injected it to a service collection in Startup.cs
services.AddScoped<IIdentityTransaction, IdentityTransaction>();
but how to call Add method from IdentityTransaction service?
I cannot instantiate it nor use dependency injection on constructor as it just loops my problem. @Set mentioned
or pass UserManager userManager as parameter to method pass it from where?
I think I'm very close, but I'm missing something. I have tried using
IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
but services which is IServiceProvider is null, I don't know where to get it from either.