Custom NLog LayoutRenderer with constructor using Dependency Injection - c#

I am trying to write a custom LayoutRenderer that logs data read from an object, but it seems that NLog is not working properly with Dependency Injection.
Here is my CustomLayoutRenderer:
[LayoutRenderer("custom-value")]
public class CustomLayoutRenderer : LayoutRenderer
{
private readonly RequestContext _context;
public CustomLayoutRenderer(RequestContext context)
{
_context = context;
}
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(_context.CustomValue);
}
}
It is using this RequestContext object:
public class RequestContext
{
public string CustomValue { get; set; } = "Valid custom value";
}
I am also wiring up DI, configuring NLog and registering my LayoutRenderer in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddScoped<RequestContext>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
LayoutRenderer.Register<CustomLayoutRenderer>("custom-value");
loggerFactory.AddNLog();
app.AddNLogWeb();
env.ConfigureNLog("nlog.config");
// ...
}
I am then trying to use my ${custom-value} in nlog.config, but I am getting an error on the AddNLog() call:
2017-02-03 13:08:08.0284 Error Parsing configuration from [project-folder]\bin\Debug\net452\win7-x64\NLog.config failed.
Exception: NLog.NLogConfigurationException: Exception when parsing [project-folder]\bin\Debug\net452\win7-x64\NLog.config.
NLog.NLogConfigurationException: Cannot access the constructor of type: ATest.CustomLayoutRenderer. Is the required permission granted?
at NLog.Internal.FactoryHelper.CreateInstance(Type t)
...
Notes
The reason why I am trying this is that I would like to log some information accessible only from the controller (like the TraceIdentifier, parts of the URL, and some request-specific custom stuff). The values in RequestContext would be set by the controller when it gets a request.
The following Renderer works as expected, which makes me think this is a dependency injection problem:
[LayoutRenderer("custom-value")]
public class CustomLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append("Hello, World!");
}
}
I did see this NLog bug but it's marked as fixed now, which is why I am posting here rather than there.
And for the sake of completeness, here is what I have added to my project.json:
"dependencies": {
...
"NLog.Extensions.Logging": "1.0.0-*",
"NLog.Web.AspNetCore": "4.3.0"
},

Two methodes:
1) DI aware
You could make NLog DI aware. Add to your startup.cs:
ConfigurationItemFactory.Default.CreateInstance = (Type type) =>
{
// your custom target. Could be a better check ;)
if(type == typeof(CustomLayoutRenderer))
return new CustomLayoutRenderer(...); // TODO get RequestContext
else
return Activator.CreateInstance(type); //default
};
This is a more generic approach.
2) AspNetMvcLayoutRendererBase
Or, override from AspNetMvcLayoutRendererBase (NLog.Web.AspNetCore) and use HttpContextAccessor?.HttpContext?.TryGetRequest() and don't add the constructor.
This only works when needing HttpContext.
[LayoutRenderer("custom-value")]
public class MyCustomRenderer : AspNetLayoutRendererBase
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
var httpRequest = HttpContextAccessor?.HttpContext?.TryGetRequest();
if (httpRequest == null)
return;
builder.Append(httpRequest.Something); //TODO
}
}

Looking at the NLog source code for FactoryHelper. It looks like you have to provide a default constructor.
You may want to also consider using the layout renders from NLog.Web package. There are several that can get request-specific information. There even an option to log stuff from HttpContext with ${aspnet-Item} layout
If that doesn't suit you perhaps a "stateful" logger will work like the one in the answer I gave here: https://stackoverflow.com/a/32857908/1289052

Related

Mapping dynamic odata routes with ASP.NET Core OData 8.0

