Resharp DI and DNS resolution - asp.net-core

I'm upgrading to version 107 restsharp and i'm wondering if both these options below are ok ways of doing dependency injection in dotnet with restsharp.
The documents say don't create a new restsharp for every request (connection pool exhaustion resaons) but if the httpclient is injected via the constructor will I be get the benefit of DNS resolution changes even though rest client is contained in transient scoped object (i think that's what AddHttpClient will do). I know there will be some price to pay for creating a transient object, but for a general purpose business app maybe that's fine?
Recommended way as described in the documentation
services.AddSingleton<IMyClient, MyClient>();
public class MyClient : IMyClient
{
protected readonly RestClient _restClient;
public MyClient()
{
_restClient = new RestClient();
}
}
OR: is it ok to do this?
services.AddHttpClient<IMyClient, MyClient>();
public class MyClient : IMyClient
{
protected readonly RestClient _restClient;
public MyClient(HttpClient httpClient)
{
_restClient = new RestClient(httpClient);
}
}

You should AddSingleton, not AddHttpClient as per official documentation:
https://restsharp.dev/v107/#restclient-lifecycle
If you use a dependency-injection container, register your API client as a singleton.
I believe it's becouse RestClient is managing the connection pools and addressing known issues, that AddHttpClient would typically address.
If you would use HttpClient directly, you should follow Microsofts recommendations from below URL, where you have a choice of Singleton or AddHttpClient:
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
Also, not sure how your 2nd option works at this point.
Also, great video explaining more about what AddHttpClient does (sets HttpClient as Transient etc):
https://www.youtube.com/watch?v=Z6Y2adsMnAA&t=335s

Related

Register dependent services on every request

I am working in Multi-tenant solution primarily there are 2 type of applications
WebAPI
Console app to process message from queue
I have implemented dependency injection to inject all services. I have crated TenantContext class where I am resolving tenant information from HTTP header and it's working fine for API, but console application getting tenant information with every message (tenant info is part of queue message) so I am calling dependency injection register method on every incoming message which is not correct, do you have any suggestion/solution here?
The way I am resolving ITenantContext in API
services.AddScoped<ITenantContext>(serviceProvider =>
{
//Get Tenant from JWT token
if (string.IsNullOrWhiteSpace(tenantId))
{
//1. Get HttpAccessor and processor settings
var httpContextAccessor =
serviceProvider.GetRequiredService<IHttpContextAccessor>();
//2. Get tenant information (temporary code, we will get token from JWT)
tenantId = httpContextAccessor?.HttpContext?.Request.Headers["tenant"]
.FirstOrDefault();
if (string.IsNullOrWhiteSpace(tenantId))
//throw bad request for api
throw new Exception($"Request header tenant is missing");
}
var tenantSettings =
serviceProvider.GetRequiredService<IOptionsMonitor<TenantSettings>>();
return new TenantContext(tenantId, tenantSettings );
});
Create two different ITenantContext implementations. One for your Web API, and one for your Console application.
Your Web API implementation than might look as follows:
public class WebApiTenantContext : ITenantContext
{
private readonly IHttpContextAccessor accessor;
private readonly IOptionsMonitor<TenantSettings> settings;
public WebApiTenantContext(
IHttpContextAccessor accessor,
IOptionsMonitor<TenantSettings> settings)
{
// Notice how the dependencies are not used in this ctor; this is a best
// practice. For more information about this, see Mark's blog:
// https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/
this.accessor = accessor;
this.settings = settings;
}
// This property searches for the header each time its called. If needed,
// it can be optimized by using some caching, e.g. using Lazy<string>.
public string TenantId =>
this.accessor.HttpContext?.Request.Headers["tenant"].FirstOrDefault()
?? throw new Exception($"Request header tenant is missing");
}
Notice that this implementation might be a bit naive for your purposes, but hopefully you'll get the idea.
This class can be registered in the Composition Root of the Web API project as follows:
services.AddScoped<ITenantContext, WebApiTenantContext>();
Because the WebApiTenantContext has all its dependencies defined in the constructor, you can do a simple mapping between the ITenantContext abstraction and the WebApiTenantContext implementation.
For the Console application, however, you need a very different approach. The WebApiTenantContext, as shown above, is currently stateless. It is able to pull in the required data (i.e. TenantId) from its dependencies. This probably won't work for your Console application. In that case, you will likely need to manually wrap the execution of each message from the queue in a IServiceScope and initialize the ConsoleTenantContext at the beginning of that request. In that case, the ConsoleTenantContext would look merely as follows:
public class ConsoleTenantContext : ITentantContext
{
public string TenantId { get; set; }
}
Somewhere in the Console application's Composition Root, you will have to pull messages from the queue (logic that you likely already have), and that's the point where you do something as follows:
var envelope = PullInFromQueue();
using (var scope = this.serviceProvider.CreateScope())
{
// Initialize the tenant context
var context = scope.ServiceProvider.GetRequiredService<ConsoleTenantContext>();
content.TenantId = envelope.TenantId;
// Forward the call to the message handler
var handler = scope.ServiceProvider.GetRequiredService<IMessageHandler>();
handler.Handle(envelope.Message);
}
The Console application's Composition Root will how have the following registrations:
services.AddScoped<ConsoleTenantContext>();
services.AddScoped<ITenentContext>(
c => c.GetRequiredServices<ConsoleTenantContext>());
With the registrations above, you register the ConsoleTenantContext as scoped. This is needed, because the previous message infrastructure needs to pull in ConsoleTenantContext explicitly to configure it. But the rest of the application will depend instead on ITenantContext, which is why it needs to be registered as well. That registration just forwards itself to the registered ConsoleTenantContext to ensure that both registrations lead to the same instance within a single scope. This wouldn't work when there would be two instances.
Note that you could use the same approach for Web API as demonstrated here for the Console application, but in practice it's harder to intervene in the request lifecycle of Web API compared to doing that with your Console application, where you are in full control. That's why using an ITenantContext implementation that is itself responsible of retrieving the right values is in this case an easier solution for a Web API, compared to the ITenantContext that is initialized from the outside.
What you saw here was a demonstration of different composition models that you can use while configuring your application. I wrote extensively about this in my series on DI Composition Models on my blog.

