Injecting values in the DelegatingHandler through IHttpClientFactory - c#

When building custom middelware for IHttpClientBuilder, using the DelegatingHandler and IHttpClientFactory.
The constructor of my class looks like this.
public HttpApiFactory(HttpApifactoryConfig config, IHttpClientFactory httpClientFactory, IServiceProvider serviceProvider)
{
this.httpClientFactory = httpClientFactory;
this.serviceProvider = serviceProvider;
this.config = config;
}
And this is the method called
public IApiClient CreateClient(MethodInfo methodInfo, object param, string pipelineName)
{
return new Simployer.Service.Client.HttpApiClient(
httpClientFactory.CreateClient(pipelineName), config.serializer);
}
I know that one of the registred handlers that will be instanciated needs an instance of a given object. This type is not registerd in the IServiceCollection because I have no way of instanciating the class at startup or by a factory.
I want to add the "methodInfo" param to the IServiceContainer, IServiceProvider somehow before calling the httpClientFactory.CreateClient()
I was thinking of IServiceScope, but i can't find a way to insert the instance.

I solved it by changing the constructor to accept a factory that was able to get the values for me.

Related

Direct Injection of NLog into .Net Core project always NULL?

I'm new with .Net Core (using 3.1) and using Dependency injection. I was able to setup NLog in the Web API Controller but now I'm trying to get NLog to work in a separate business class following the basics of what I did in the API Controller. I keep get errors about the logger being NULL and when I put a break point on the _logger and _config, sure enough they are NULL. I'm not sure what I'm missing here.
This is my Business class and I thought I had it setup correctly but obviously not.
public class ShiftBLL
{
private static IConfiguration _config;
private static ILogger<ShiftBLL> _logger;
public ShiftBLL(ILogger<ShiftBLL> logger, IConfiguration config)
{
_config = config;
_logger = logger;
}
public static List<AppsShift> GetShifts(string station, string shiftDate)
{
_logger.LogInformation("Staion: {0} | ShiftDate: {1}", station, shiftDate);
*code removed for clarity. The app breaks on the initial call of _logger.
}
}
FIX
I removed the "static" from the ShiftBLL class as well as from the local parameters. Then I had to create an object of ShiftBLL in my Controller passing in the logger and config from the controller where I have DI working into the ShiftBLL. I ended up with this in my Controller:
ShiftBLL BLL = new ShiftBLL(_logger, _config);
listShifts = BLL.GetShifts(station, shiftDate);
Here is my updated ShiftBLL:
public class ShiftBLL
{
private IConfiguration _config;
private readonly ILogger _logger;
public ShiftBLL(ILogger logger, IConfiguration config)
{
_config = config;
_logger = logger;
}
public List<AppsShift> GetShifts(string station, string shiftDate)
{
_logger.LogInformation("Staion: {0} | ShiftDate: {1}", station, shiftDate);
}
Still getting my head wrapped around Dependency Injection.
When using DI, the DI container will insert the parameters for all dependencies when needed.
I will try to explain with a simplified example:
public class MyClass
{
public MyClass(ShiftBLL shiftBll)
{ .. }
}
When resolving MyClass, the DI container will do:
Find all needed dependencies for creating an instance of MyClass - in this case: ShiftBLL
Find all needed dependencies for creating ShiftBLL, in this ILogger and IConfiguration
Find the registrations for ILogger and IConfiguration (as those are interfaces), and dependencies etc.
Create a instance of ILogger and IConfiguration (via the registrations), create instance of ShiftBLL by injecting the instances into the constructor
Create a instance of MyClass and inject in instance of ShiftBLL.
So with this chain, all your constructor dependencies are created.
But if you're are using static methods in ShiftBLL, you are not sure if the constructor of ShiftBLL (with the ILogger) is ever invoked - so the ILogger field could be null. .
So to fix this case you could do:
Make the method non-static, or
Send all dependencies (ILogger) to the static method as parameter
You mixed up static methods calls and Dependency Injection. I would say to avoid the use of static methods whenever possible, anyway when you are using static methods you cannot access dependencies that are injected through dependency injection. For instance you cannot access _logger from GetShifts because you don't have an instance of ShiftBll and the fact that you call it statically means that its constructor is never called.
This is a working example of how to design classes and their dependencies and how to register them.
public class AController : Controller {
private readonly ILogger<AController> _logger;
private readonly ShiftBll _shiftBll;
public AController(ILogger<AController> logger, ShiftBll shiftBll) {
_logger = logger;
_shiftBll = shiftBll;
//I'm pretty sure you didn't inject the dependency here
//you preferred to use a static reference, but it is not correct in this case
}
public ActionResult AnAction() {
var shifts = _shiftBll.GetShifts("aStation", "aShiftDate");
}
}
public class ShiftBLL
{
private readonly IConfiguration _config; //not static
private readonly ILogger<ShiftBLL> _logger; //not static
public ShiftBLL(ILogger<ShiftBLL> logger, IConfiguration config)
{
_config = config;
_logger = logger;
}
public List<AppsShift> GetShifts(string station, string shiftDate) //not static
{
_logger.LogInformation("Staion: {0} | ShiftDate: {1}", station, shiftDate);
*code removed for clarity. The app breaks on the initial call of _logger.
}
}
.NET Core DI automatically registers controllers, but you have to manually register your own services. To do this go in the ConfigureServices method of your Startup.cs and add
services.AddScoped<ShiftBll>();
now you can use ShiftBll from AController.

How to pass a callback parameter to my service class with IServiceCollection in C#?

First of all, I want to apologize for my bad English cause I'm a Chinese. And I will try my best to describe my question now. If you can read this patiently and give me some recommendations it would be appreciated.
I am going to develop a Web application with Microsoft.AspNetCore. There is a sub-project with a service class named MyService and a ServiceCollectionExtensions used to add MyService to WebHostBuilder.
public class MyService: IMyService
{
public MyService(IHttpContextAccessor httpContextAccessor, Func<RequestContext> callBack = null)
{
_httpContextAccessor = httpContextAccessor;
_callBack = callBack;
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyService(this IServiceCollection services, Func<RequestContext> callBack = null)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// TODO: pass callBack function to the constructor of MyService
return services.AddScoped<IMyService, MyService>();
}
}
So my question is how can I pass the callBack parameter to the constructor of MyService class?
The addScoped methods has a few overloads one of which is a factory which creates the instance of the service so you can use that overload and pass in your created factory and in the factory definition add pass the callback in your Service constructor. You will find more details here
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addscoped?view=aspnetcore-3.0#Microsoft_Extensions_DependencyInjection_ServiceCollectionServiceExtensions_AddScoped_Microsoft_Extensions_DependencyInjection_IServiceCollection_System_Type_System_Func_System_IServiceProvider_System_Object__
Write your extension method as follows:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyService(this IServiceCollection services, Func<RequestContext> callBack = null)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IMyService, MyService>(sp => new MyService(new HttpContextAccessor(), new Func<RequestContext>(target)));
return services;
}
}

