I am building ASP.Net Core App depending on this clean architecture example which is using MediatR to execute commands.
And i want to use ASP.Net Core Identity in my app, so in my CreateUserCommandHandler i want to use UserManager to add new user, but when i add UserManager to Command contractor MediatR unable to create the handler and fail with this exception:
System.InvalidOperationException: Error constructing handler for request of type MediatR.IRequestHandler`2[GoGYM.Application.Identity.Commands.CreateUser.CreateUserCommand,MediatR.Unit]. Register your handlers with the container. See the samples in GitHub for examples. ---> System.InvalidOperationException: Unable to resolve service for type 'GoGYM.Persistence.GoGYMDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[GoGYM.Domain.Entities.ApplicationUser,GoGYM.Domain.Entities.ApplicationRole,GoGYM.Persistence.GoGYMDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim`1[System.String],Microsoft.AspNetCore.Identity.IdentityUserRole`1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin`1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken`1[System.String],Microsoft.AspNetCore.Identity.IdentityRoleClaim`1[System.String]]'.
In Configure services i register my DBContext and MediatR like this:
// Add AutoMapper
services.AddAutoMapper(new Assembly[] { typeof(AutoMapperProfile).GetTypeInfo().Assembly });
// Add MediatR
services.AddMediatR(typeof(GetUsersListQueryHandler).GetTypeInfo().Assembly);
// Add DbContext using SQL Server Provider
services.AddDbContext<IGoGYMDbContext, GoGYMDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("NorthwindDatabase")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<GoGYMDbContext>();
services.AddMvc();
....
And this my command handler code:
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, Unit>
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly IGoGYMDbContext _context;
public CreateUserCommandHandler(IGoGYMDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public Task<Unit> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
And my controller
[HttpPost]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesDefaultResponseType]
public async Task<IActionResult> Create(string values)
{
await Mediator.Send(new CreateUserCommand(values));
return NoContent();
}
I have tried a lot of things and nothing work, only if i remove UserManager from command handler it gets executed then.
You register IGoGYMDbContext with DI but pass in GoGYMDbContext to AddEntityFrameworkStores. GoGYMDbContext isn't registered with DI, so it can't be resolved when requested by the ASP.NET Core Identity framework.
The following changes allow you to register both the interface and the implementation, but using the same implementation instance whether requested via the interface or the implementation:
Remove the interface from the call to AddDbContext:
services.AddDbContext<GoGYMDbContext>(...);
Add a passthrough from the interface to the GoGYMDbContext implementation:
services.AddScoped<IGoGYMDbContext>(sp => sp.GetRequiredService<GoGYMDbContext>());
Related
I have made an ISignUp interface which is implemented by the SignUp class to create a new user. I am using Dependancy Injection (DI). The issue is that I cannot register this service. Instead of creating new users in the SignUpController directly, I rather prefer calling a helper class via the Interface it implements.
I am sure I am missing something here. Let me post the code and the exception so that someone may point out what I am missing.
Project Details:
ASP.NET CORE 3.1
I will appreciate help in pointing out the issue here.
Below is the ISignUp interface:
public interface ISignUp
{
public Task<bool> IsUserCreatedAsync(ApplicationUser user, string password);
}
Below is the SignUp class implementing the ISignUp Interface:
public class SignUp : ISignUp
{
private readonly UserManager<ApplicationUser> userManager;
public SignUp(UserManager<ApplicationUser> _userManager)
{
userManager = _userManager;
}
public async Task<bool> IsUserCreatedAsync(ApplicationUser user, string password)
{
// create a new user account
var result = await userManager.CreateAsync(user, password);
// check result
if (result.Succeeded)
{
return true;
}
return false;
}
}
Below is the SignUpController:
public class SignUpController : Controller
{
private readonly ISignUp signUp;
public SignUpController(ISignUp _signUp)
{
signUp = _signUp;
}
}
Below checking if the user is created:
// create new user
var userCreated = await signUp.IsUserCreatedAsync(user, model.Password);
// check result
if (userCreated)
{
// user created, log them in
}
Registering the service in the StartUp.cs Class:
// registering an ISignUp service and its implementation
services.AddScoped<ISignUp, SignUp>();
When I build and run, I get this exception:
System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: School_ERP_Software.Helpers.Interfaces.ISignUp Lifetime: Scoped ImplementationType: School_ERP_Software.Helpers.Implementation.SignUp': Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[School_ERP_Software.Database.Model.ApplicationUser]' while attempting to activate 'School_ERP_Software.Helpers.Implementation.SignUp'.)'
What am I missing here?
You only have on constructor here:
public class SignUp : ISignUp
{
private readonly UserManager<ApplicationUser> userManager;
public SignUp(UserManager<ApplicationUser> _userManager)
{
userManager = _userManager;
}
The framework, tries to initialize the SignUp object with the only constructor it can find, which is the one above.
It does not know what value to provide, as you have not resolved this dependency for the DI container in your configuration, so you get the error above, hence the error
Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager1
You need to register the UserManager service to get rid of the exception.
Check ASP.NET Core Identity does not inject UserManager<ApplicationUser>
I don't fully understand how to refactor my app to make it more testable.
I am using ASP.NET Core 3.0 (Razor Pages, if that matters).
Current State:
I have razor pages models that inject in my context (inherited from IdentityDbContext) and userManager:
public class DashboardModel : PageModel
{
private readonly ApplicationDbContext _context;
private readonly UserManager<AVUser> _userManager;
public DashboardModel(ApplicationDbContext context, UserManager<AVUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task<IActionResult> OnGetAsync()
{
AVUser = await _userManager.GetUserAsync(User);
MyRepository myRepository = new MyRepository(_context, AVUser);
// omitted - just getting data from the repository for use on the page
return Page();
}
I have repositories created who have constructors as the following:
public MyRepository(ApplicationDbContext context, AVUser user)
I never use the context directly in my Model classes and instead create repositories in the Model classes using the context passed in. In this case ApplicationDbContext is my EF Core context, and AVUser is my type of Identity User, defined as:
public class AVUser : IdentityUser
My DI is set up as:
// DB Setup
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
// Identity Setup
services.AddDefaultIdentity<AVUser>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
What I don't understand
I want to make my razor pages model classes unit testable without relying on the database or repository classes, mocking up the repository class, so I expect I have to remove the context from the constructor and instead created interfaces for my repositories and DI them.
However, I don't know how to DI in the user parameter for the Repository - how do I set up my service in Startup.cs so this can get passed in? Today to get the value for this, I run:
AVUser = await _userManager.GetUserAsync(User);
If you inject your repositories in the DI container, IoC will automatically inject their dependencies like controllers and pages. so you just need to have a constructor like this in your repository class:
private readonly ApplicationDbContext _context;
private readonly UserManager<AVUser> _userManager;
public MyRepository(ApplicationDbContext context, UserManager<AVUser> userManager)
{
_context = context;
_userManager = userManager;
}
and then inject it to the container:
services.AddTransient<IMyRepository,MyRepository>();
and that's it.
remember to create an interface for your repository so you can inject different instances for either production or testing. Nonetheless, You shouldn't rely on DI for testing. If you are writing unit tests then create a test implementation for your MyRepository and simply instantiate it. If you are writing integration tests then you have to use a different sturtup class.
I have web api application .net framework 4.6.2 and we store user credentials in the token. I override dbcontext.savechanges to track database changes. I want to access userId which is stored in token and in api I can access that by httpContext. But the HTTP context is not available in dbcontext.
Does System.Web.HttpContext.Current get the current Request Context?
I should point out that adding stuff that references this directly into a DbContext is poor design though... You should probably use Dependency Injection and manage this via some service with a lifetime scoped to the request - or at least that's how I'd do it...
As #GPW said that's a poor design decision.
To solve that situation you need to use IoC to do registrations such as:
// Autofac
builder.Register(c => new HttpContextWrapper(HttpContext.Current))
.As<HttpContextBase>()
.InstancePerRequest();
// Again Autofac
builder.RegisterModule(new AutofacWebTypesModule());
// Castle Windsor
container.Register(Component.For<HttpContextBase()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => new HttpContextWrapper(HttpContext.Current)));
With controllers using contructor injection:
public class HomeController : Controller
{
private readonly HttpContextBase _httpContext;
public HomeController(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
}
So you inject HttpContextBase in order to access the context
public class EntitiesContext : DbContext
{
private readonly HttpContextBase _httpContext;
public EntitiesContext(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
}
I am trying to inject dependency into my middleware constructor as follows
public class CreateCompanyMiddleware
{
private readonly RequestDelegate _next;
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddleware(RequestDelegate next
, UserManager<ApplicationUser> userManager
)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next.Invoke(context);
}
}
My Startup.cs file looks like
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("IdentityConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
app.UseMiddleware<CreateCompanyMiddleware>();
...
But I am getting this error
An error occurred while starting the application.
InvalidOperationException: Cannot resolve scoped service 'Microsoft.AspNetCore.Identity.UserManager`1[Common.Models.ApplicationUser]' from root provider.
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
UserManager<ApplicationUser> is (by default) registered as a scoped dependency, whereas your CreateCompanyMiddleware middleware is constructed at app startup (effectively making it a singleton). This is a fairly standard error saying that you can't take a scoped dependency into a singleton class.
The fix is simple in this case - you can inject the UserManager<ApplicationUser> into your Invoke method:
public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
{
await _next.Invoke(context);
}
This is documented in ASP.NET Core Middleware: Per-request middleware dependencies:
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by DI:
Another way to do that is to create a middleware by IMiddleware interface and register it as a service
For example , the middleware
public class CreateCompanyMiddlewareByInterface : IMiddleware
{
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddlewareByInterface(UserManager<ApplicationUser> userManager )
{
this._userManager = userManager;
}
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
return next(context);
}
}
and service registeration :
services.AddScoped<CreateCompanyMiddlewareByInterface>();
So why it happens ?
The middlewares using IMiddleware are built by UseMiddlewareInterface(appBuilder, middlewareType type) :
private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
{
return app.Use(next =>
{
return async context =>
{
var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory));
if (middlewareFactory == null) { /* throw ... */ }
var middleware = middlewareFactory.Create(middlewareType);
if (middleware == null) { /* throw ... */ }
try{
await middleware.InvokeAsync(context, next);
}
finally{
middlewareFactory.Release(middleware);
}
};
});
}
here the codes inside the context=>{} are executed per-request . So every time there's an incoming request , the var middleware = middlewareFactory.Create(middlewareType); will be executed and then ask for a middleware of middlewareType ( which is already registered as a service ) from the ServiceProvider .
As for by-convention middlewares , there's no factory creating them .
Those instances are all created by ActivatorUtilities.CreateInstance() at startup time . And any Invoke method of by-convention middlewares , such as
Task Invoke(HttpContext context,UserManager<ApplicationUser> userManage, ILoggerFactory loggeryFactory , ... )
will be compiled into a function like below :
Task Invoke(Middleware instance, HttpContext httpContext, IServiceprovider provider)
{
var useManager /* = get service from service provider */ ;
var log = /* = get service from service provider */ ;
// ...
return instance.Invoke(httpContext,userManager,log, ...);
}
As you see , here the instance is created at startup time , and those services of Invoke method are requested per request .
I have a custom Authentication Middelware which uses my custom service injected in the constructor.
In MyAuthenticationHandler I am calling a method of MyService which sets a property value.
_myService.SetCompany(company);
company is loaded in the authentication handler and is not null. However when I try to access the value from the controller I find that MyService has been reinitialized.
This is how it's set in Startup.cs
services.AddScoped<IMyService, MyFactory>();
Middleware is only initialized once, when you register. You need to resolve your dependency in the Invoke method.
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var service = context.RequestServices.GetService<IMyService>();
service.SetCompany("My Company");
await _next.Invoke(context);
}
}
Now the service is properly resolved per request, rather than per application life time.
Edit:
i.e. in order to have your middleware be called after the authorization middelware is called you'd do something like this in your Configure(IAppBuilder app) method:
app.UseCookieAuthentication(options => { ... });
app.UseJwtBearerAuthentication(options => { ... });
app.UseMiddleware<MyMiddleware>(options => { ... });
Then on a request, first the cookie middleware will be called. If it can handle the scheme and it fails, then following middlewares won't be executed. If it can't handle the scheme, next one will be called (jwt bearer). If that passes, the next middleware (MyMiddleware) will be called.
In other words, when your MyMiddleware.Invoke() method is being called, the user has been authenticated.
You can inject the service directly in the Invoke signature.
From the official doc here:
If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by dependency injection.
So in your case:
public async Task Invoke(HttpContext context, IMyService service)
{
service.SetCompany("My Company");
await _next.Invoke(context);
}
will work.