Cannot resolve scoped service dbcontext from root provider [duplicate] - asp.net-core

This question already has answers here:
How to do DI in asp.net core middleware?
(2 answers)
Closed 2 years ago.
I have created an API Logging middleware to log request to DB and to Azure Microsoft insight however for some reason the Dependency Injection for the Database context is not found (gives cannot resolve scoped service from root provider).
API Logging MiddleWare
public class APILoggingMiddleware
{
private readonly TelemetryClient _telemetry;
private readonly ILogger _logger;
private readonly RequestDelegate _next;
private APIsAuditContext db;
public static IConfiguration _configuration;
public APILoggingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, TelemetryClient telemetry, IConfiguration configuration, APIsAuditContext dbContext)
{
_configuration = configuration;
_telemetry = telemetry;
_next = next;
_logger = loggerFactory.CreateLogger<APILoggingMiddleware>();
db = dbContext;
}
//static class to simplify adding the middleware to the application’s pipeline
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
finally
{
Task logReq = logRequest(context);
Task lodRes = logResponse(context);
}
}
}
DB Context
public class APIsAuditContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
public APIsAuditContext(DbContextOptions<APIsAuditContext> options) : base(options) { }
public DbSet<APIsRequestsAudit> APIsRequestsAudits { get; set; }
}
Startup
public class Startup
{
public IConfiguration _configuration { get; }
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<APIsAuditContext>(options => options.UseSqlServer(_configuration["ConnectionStrings:APIAuditConnection"]));
services.AddApplicationInsightsTelemetry();
services.AddControllers();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//Pipeline to API Logging
app.UseRequestResponseLogging();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Can someone help please?

As per https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1#per-request-middleware-dependencies, you must inject scoped service in the Invoke method:
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
// IMyScopedService is injected into Invoke
public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
}

Related

how to set the output type List<string> in a middleware in .NET Core 2.1?