A suitable constructor for type 'RestDataService' could not be located

While running the .Net Core 2.0 API endpoint getting below error.
A suitable constructor for type 'RestDataService' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.
public partial class RestDataService : IRestDataService
{
private static HttpClient _client;
private static AppConfiguration _configuration;
private const short MaxRetryAttempts = 3;
private const short TimeSpanToWait = 2;
public RestDataService(AppConfiguration configuration)
{
_client = configuration.HttpClient;
_configuration = configuration;
}
........
And my startup class is something like this :
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var config = new AppConfiguration
{
Environment = Configuration["environment"],
};
services.AddMvc().AddJsonOptions(o => o.SerializerSettings.NullValueHandling = NullValueHandling.Include);
services.AddMemoryCache();
services.AddCors();
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddSingleton(Configuration);
services.AddSingleton(config);
services.AddLogging();
services.AddTransient<IRestDataService, RestDataService>();
services.AddHttpClient<IRestDataService, RestDataService>()
.AddPolicyHandler(request => request.Method == HttpMethod.Get ? retryPolicy : noOp);
Any suggestions, to get rid of this? constructor is already public and all the parameters are registered in startup file
I received the error "A suitable constructor for type '<type>' could not be located." after accidentally generating a protected constructor instead of public constructor. Marking it back to public fixed it.
For AddHttpClient, you need to provide HttpClient parameter for RestDataService. And, you need to register AppConfiguration.
RestDataService
public class RestDataService: IRestDataService
{
private static HttpClient _client;
private static AppConfiguration _configuration;
private const short MaxRetryAttempts = 3;
private const short TimeSpanToWait = 2;
public RestDataService(AppConfiguration configuration
, HttpClient client)
{
_client = configuration.HttpClient;
_configuration = configuration;
}
}
Startup.cs
var config = new AppConfiguration
{
Environment = Configuration["environment"],
};
services.AddSingleton(typeof(AppConfiguration), config);
services.AddHttpClient<IRestDataService, RestDataService>();
I just ran across this issue because I was accidentally registering HttpClient to an interface.
public void ConfigureServices(IServiceCollection services) {
// !! Wrong, this is done automatically by AddHttpClient<IMyService, MyService>()
services.AddTransient<IMyService, MyService>();
// !! There is no suitable constructor for IMyService, since it is an interface.
services.AddHttpClient<IMyService>();
// Instead, let AddHttpClient manage your service's lifetime,
// and tell it the implementation.
// It is registered with a Transient lifetime as described below.
services.AddHttpClient<IMyService, MyService>();
}
Important context, from source: https://www.stevejgordon.co.uk/ihttpclientfactory-patterns-using-typed-clients-from-singleton-services
When defining typed clients in your ConfigureServices method, the typed service is registered with transient scope. This means that a new instance is created by the DI container every time one is needed. The reason this occurs is that a HttpClient instance is injected into the typed client instance. That HttpClient instance is intended to be short lived so that the HttpClientFactory can ensure that the underlying handlers (and connections) are released and recycled.
You have to define which concrete class of interface you want to use for IRestDataService. So, define like this.
services.AddTransient<IRestDataService, RestDataService>();
Remove static keyword before AppConfiguration.
private readonly AppConfiguration _configuration;
In my case I got this problem when inattentively duplicating row with DI service registration to register new service but accidentally missed that this is .AddHttpClient() method instead of .AddTransient() or .AddScoped()
This doesn't directly answer the question, but is related.
For anyone using ActivatorUtilities.CreateInstance<T>(IServiceProvider, params object[] parameters) it's worth mentioning that every parameter must be in the parameters of the class you are creating. I.e.
public class Example
{
public Example(AppConfiguration configuration)
{
...
}
}
_ = ActivatorUtilities.CreateInstance<Example>(_serviceProvider, new HttpClient())
This will fail as the Example class doesn't have a constructor that has a HttpClient paramter
I had forgotten to add a RequestDelegate next parameter to my middleware constructor. Without this, I would get an exception with the message "A suitable constructor for type [Type] could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor" upon calling IApplicationBuilder.UseMiddleware. My constructor was already public, and my services were all registered. I just needed to add RequestDelegate next to my parameters.

asp.net mvc core dependency injection constructor parameter

I'm trying to understand dependency injection in ASP.NET MVC CORE.
All examples are the same, they show to register HttpContextAccessor
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
Then the class that wants to access it:
public class UserService : IUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public bool IsUserLoggedIn()
{
var context = _httpContextAccessor.HttpContext;
return context.User.Identities.Any(x => x.IsAuthenticated);
}
}
But then, when i actually want to create an instance of UserService, it asks for the httpContextAccessor object in constructor, where do i get that from?
When using dependency injection, you are not supposed to actually create any service yourself. To consume your UserService, you should just inject that somewhere as well.
Typically, the flow in ASP.NET Core for your application code starts in a controller. So if you want to use the UserService inside of a controller action, you should inject it into the controller:
public class ExampleController : Controller
{
private readonly IUserService _userService;
public ExampleController(IUserService userService)
{
_userService = userService;
}
public IActionResult Index()
{
var isLoggedIn = _userService.IsUserLoggedIn();
// …
return View();
}
}
So you don’t create an instance yourself using new but instead you rely on the dependency injection system to provide you with an instance.
You just need to make sure to register the service inside of the ConfigureServices:
services.AddTransient<IUserService, UserService>();
This principle holds regardless of where you are within your application. Since the entry point is always being created by the system, you are always inside of a dependency injection context, so you can just depend on things which have dependencies themselves (which again could have more dependencies etc).
I would strongly suggest you to read the chapter on dependency injection of the documentation, as it covers the idea very well. It also explains what the different lifetimes mean.
in DI, you normally dont need to create Instance by yourself. Instead you need to register your implemented services to the DI service container and then call it inside your constructor.
This way you eliminates the need for manual instance creation.
services.AddScoped<IMyService, MyService>();
then
class MyConsumerClass
{
private readonly IMyService _myService;
MyConsumerclass(IMyService myService)
{
_myService = myService;
}
}
In this way, you don't need to care about what services is needed to be initialized (parameterized) into your constructor.