I've got an application where the EDM datatypes are generated during the runtime of the application (they can even change during runtime). Based loosely on OData DynamicEDMModelCreation Sample - refactored to use the new endpoint routing. There the EDM model is dynamically generated at runtime and all requests are forwarded to the same controller.
Now I wanted to update to the newest ASP.NET Core OData 8.0 and the whole routing changed so that the current workaround does not work anymore.
I've read the two blog posts of the update Blog1Blog2 and it seems that I can't use the "old" workaround anymore as the function MapODataRoute() within the endpoints is now gone. It also seems that none of the built-in routing convention work for my use-case as all require the EDM model to be present at debug time.
Maybe I can use a custom IODataControllerActionConvention. I tried to active the convention by adding it to the Routing Convention but it seems I'm still missing a piece how to activate it.
services.TryAddEnumerable(
ServiceDescriptor.Transient<IODataControllerActionConvention, MyEntitySetRoutingConvention>());
Does this approach even work? Is it even possible to activate a dynamic model in the new odata preview? or does anybody has a slight idea how to approach a dynamic routing for the new odata 8.0?
There is an example for dynamic routing and dynamic model here:
https://github.com/OData/AspNetCoreOData/blob/master/sample/ODataDynamicModel/
See MyODataRoutingApplicationModelProvider and MyODataRoutingMatcherPolicy which will pass a custom IEdmModel to the controller.
The HandleAllController can handle different types and edm models in a dynamic way.
So after 5 days of internal OData debugging I managed to get it to work. Here are the necessary steps:
First remove all OData calls/attributes from your controller/configure services which might do funky stuff (ODataRoutingAttribute or AddOData())
Create a simple asp.net controller with the route to your liking and map it in the endpoints
[ApiController]
[Route("odata/v{version}/{Path?}")]
public class HandleAllController : ControllerBase { ... }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory)
{
...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
}
}
Create and register your InputFormatWrapper and OutputFormatWrapper
public class ConfigureMvcOptionsFormatters : IConfigureOptions<MvcOptions>
{
private readonly IServiceProvider _services;
public ConfigureMvcOptionsFormatters(IServiceProvider services)
{
_services = services;
}
public void Configure(MvcOptions options)
{
options.InputFormatters.Insert(0, new ODataInputFormatWrapper(_services));
options.OutputFormatters.Insert(0, new OdataOutputFormatWrapper(_services));
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.ConfigureOptions<ConfigureMvcOptionsFormatters>();
...
}
public class ODataInputFormatWrapper : InputFormatter
{
private readonly IServiceProvider _serviceProvider;
private readonly ODataInputFormatter _oDataInputFormatter;
public ODataInputFormatWrapper(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
//JSON and default is first - see factory comments
_oDataInputFormatter = ODataInputFormatterFactory.Create().First();
}
public override bool CanRead(InputFormatterContext context)
{
if (!ODataWrapperHelper.IsRequestValid(context.HttpContext, _serviceProvider))
return false;
return _oDataInputFormatter.CanRead(context);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
return _oDataInputFormatter!.ReadRequestBodyAsync(context);
}
}
// The OutputFormatWrapper looks like the InputFormatWrapper
Within the ODataWrapperHelper you can check stuff and get/set your dynamic edmModel. It is necessary in the end to set these ODataFeature()... That's not beautiful but it gets the dynamic job done...
public static bool IsRequestValid(HttpContext context, IServiceProvider serviceProvider)
{
//... Do stuff, get datasource
var edmModel = dataSource!.GetModel();
var oSegment = new EntitySetSegment(new EdmEntitySet(edmModel.EntityContainer, targetEntity, edmModel.SchemaElements.First(x => targetEntity == x.Name) as EdmEntityType));
context.ODataFeature().Services = serviceProvider.CreateScope().ServiceProvider;
context.ODataFeature().Model = edmModel;
context.ODataFeature().Path = new ODataPath(oSegment);
return true;
}
Now to the ugly stuff: We still need to register some ODataService in ConfigureServices(IServiceCollection services). I added a function there called AddCustomODataService(services) and there you can either register ~40 services yourself or do some funky reflection...
So maybe if someone from the odata team reads this, please consider opening Microsoft.AspNetCore.OData.Abstracts.ContainerBuilderExtensions
I created a
public class CustomODataServiceContainerBuilder : IContainerBuilder which is a copy of the internal Microsoft.AspNetCore.OData.Abstracts.DefaultContainerBuilder there I added the function:
public void AddServices(IServiceCollection services)
{
foreach (var service in Services)
{
services.Add(service);
}
}
and the ugly AddCustomODataServices(IServiceCollection services)
private void AddCustomODataService(IServiceCollection services)
{
var builder = new CustomODataServiceContainerBuilder();
builder.AddDefaultODataServices();
//AddDefaultWebApiServices in ContainerBuilderExtensions is internal...
var addDefaultWebApiServices = typeof(ODataFeature).Assembly.GetTypes()
.First(x => x.FullName == "Microsoft.AspNetCore.OData.Abstracts.ContainerBuilderExtensions")
.GetMethods(BindingFlags.Static|BindingFlags.Public)
.First(x => x.Name == "AddDefaultWebApiServices");
addDefaultWebApiServices.Invoke(null, new object?[]{builder});
builder.AddServices(services);
}
Now the controller should work again (with odataQueryContext and serialization in place) - Example:
[HttpGet]
public Task<IActionResult> Get(CancellationToken cancellationToken)
{
//... get model and entitytype
var queryContext = new ODataQueryContext(model, entityType, null);
var queryOptions = new ODataQueryOptions(queryContext, Request);
return (Collection<IEdmEntityObject>)myCollection;
}
[HttpPost]
public Task<IActionResult> Post([FromBody] IEdmEntityObject entityDataObject, CancellationToken cancellationToken)
{
//Do something with IEdmEntityObject
return Ok()
}

A suitable constructor for my type using DI could not be located

I have a .NET core WebAPI project that uses Hangfire for background jobs. I am trying to setup Simple Injector for DIs. My porject has an IFoo and a Foo class that looks as follows
public interface IFoo
{
void DoSomething();
}
public class Foo : IFoo
{
public Foo() { }
public void DoSomething()
{
Console.WriteLine($"Foo::DoSomething");
}
}
Below is how I setup the Simple Injector container. I am using Hangfire.SimpleInjector nuget package
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var container = new SimpleInjector.Container();
container.Register<IFoo, Foo>();
GlobalConfiguration.Configuration.UseActivator(
new Hangfire.SimpleInjector.SimpleInjectorJobActivator(container));
services.AddHangfire(x => x.UseSqlServerStorage(<My Connection string>));
services.AddHangfireServer();
services.AddControllers();
}
}
The background job is setup as following in controller
public IActionResult DoSomething()
{
var jobID = BackgroundJob.Enqueue<IFoo>( x => x.DoSomething());
return Ok();
}
But this job fails with following stack trace.
An exception occurred during processing of a background job.
System.InvalidOperationException A suitable constructor for type
'MyWebAPI.Controllers.IFoo' could not be located. Ensure the type is
concrete and services are registered for all parameters of a public
constructor.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider, Type, Object[])
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(IServiceProvider, Type)
at Hangfire.AspNetCore.AspNetCoreJobActivatorScope.Resolve(Type type)
at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)
at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_0.<PerformJobWithFilters>b__0()
at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter, PerformingContext, Func`1)
at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_1.<PerformJobWithFilters>b__2()
at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext, IEnumerable`1)
at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context)
at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext, IStorageConnection, String)
What am I doing wrong in setting all this up?
I am not sure the DI in Hangfire is for this purpose.
You need dependency injection to resolve inner dependencies,
not to resolve the main type you want to use.
You can check the documentation here.
Check this answer with same problem.

