How to use identity? - c#

Description
I'm learning ASP.NET Core with MVC pattern and I'm trying to create a custom Roles for my users.
Code
For doing this I setup inside the ConfigureServices method this Identity:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyAppContext>(options => options.UseSqlServer(#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=MyApp;Integrated Security=True;Connect Timeout=30;"));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MyAppContext>()
.AddDefaultTokenProviders();
}
then inside the Configure method I declare this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
new UserRoleSeed(app.ApplicationServices.GetService<RoleManager<IdentityRole>>()).SeedAsync();
}
Essentially I used the Dependency Injection to pass the RoleManager in the UserRoleSeed constructor, which is a configuration class for the role:
public class UserRoleSeed
{
private readonly RoleManager<IdentityRole> _roleManager;
public UserRoleSeed(RoleManager<IdentityRole> roleManager)
{
_roleManager = roleManager;
}
public async void SeedAsync()
{
if ((await _roleManager.FindByNameAsync("Admin")) == null)
{
await _roleManager.CreateAsync(new IdentityRole { Name = "Admin" });
}
}
}
when I start the application I get this error:
System.InvalidOperationException: 'Cannot resolve scoped service 'Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]' from root provider.'
What I did wrong?
NB: I added only the relevant code.

There are two issues in your code:
resolving RoleManager<IdentityRole> from root provider
SeedAsync will cause object dispose error.
Try steps below to resolve your issue:
Add IServiceProvider to Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
Modify SeedAsync from void to Task
public async Task SeedAsync()
Call SeedAsync from Configure
new UserRoleSeed(serviceProvider.GetService<RoleManager<IdentityRole>>()).SeedAsync().Wait();

Related

Singleton service and EF Core dbContext

The application uses ASP.NET Core 3. At the first call, a project class service is created.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
Project.cs
public class Project
{
private readonly DataBaseContext _dbContext;
public Project(DataBaseContext dbContext)
{
_dbContext = dbContext;
Init();
}
public async void Init()
{
await SomeMethod('text');
}
public async Task SomeMethod(string message)
{
_dbContext.Items.Add(message);
await _dbContext.SaveChangesAsync();
}
}
This is not entirely correct and I want to create a service when the application starts.
public void ConfigureServices(IServiceCollection services)
{
// AddDbContext
Project project = new Project(dbContext); // How to get dbcontext?
services.AddSingleton(typeof(Project), project);
}
How to pass dbcontext in this case?
UPDATE
Now in the Stratup class, I call the init () method of the project service.
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
Project project = serviceProvider.GetService<Project>();
project.Init();
// some code
}
Dunno why would you not use the automatic Dependecy Injection at your first code
Singleton are created upon app start. And as long as the init method is called inside the constructor it will run. So this code will work on your case already
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
But anyway if you insist on instantiating the Project class then you can use this. Get the DBContext using ServiceProvider.
public void ConfigureServices(IServiceCollection services)
{
// AddDbContext
var sp = services.BuildServiceProvider();
var dbContext = sp.GetRequiredService<DbContext>();
Project project = new Project(dbContext);
services.AddSingleton(typeof(Project), project);
}

How to fix the Dependency Injection error in Startup.cs class in .NET Core

I use ASP.NET Core 2.2
I am trying to call a basic service class from Startup. It is throwing this exception:
InvalidOperationException: Unable to resolve service for type
'TIR.NetCore.ICommonLogService' while attempting to activate
'AdminCentral.NetCore.Startup'.
This my code:
public class Startup
{
private readonly ICommonLogService _CommonLogService;
public Startup(IConfiguration configuration, ICommonLogService CommonLogService)
{
_CommonLogService = CommonLogService;
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public string connectionString;
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var container = new Container();
container.Configure(config =>
{
config.AddRegistry(new StructuremapRegistry());
config.Populate(services);
});
return container.GetInstance<IServiceProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
LogException(Exception )
}
private void LogException(Exception error, HttpContext context)
{
_CommonLogService.InsertLogDetail();
}
}
If you want to use ICommonLogService in the Startup.cs class, you need to get an instance from the container like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var container = new Container();
container.Configure(config =>
{
config.AddRegistry(new StructuremapRegistry());
config.Populate(services);
});
//Get an instance of ICommonLogService from container
ICommonLogService CommonLogService = container.GetInstance<ICommonLogService>();
//Use CommonLogService here
return container.GetInstance<IServiceProvider>();
}

Access HttpContextAccessor from startup.cs in .net Core WebApi