How to inject ClaimsPrincipal in Nancy

I have a Nancy module and a decorator class that needs to know the ClaimsPrincipal in order to retrieve the user's email address. So I have declared a constructor as
public EmailDecorator(IDbConnectionFactory connectionFactory,
IPrincipal principal,
IEmailClient emailClient) : base(connectionFactory)
What I'm struggling to figure out is how to inject the principal into the constructor. I'm using Simple Injector for DI and it has been very effective. But if I override the ConfigureRequestContainer() method, which has as a parameter for the NancyContext, to instantiate IPrincipal I get an exception
protected override void ConfigureRequestContainer(
TinyIoCContainer container, NancyContext context)
{
container.Register<IPrincipal>((c, o) => context.CurrentUser);
base.ConfigureRequestContainer(container, context);
}
The exception indicates Simple Injector doesn't know about IPrincipal which is registered in the default TinyIOC container.
The configuration is invalid. Creating the instance for type InvoiceModule failed. The constructor of type EmailDecorator contains the parameter with name 'principal' and type IPrincipal that is not registered. Please ensure IPrincipal is registered, or change the constructor of EmailDecorator.
Edit
I've spent way too long trying to resolve this and I suspect the answer is to stop trying to do it this way. In my original example, I'm trying to inject IPrincipal into the decorator constructor which is incorrect. Instead I need to inject some form of service that allows me to derive IPrincipal when one of the methods in the decorator is called, e.g. Func<IPrincipal>. Nancy provides an override-able method ConfigureRequestContainer() that is called for each request, so could potentially be used:
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
container.Register<Func<IPrincipal>>((c, o) =>
{
return () => context.CurrentUser;
});
}
Except that the request container is a TinyIoCContainer and I'm using SimpleInjector. So, maybe I can add a SimpleInjector registration that offloads resolution to the request container?
_container.RegisterSingleton<Func<IPrincipal>>(() => nancy.Resolve<Func<IPrincipal>>());
Actually no, nancy is not the same container as the request container. So maybe somewhere there is a container that can successfully resolve IPrincipal but I do not know how to access it. The objective of the exercise was to avoid having to modify the method signatures of the repo code to include the ClaimsPrincipal, kind of like the old days when it was possible to call Thread.CurrentPrincipal. Still looking for a clue but will start modifying the method signature to include a ClaimsPrincipal.
The answer is to use IHttpContextAccessor. In Startup.cs add a new service definition in the ConfigureServices() method:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAppSettings>(settings);
services.AddSingleton<IHttpContextAccessor>(new HttpContextAccessor());
…
}
Then pass app.ApplicationServices as a parameter to the bootstrapper:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAppSettings settings)
{
var log = ConfigureLogger(settings);
app.UseOwin(buildFunc =>
{
buildFunc.UseMonitoringAndLogging(log, HealthCheckAsync);
buildFunc.UseNancy(cfg =>
cfg.Bootstrapper = new Bootstrapper(env, log, app.ApplicationServices));
});
}
In the Bootstrapper class ApplicationStartup() method, register the service in the Simple Injector container:
public Bootstrapper(IHostingEnvironment env, ILogger log, IServiceProvider services)
{
_env = env;
_log = log;
_services = services;
}
…
_container.Register<IHttpContextAccessor>(() =>
(IHttpContextAccessor) _services.GetService(typeof(IHttpContextAccessor)));
Then in the decorator class, add IHttpContextAccessor to the constructor:
public EmailDecorator(IDbConnectionFactory connectionFactory,
ILogger log,
IHttpContextAccessor httpContextAccessor,
IEmailClient emailClient) : base(connectionFactory, log)
{
_emailClient = emailClient;
_httpContextAccessor = httpContextAccessor;
}
The ClaimsPrincipal can be accessed from the _httpContextAccessor.HttpContext property.

Categories