How does one consume Bugsnag from singleton?

I have an ASP.NET core application that implements a singleton service.
I would like errors to be sent to Bugsnag so I've added IClient bugsnag to my constructor but am getting the following error during startup:
Cannot consume scoped service 'Bugsnag.IClient' from singleton
I cannot find anything in the Bugsnag docs that mentions IClient being scoped or how to construct a singleton instance to use in my application.
As mentioned in the comments, a possible solution would be to use IServiceScopeFactory to create a scope to use in the singleton. This is not ideal because the whole reason for using Bugsnag is to have something that catches all unhandled errors in the application and reports them to a central point for monitoring.
UPDATE: since posting the question I came across a GitHub issue addressing this problem.
In short, you couldn't register the service as singleton , since the IClient has been registered as scoped but it called in the singleton service.
When we have a scoped instance, each time we load the page, a new instance of our ChildService is created and inserted in the parent service.
Whereas when we do a singleton, it keeps the exact same instance (Including the same child services). When we make the parent service a singleton, that means that the child service is unable to be created per page load.
ASP.NET Core is essentially stopping us from falling in this trap of thinking that a child service would be created per page request, when in reality if the parent is a singleton it’s unable to be done. This is why the exception is thrown.
If you want to use iclient in the singleton service, it's impossible. The right way is make that singleton service to scoped.
Update:
As #JHBonarius says, we could inject the IServiceScopeFactory to the singleton sercive and manage the scope service.
More details, you could refer to below codes:
public class Singleton : ISingleton
{
private readonly IServiceScopeFactory scopeFactory;
public Singleton(IServiceScopeFactory scopeFactory)
{
this.scopeFactory = scopeFactory;
}
public void MyMethod()
{
using(var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<yourservice>();
// when we exit the using block,
// the IServiceScope will dispose itself
// and dispose all of the services that it resolved.
}
}
}

An injectable class needs to read http headers prior to being injected into other class constructors

Is it possible to read http header keys and provide them in a service class to be used by injection to other services.
In startup.cs - I know this happens before the request is materialized, therefore HttpContext is null.
services.AddScoped<IMyConfigurationService>(provider =>
{
string theKey = new HttpContextAccessor().HttpContext?.Request?.Headers["TheKey"].ToString();
return new MyConfigurationService(theKey);
});
In ClassA
public MyCommandDirector(
IMyConfigurationService myConfiguration)
//IHttpContextAccessor httpContext) //<-- Can not do this. This library is shared in legacy apps.
{
...
}
The library is shared across different technologies and IHttpContextAccessor is not available in legacy win forms app. Is there another way I can inject a configuration class that has already read keys in the http header prior to injection in web api 2.0 .net core?