I'm logging exceptions to database in asp.net core. MyDbContext take HttpContextAccessor parameter.So, I'm sending HttpContextAccessor to MyDbContext.cs for access my JWT. But, I can't access my HttpContextAccessor from Startup.cs. How can I achieve this?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddMvc();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDbContext<MyDbContext>();
services.AddTransient<IUnitOfWork, UnitOfWork>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandler(builder => builder.Run(async context =>
{
var error = context.Features.Get<IExceptionHandlerFeature>();
context.Response.AddApplicationError(error,???????);//I want access HttpContextAccessor
await context.Response.WriteAsync(error.Error.Message);
}));
app.UseHttpsRedirection();
app.UseMvc();
}
ExceptionHelper.cs
public static class ExceptionHelper
{
public static async Task AddApplicationError(this HttpResponse response, IExceptionHandlerFeature error, IHttpContextAccessor httpContextAccessor)
{
Log log = new Log();
log.Message = error.Error.Message;
MyDbContext context = new MyDbContext(null, httpContextAccessor);
UnitOfWork uow = new UnitOfWork(context);
uow.LogRepo.AddOrUpdate(log);
await uow.CompleteAsync(false);
}
}
MyDbContext
public class MyDbContext : DbContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyDbContext(DbContextOptions<MyDbContext> options, IHttpContextAccessor httpContextAccessor)
: base(GetOptions())
{
_httpContextAccessor = httpContextAccessor;
}
private static DbContextOptions GetOptions()
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), "server=asd; database=; user id=asd; password=1234").Options;
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var token = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
var audits = AuditHelper.AddAuditLog(base.ChangeTracker, token);
return (await base.SaveChangesAsync(true, cancellationToken));
}
}
You can inject whatever you need into the Configure method. You have already added it to the service collection with this line:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
So all you need to do is add it to the list of arguments on the method like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor accessor)
{
// make use of it here
}
As an aside: I would also point out that it's a bit of a code smell that you are manually creating an instance of your DbContext inside your static helper class when you are using dependency injection.
Update in response to comment
In order to tidy things up a bit I would start by changing your startup to configure you DbContext something like this:
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// register other things here...
services.AddDbContext<DataContext>(o => o.UseSqlServer(
config.GetConnectionString("MyConnectionString") // from appsettings.json
));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// set up app here...
}
}
You can then remove the .GetOptions() method from MyDbContext, and change the constructor to:
public MyDbContext(DbContextOptions<MyDbContext> options, IHttpContextAccessor httpContextAccessor)
: base(options)
{
_httpContextAccessor = httpContextAccessor;
}
Then you inject an instance of MyDbContext into whatever class needs access to it. The problem is that (to my knowledge) DI does not work well with static classes/methods, and you are using an extension method on the HttpResponse to log your error.
In my opinion it would be better to create a class that is responsible for logging the error with a dependency on your MyDbContext and have that injected into the Configure method:
public class ErrorLogger
{
private MyDataContext db;
public ErrorLogger(MyDataContext db) => this.db = db;
public void LogError(IExceptionHandlerFeature error)
{
Log log = new Log();
log.Message = error.Error.Message;
UnitOfWork uow = new UnitOfWork(this.db);
uow.LogRepo.AddOrUpdate(log);
await uow.CompleteAsync(false);
}
}
Register it with the DI container as you have with other things, then inject it into Configure instead of the HTTP accessor:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ErrorLogger logger)
{
app.UseExceptionHandler(builder => builder.Run(async context =>
{
var error = context.Features.Get<IExceptionHandlerFeature>();
logger.LogError(error);
await context.Response.WriteAsync(error.Error.Message);
}));
}
I have not tested this, and I am not familiar with .UseExceptionHandler(...) as I use application insights to log exceptions etc (take a look at it if you've not seen it). One thing to be aware of is the scope of your dependencies; your DbContext will be Scoped by default (and I think you should leave it that way), which means you cannot inject it into Singleton objects.

Is the constructor injection of HttpContext a good idea? [duplicate]

I need to access current HttpContext in a static method or a utility service.
With classic ASP.NET MVC and System.Web, I would just use HttpContext.Current to access the context statically. But how do I do this in ASP.NET Core?
HttpContext.Current doesn't exist anymore in ASP.NET Core but there's a new IHttpContextAccessor that you can inject in your dependencies and use to retrieve the current HttpContext:
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
return _contextAccessor.HttpContext.Session.GetString(*KEY*);
}
}
Necromancing.
YES YOU CAN
Secret tip for those migrating large junks chunks (sigh, Freudian slip) of code.
The following method is an evil carbuncle of a hack which is actively engaged in carrying out the express work of satan (in the eyes of .NET Core framework developers), but it works:
In public class Startup
add a property
public IConfigurationRoot Configuration { get; }
And then add a singleton IHttpContextAccessor to DI in ConfigureServices.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
Then in Configure
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
add the DI Parameter IServiceProvider svp, so the method looks like:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Next, create a replacement class for System.Web:
namespace System.Web
{
namespace Hosting
{
public static class HostingEnvironment
{
public static bool m_IsHosted;
static HostingEnvironment()
{
m_IsHosted = false;
}
public static bool IsHosted
{
get
{
return m_IsHosted;
}
}
}
}
public static class HttpContext
{
public static IServiceProvider ServiceProvider;
static HttpContext()
{ }
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
// var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));
// Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
// context.Response.WriteAsync("Test");
return context;
}
}
} // End Class HttpContext
}
Now in Configure, where you added the IServiceProvider svp, save this service provider into the static variable "ServiceProvider" in the just created dummy class System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)
and set HostingEnvironment.IsHosted to true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
this is essentially what System.Web did, just that you never saw it (I guess the variable was declared as internal instead of public).
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
ServiceProvider = svp;
System.Web.HttpContext.ServiceProvider = svp;
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest
, CookieHttpOnly=false
});
Like in ASP.NET Web-Forms, you'll get a NullReference when you're trying to access a HttpContext when there is none, such as it used to be in Application_Start in global.asax.
I stress again, this only works if you actually added
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
like I wrote you should.
Welcome to the ServiceLocator pattern within the DI pattern ;)
For risks and side effects, ask your resident doctor or pharmacist - or study the sources of .NET Core at github.com/aspnet, and do some testing.
Perhaps a more maintainable method would be adding this helper class
namespace System.Web
{
public static class HttpContext
{
private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;
public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
{
m_httpContextAccessor = httpContextAccessor;
}
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
return m_httpContextAccessor.HttpContext;
}
}
}
}
And then calling HttpContext.Configure in Startup->Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
System.Web.HttpContext.Configure(app.ApplicationServices.
GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
);
The most legit way I came up with was by injecting IHttpContextAccessor in your static implementation as follow:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Then assigning the IHttpContextAccessor in the Startup Configure should do the job.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
I guess you should also need to register the service singleton:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Just to add to the other answers...
In ASP.NET Core 2.1, there's the AddHttpContextAccessor extension method, that will register the IHttpContextAccessor with the correct lifetime:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
}
According to this article: Accessing HttpContext outside of framework components in ASP.NET Core
namespace System.Web
{
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
}
Then:
public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
System.Web.HttpContext.Configure(httpContextAccessor);
return app;
}
}
Then:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
You can use it like this:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}
In Startup
services.AddHttpContextAccessor();
In Controller
public class HomeController : Controller
{
private readonly IHttpContextAccessor _context;
public HomeController(IHttpContextAccessor context)
{
_context = context;
}
public IActionResult Index()
{
var context = _context.HttpContext.Request.Headers.ToList();
return View();
}
}
To access to the session object from a class without explicitly use dependency injection in class constructor follow the next steps:
Add a Singleton instance on Startup.cs (ConfigureServices):
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
In your target class declare an instance of HttpContextAccessor:
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
Access to the session object :
string mySessionVar = _httpContextAccessor.HttpContext.Session.GetString("_MySessionVar");
EXAMPLE
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
YourClass.cs
public class YourClass {
public string yourProperty {
get{
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
return _httpContextAccessor.HttpContext.Session.GetString("_YourSessionVar");
}
}
}
Enjoy :)