Unable to resolve service for type while attempting to activate

In my ASP.NET Core application, I get the following error:
InvalidOperationException: Unable to resolve service for type 'Cities.Models.IRepository' while attempting to activate 'Cities.Controllers.HomeController'.
I the HomeController I am trying to pass the Cities getter to the view like so:
public class HomeController : Controller
{
private readonly IRepository repository;
public HomeController(IRepository repo) => repository = repo;
public IActionResult Index() => View(repository.Cities);
}
I have one file Repository.cs that contains an interface and its implementation like so:
public interface IRepository
{
IEnumerable<City> Cities { get; }
void AddCity(City newCity);
}
public class MemoryRepository : IRepository
{
private readonly List<City> cities = new List<City>();
public IEnumerable<City> Cities => cities;
public void AddCity(City newCity) => cities.Add(newCity);
}
My Startup class contains the default-generated code from the template. I have made any changes:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
}
}
For the Dependency Injection framework to resolve IRepository, it must first be registered with the container. For example, in ConfigureServices, add the following:
services.AddScoped<IRepository, MemoryRepository>();
For .NET 6+, which uses the new hosting model by default, add the following in Program.cs instead:
builder.Services.AddScoped<IRepository, MemoryRepository>();
AddScoped is just one example of a service lifetime:
For web applications, a scoped lifetime indicates that services are created once per client request (connection).
See the docs for more information on Dependency Injection in ASP.NET Core.
We are getting this error in Entity frame work core database first approach. I followed below steps and error got resolved
Step 1: Check Your context class constructor should be like this
public partial class ZPHSContext : DbContext
{
public ZPHSContext(DbContextOptions<ZPHSContext> dbContextOptions)
: base(dbContextOptions)
{
}
}
Step 2: In Startup file
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ZPHSContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("BloggingDatabase")));
}
Step 3: Connection string in appsettings
"ConnectionStrings": {
"BloggingDatabase": "Server=****;Database=ZPHSS;Trusted_Connection=True;"
}
Step 4: Remove default code in OnConfiguring method in context class
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
Other answers are CORRECT, however I was spinning up a new asp.net core 2.1.x project and got this error.
Ended up being a typo by ME.
So in my Controller instead of Correctly using the Interface like this
public HomeController(IApplicationRepository applicationRepository)
{
_applicationRepository = applicationRepository;
}
My typo had me using ApplicationRepository instead of its interface IApplicationRepository
Notice below, and so with NO ERRORS spotting the missing "I" was fun :/
public HomeController(IApplicationRepository applicationRepository)
{
_applicationRepository = applicationRepository;
}
Thus the controller was not resolving the DI...
A method like this needs to be added to your Startup:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//...
// Add application services.
services.AddTransient<IRepository, MemoryRepository>();
//..
}
Services should be registered before used.
UPDATE:
If you do not want to use DI on your application, just create and instance of MemoryRepository on the constructor of HomeController, like this:
public class HomeController : Controller
{
private IRepository repository;
public HomeController()
{
repository = new MemoryRepository();
}
public IActionResult Index()
{
return View(repository.Cities);
}
}
You have to add your implementation to DI (Dependeny Injection) section. For .Net Core Mvc, it would be like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase()
);
services.AddScoped<IRepository, MemoRepostory>();
}
This may not be helpful for your code sample but in my case the same error was a result of a circular dependency.
you have to register your repository like this
services.AddSingleton<IRepository, MemoryRepository>();
In my case, I was trying to access context through constructor. like here;
private readonly Context _context;
public ImageController(Context context)
{
_context = context;
}
But When I tried to access the context just by creating an instance of class, it worked like here;
Context c = new Context();
For me I am using visual studio 2022 and .NET 6
the solution was add the following line in the Program.cs file :
builder.Services.AddSingleton<IHISInterface<UserDetails>, UserDetailsRepository>();
There is one more possibility that, You might have sent wrong variable in the place while writing this HTTPPOST last part code
mine is
var categoryMap = _mapper.Map(categoryCreate);
if(!_categoryRepository.CreateCategory(categoryMap))
{
ModelState.AddModelError("", "Something went wrong while saving");
return StatusCode(500, ModelState);
}
return Ok("Successfully created");
in the if condition I passed the category as parameter instead of categoryMap
so please cross check