How to add Custom Interceptor in Spring data rest (spring-data-rest-webmvc 2.3.0)

I am working on the spring data rest services & facing some issue in the custom interceptors. Earlier I used spring-data-rest-webmvc 2.2.0 & added interceptor in following way.
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new MyInterceptor() });
return mapping;
}
It worked perfectly fine for me. But when i upgraded to spring-data-rest-webmvc 2.3.0 version, I noticed that handlerMapping is hidden behind DelegatingHandlerMapping. Hence I tried to add interceptor in following way.
In one of my config class I have extended RepositoryRestMvcConfiguration class & override its method.
public class AppConfig extends RepositoryRestMvcConfiguration {
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping()
{
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(super.resourceMappings(), super.config());
repositoryMapping.setInterceptors(new Object[] { new MyInterceptor()});
repositoryMapping.setJpaHelper(super.jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(super.config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
But after adding this some of my repository operations (findAll() operation on repository) start failing. If I removed this interceptors those operations worked fine. (In this interceptor I am just authenticate the user.)
Hence I am unable to understand problem here. Am I adding the interceptor in wrong way? Is there any other way to add the interceptor?
You should not use repositoryMapping.setInterceptors() - it destoys the internal interceptors Spring placed there, and that's probably the reason some methods stopped working.
I suggest you override jpaHelper() method and put your interceptors into the JpaHelper object in RepositoryRestMvcConfiguration. Spring will should them to the global interceptor list.
But, again, if all you need is authentication, why not use a Spring Security filter?
EDIT: the solution above works only for RepositoryRestHandlerMapping, not for BasePathAwareHandlerMapping.
I suggest you declare a custom MappedInterceptor bean somewhere:
#Bean
public MappedInterceptor myMappedInterceptor() {
return new MappedInterceptor(new String[]{"/**"}, new MyInterceptor());
}
From my understanding of the source code Spring should automatically add this interceptor to all request handlers.

How to resolve dependency inside AuthorizeAttribute with WebApi and Ninject

My current setup is using Ninject for simple IoC, everything goes fine, but I'm not able to resolve one of the classes I need inside my AuthorizeAttribute. I need to access a class that does ClaimsVerification:
Here's my code:
IoC Config:
var kernel = new StandardKernel(); // Ninject IoC
// These registrations are "per instance request".
// See http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
kernel.Bind<RepositoryFactories>().To<RepositoryFactories>()
.InSingletonScope();
kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
kernel.Bind<ISmartDocumentorUow>().To<SmartDocumentorUow>();
kernel.Bind<IClaimsVerification>().To<ClaimsVerification>();
// kernel
//kernel.BindFilter<MyAuthorizeAttribute>(FilterScope.Controller, 0).WhenControllerHas<RequireRolesAttribute>();
// Tell WebApi how to use our Ninject IoC
config.DependencyResolver = new NinjectDependencyResolver(kernel);
MyAuthorizeAttribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
[Inject]
IClaimsVerification clamisverify { get; set; }
public MyAuthorizeAttribute()
{
//var x = System.Web.Mvc.DependencyResolver.Current.(typeof(IClaimsVerification));
}
Yap, sorry, the problem was injecting the iClaimsverification that isn't working in web api..
I tryed with the public property and still it didn't work.
the bindfilter is commented out, because it doesn't exist in the core NInject api (dll), it does exists in the MVC dll of ninject but it works for Action filters in the web mvc, and not in the api mvc for what i can tell..
i do solved the issue like this, though i don't like a lot of this fix:
private IClaimsVerification verifier
{
get
{
return (GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IClaimsVerification)) as IClaimsVerification);
}
}
The property you have marked with Inject is private - you need to initialize Ninject with a custom configuration to opt into what would be a much less efficient process
(You didnt state the problem in your question. I see you were trying BindFilter, but it's commented out (why?) - this is the correct approach. I recommend reading the Ninject.MVC3 wiki article on BindFilter for an example)