Accessing DbContext from custom Class .Net Core

I need to access my DbContext from one handler class which is instantiated in the configure method of Startup.cs class. How Can Instantiate my handler class in order to use the db context registered with the dependency injection container in Startup.ConfigureServices method.
This is my code:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=MyDb;Initial Catalog=MYDB;Persist Security Info=True; Integrated Security=SSPI;";
services.AddDbContext<iProfiler_ControlsContext>(options => options.UseSqlServer(connection));
//.........
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//.............
options.SecurityTokenValidators.Add(new MyTokenHandler(MY INSTANCE OF DBCONTEXT HERE));
app.UseJwtBearerAuthentication(options);
//..............
}
Handler Class:
internal class MyTokenHandler : ISecurityTokenValidator
{
private JwtSecurityTokenHandler _tokenHandler;
private iProfiler_ControlsContext _context;
public MyTokenHandler(iProfiler_ControlsContext context)
{
_tokenHandler = new JwtSecurityTokenHandler();
_context = context;
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
var principal = _tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken);
var tblVerificationPortalTimeStamps = _context.TblVerificationPortalTimeStamps.ToList();
//......
}
}
First update ConfigureServices to return a service provider from the service collection.
public IServiceProvider ConfigureServices(IServiceCollection services) {
var connection = #"Server=MyDb;Initial Catalog=MYDB;Persist Security Info=True; Integrated Security=SSPI;";
services.AddDbContext<iProfiler_ControlsContext>(options => options.UseSqlServer(connection));
//.........
var provider = services.BuildServiceProvider();
return provider;
}
Next update Configure method to inject IServiceProvider
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory, IServiceProvider provider) {
//.............
var dbContext = provider.GetService<iProfiler_ControlsContext>();
options.SecurityTokenValidators.Add(new MyTokenHandler(dbContext));
app.UseJwtBearerAuthentication(options);
//..............
}

Categories

Resources