Inject service into Action Filter

I am trying to inject a service into my action filter but I am not getting the required service injected in the constructor. Here is what I have:
public class EnsureUserLoggedIn : ActionFilterAttribute
{
private readonly ISessionService _sessionService;
public EnsureUserLoggedIn()
{
// I was unable able to remove the default ctor
// because of compilation error while using the
// attribute in my controller
}
public EnsureUserLoggedIn(ISessionService sessionService)
{
_sessionService = sessionService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Problem: _sessionService is null here
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
}
And I am decorating my controller like so:
[Route("api/issues"), EnsureUserLoggedIn]
public class IssueController : Controller
{
}
Startup.cs
services.AddScoped<ISessionService, SessionService>();
Using these articles as reference:
ASP.NET Core Action Filters
Action filters, service filters and type filters in ASP.NET 5 and MVC 6
Using the filter as a ServiceFilter
Because the filter will be used as a ServiceType, it needs to be registered with the framework IoC. If the action filters were used directly, this would not be required.
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();
...
}
Custom filters are added to the MVC controller method and the controller class using the ServiceFilter attribute like so:
[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}
There were other examples of
Using the filter as a global filter
Using the filter with base controllers
Using the filter with an order
Take a look, give them a try and see if that resolves your issue.
Hope this helps.
Global filters
You need to implement IFilterFactory:
public class AuthorizationFilterFactory : IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// manually find and inject necessary dependencies.
var context = (IMyContext)serviceProvider.GetService(typeof(IMyContext));
return new AuthorizationFilter(context);
}
}
In Startup class instead of registering an actual filter you register your filter factory:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilterFactory());
});
One more way for resolving this problem. You can get your service via Context as in the following code:
public override void OnActionExecuting(ActionExecutingContext context)
{
_sessionService = context.HttpContext.RequestServices.GetService<ISessionService>();
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
Please note that you have to register this service in Startup.cs
services.AddTransient<ISessionService, SessionService>();
Example
private ILoginService _loginService;
public override void OnActionExecuting(ActionExecutingContext context)
{
_loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
}
Hope it helps.
After reading this article ASP.NET Core - Real-World ASP.NET Core MVC Filters (Aug 2016) I implemented it like this:
In Starup.cs / ConfigureServices:
services.AddScoped<MyService>();
In MyFilterAttribute.cs:
public class MyFilterAttribute : TypeFilterAttribute
{
public MyFilterAttribute() : base(typeof (MyFilterAttributeImpl))
{
}
private class MyFilterAttributeImpl : IActionFilter
{
private readonly MyService _sv;
public MyFilterAttributeImpl(MyService sv)
{
_sv = sv;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_sv.MyServiceMethod1();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_sv.MyServiceMethod2();
}
}
}
In MyFooController.cs :
[MyFilter]
public IActionResult MyAction()
{
}
Edit: Passing arguments like [MyFilter("Something")] can be done using the Arguments property of the TypeFilterAttribute class: How do I add a parameter to an action filter in asp.net? (rboe's code also shows how to inject things (the same way))
While the question implicitly refers to "filters via attributes", it is still worth highlighting that adding filters "globally by type" supports DI out-of-the-box:
[For global filters added by type] any constructor dependencies will be populated by dependency injection (DI). Adding a filter by type is equivalent to filters.Add(new TypeFilterAttribute(typeof(MyFilter))).
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
With regards to attribute-based filters:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they're applied. This is a limitation of how attributes work.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
However, as mentioned in the previous answers to the OP, there are ways of indirection that can be used to achieve DI. For the sake of completeness, here are the links to the official docs:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory implemented on your attribute

Asp.Net Core - Need to access to HttpContext instance from instances generated without Dependency Injection

I'm using Asp.Net Core RC1, and I've to access to an HttpContext instance from instances generated by a model generator (from interceptors of Castle.Core, for be exact). Model generator has to be a single instance through the entire application.
I need to create an instance of ModelGenerator into startup file, because it is used into static lambdas needed to configure some serializers. Serializers are statically registered, so I have to write into startup:
var modelGenerator = new ModelGenerator();
Serializers.Configure(modelGenerator); // static use of model generator instance
I also add modelGenerator as singleton instance for other uses with DI.
services.AddInstance<IModelGenerator>(modelGenerator);
What I would have done with DI is to take a IHttpContextAccessor interface from ModelGenerator's constructor, but into this context I can't because I don't have an instance on startup. I need something like a ServiceLocator to call from ModelGenerator, or some other patter that I ignore.
How can reach an updated HttpContext instance, with information of current request, from interceptors generated by ModelGenerator?
It appears that there is no way to get an instance of HttpContext in application startup. This makes sense - in previous versions of MVC this wasn't possible in IIS integrated mode or OWIN.
So what you have are 2 issues:
How do you get the IHttpContextAccessor into your serializer?
How do you ensure the HttpContext is not accessed until it is available?
The first issue is pretty straightforward. You just need to use constructor injection on IHttpContextAccessor.
public interface ISerializer
{
void Test();
}
public class ModelGenerator : ISerializer
{
private readonly IHttpContextAccessor httpContextAccessor;
public ModelGenerator(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void Test()
{
var context = this.httpContextAccessor.HttpContext;
// Use the context
}
}
And to register...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Other code...
// Add the model generator
services.AddTransient<ISerializer, ModelGenerator>();
}
// 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)
{
var serializers = app.ApplicationServices.GetServices<ISerializer>();
foreach (var serializer in serializers)
{
Serializers.Configure(serializer);
}
// Other code...
}
The second issue can be resolved by moving whatever initialization calls that you require HttpContext in into a global filter.
public class SerializerFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext context)
{
// TODO: Put some kind of if condition (possibly a
// global static variable) here to ensure this
// only runs when needed.
Serializers.Test();
}
}
And to register the filter globally:
public void ConfigureServices(IServiceCollection services)
{
// Other code...
// Add the global filter for the serializer
services.AddMvc(options =>
{
options.Filters.Add(new SerializerFilter());
});
// Other code...
}
If your Serializers.Configure() method requires HttpContext to work, then you will need to move that call into the global filter.

Categories

Resources