I have this middleware class when I want to show a List<string> in the output:
namespace WebAspNetCore2_1
{
public class LearningMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LearningMiddleware> _logger_log;
private readonly List<string> logger;
public LearningMiddleware(RequestDelegate next,ILogger<LearningMiddleware> logger_log)
{
_next = next;
_logger_log = logger_log;
List<string> _logger = new List<string>
{
("EUR/USD"),
("1.0500")
};
logger = _logger;
}
public Task Invoke(HttpContext httpContext)
{
_logger_log.Log(Microsoft.Extensions .Logging.LogLevel.Information,"information of logger",logger[0]);
return _next(httpContext);
}
}
}
I have debugged my code but seen to be correct, my List<> is filled, I don't know why the compiler is throwing this exception:
InvalidOperationException: Could not create an instance of type Microsoft.Extensions.Logging.ILogger`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the 'logger' parameter a non-null default value.
i thought was the order declaration in StartUp, but not
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// app.UseLearningMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMiddleware<LearningMiddleware>();
app.UseMvc();
}
link in video for detail evidence: https://youtu.be/2FoLvhLweYo
I tested your code in my side but it worked well... I created a new asp.net core 2.1 MVC project and create a middleware. In StartUp.cs, I put app.UseMiddleware<MyMiddleware>(); just before app.UseMvc(routes =>
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApplication2
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<MyMiddleware> _logger_log;
private readonly List<string> logger;
public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger_log)
{
_next = next;
_logger_log = logger_log;
List<string> _logger = new List<string>
{
("EUR/USD"),
("1.0500")
};
logger = _logger;
}
public Task Invoke(HttpContext httpContext)
{
_logger_log.Log(Microsoft.Extensions.Logging.LogLevel.Information, "information of logger", logger[0]);
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
}
}

LinkGenerator.GetUriByAction not following the custom route

That's the controller:
[ApiController]
[Route("api/[controller]")
public class MarketController : ControllerBase
{
[HttpGet("{id}/picture")
public async Task<IActionResult> GetPictureAsync(Guid id)
{
...
}
}
I'm using LinkGenerator to create a Absolute URI from GetPictureAsync. And set the Startup class to start HttpContextAccessor as DI.
// Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapDefaultControllerRoute();
});
}
And in my custom class I use that way:
public class CustomClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly LinkGenerator _linkGenerator;
public CustomClass(IHttpContextAccessor httpContextAccessor,
LinkGenerator linkGenerator)
{
_httpContextAccessor = httpContextAccessor;
_linkGenerator = linkGenerator;
}
public void SomeMethod()
{
var uri = _linkGenerator.GetUriByAction(_httpContextAccessor.HttpContext, "GetPicture", "Markets", new { id = id });
}
}
The problem is because LinkGenerator is not following the custom route sample that I set in GetPicture method.
The LinkGenerator generates the following value:
https://localhost:5051/Markets/GetPicture/00748d23-afa7-4efb-b67b-77f68fdc44d5
But it should generate:
https://localhost:5051/api/Markets/00748d23-afa7-4efb-b67b-77f68fdc44d5/picture
The reason is you use wrong controller name in SomeMethod. Follow the steps you provided, I reproduced your issue.
You should use Market, not Markets.
Because your controller name is MarketController.
After test it,it works for me.

How to access session in class library .Net Core 5

is there a way to access HttpContext.session in a class library? (im using .Net Core 5)
I have the configuration all set up and also using microsoft.AspNetCore.Http but im still unable to access the session variables.
If there is no way to do that whats the best way to perform actions that require the current User's ID/Identification?
As #King King answered, you could inject the IHttpContextAccessor into the class.
Step 1 Add Session service
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSession();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
...
}
Step 2 Access Session in custom class
public class SessionTest
{
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public SessionTest(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void setSession()
{
_session.SetString("Test", "Hello World!");
}
public void getSession()
{
var message = _session.GetString("Test");
...
}
}
Step 3 access session via custom class
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ISession _session;
public HomeController(IHttpContextAccessor httpContextAccessor,ILogger<HomeController> logger)
{
_logger = logger;
_httpContextAccessor = httpContextAccessor;
_session = _httpContextAccessor.HttpContext.Session;
}
public IActionResult Index()
{
SessionTest session = new SessionTest(_httpContextAccessor);
session.setSession();
session.getSession();
return View();
}
}

Scoped service: Access HTTP context without IHttpContextAccessor

In ASP.NET Core I have a service that is registered in DI as scoped. Can I access the HTTP context in that service without using IHttpContextAccessor and the overhead it has?
You'd have to add a scoped service to the container, then you'd have to add a middleware that resolved that service and set the current http context on it.
public class ScopedHttpContext
{
public HttpContext HttpContext { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ScopedHttpContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<ScopedHttpContextMiddleware>();
}
public class ScopedHttpContextMiddleware
{
private readonly RequestDelegate _next;
public ScopedHttpContextMiddleware(RequestDelegate next)
{
_next = next;
}
public Task InvokeAsync(HttpContext context, ScopedHttpContext scopedContext)
{
scopedContext.HttpContext = context;
return _next(context);
}
}

Validating AntiForgery token globally in ASP.NET Core

I want to validate AntiForgery token in ASP.NET Core application. I know i can individually do that by adding [AutoValidateAntiforgeryToken] or [ValidateAntiforgeryToken] attributes on Action methods as suggested in SO post here
I'm looking for global approach to validate token for all POST methods. So i created a middleware to do so. However i could not find suitable method to validate the token. Like in classic asp.net there is AntiForgery.Validate().
What's the equivalent method in ASP.NET Core
public class ValidateAntiForgeryTokenMiddleware
{
private readonly RequestDelegate _next;
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Method.ToUpper() == "POST")
{
// where does this mehod exists?
// i could not find it in Microsoft.AspNetCore.Antiforgery namespace
AntiForgery.Validate();
}
await _next(httpContext);
}
}
public static class ValidateAntiForgeryTokenMiddlewareExtensions
{
public static IApplicationBuilder UseValidateAntiForgeryToken(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ValidateAntiForgeryTokenMiddleware>();
}
}
I have to Inject Antiforgery as service
public class ValidateAntiForgeryTokenMiddleware
{
private readonly RequestDelegate _next;
private readonly IAntiforgery _antiforgery;
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
{
_next = next;
_antiforgery = antiforgery;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Method.ToUpper() == "POST")
{
await _antiforgery.ValidateRequestAsync(httpContext);
}
await _next(httpContext);
}
}
add Antiforgery as service in startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery();
}
Use my middlware
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
app.UseValidateAntiForgeryToken();
}