How does one consume Bugsnag from singleton? - asp.net-core

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.
}
}
}

Related

Resharp DI and DNS resolution

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

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 to access the TelemetryClient singleton instance in startup for ASP.NET core app

I have an ASP.NET Core Web App that uses Application Insights for telemetry. I would like to wrap the TelemetryClient object with my own interface and add that instance to the services collection. To do this I need access to the singleton instance of Telemetry client in startup.cs. I can get the service information with this code:
var info = services.Where(x => x.ServiceType.Name == "TelemetryClient").Single();
That info has a ImplementationInstance that I would expect to find the instance in, but it is null. Is there any way to get the singleton instance of this item so I can create my wrapper class instance with it? My other option is to create the wrapper class in my controller constructors because there I can access it from the services container via this code:
HttpContext.RequestServices.GetService(typeof(T)) as T;
Any help would be appreciated. Thanks.
Couldn't you do this?
public class Wrapper
{
public Wrapper(TelemetryClient telemetryClient)
{
// Handle init
}
}
And register that in the service collection:
services.AddTransient<Wrapper>();
You can then get the Wrapper in your controllers through the constructor.
If this does not work for you, please leave a comment and explain why.

Shared service provider NServiceBus and ASPNET Core

I'm creating a way to publish integration events via NServiceBus that are published from within an operation executed in a handler. The path I've chosen is bridge the IIntegrationEventProvider with IEventCollectionPublisher to get the published events from domain layer.
public sealed class Bridge : IIntegrationEventProvider /* Infrastructure */,
IEventCollectionPublisher /* Domain */
{
private readonly List<object> _events = new List<object>();
void IEventCollectionPublisher.Publish(object domainEvent) { _events.Add(domainEvent): }
IReadOnlyCollection IIntegrationEventProvider.GetEvents() => _events;
}
Since NServiceBus has its own service provider (IBuilder) I need to resolve the class doing the application operation from the IServiceProvider that is made available to pipeline in ServiceScopedBehavior. Doing this I can get the bridge instance that contains the events published from domain layer and publish them as integration events using NServiceBus.
I published a Gist with (hopefully) the code pieces needed to grasp what I'm trying to achieve.
The question is: can I instruct NServiceBus to just delegate calls to the application service provider instead of building it and copy all instructions in endpoint.UserContainer<ServiceBuilder>()? Below is an example
internal sealed class Handler : IHandleMessages<Command>
{
public async Task Handle(Command message, IMessageHandlerContext context)
{
// Resolved from ASPNET DI
var useCase = context.GetService<CommandUseCase>();
// _useCase is resolved NSB DI since injected from constructor
Debug.Assert(ReferenceEquals(useCase, _useCase), "");
await useCase.Execute().ConfigureAwait(false);
}
}
This way I could inject to correct scoped application class in the handler constructor instead of resolving it from the scope provided by IServiceProvider that is made available from context.Extensions.Get<IServiceScope>().ServiceProvider.
Thanks for help
Regards
I think ASP.NET Core integration sample could be useful. Starting from version 7.2 sharing of the DI infrastructure between ASP.NET and NServiceBus is much simpler. There is also a specialized NServiceBus.Extensions.Hosting adapter package that adds UseNServiceBus API.

Autofac with self hosted WCF service and object disposal

I'm injecting a business layer into a self-hosted (InstanceContextMode=per-call) WCF service by leveraging AutoFac's ServiceHost.AddDependencyInjectionBehavior() extension (as described in the Autofac documentation)
My business layer uses components that cannot be re-created each time a new request comes in (let's say it needs a persistent database connection).
As such, when building the container I'd like to register the BL service as a Single instance, i.e.:
builder.RegisterType<BusinessLayer>()
.SingleInstance();
The business-layer injection into the WCF service is working fine; my issue is that:
Dispose() is not called on ANY services created in the container: not just the Business layer itself, but the 'persistent' services too.
I'd expect this to happen for the BL service itself. From the Autofac docs again:
If you have singleton components (registered as SingleInstance()) they will live for the life of the container. Since container lifetimes are usually the application lifetime, it means the component
won’t be disposed until the end of the application.
, but why are none of my 'child' (Autofac-registered) services (i.e the 'IPersistentService' below) disposed when the lifetime scope is -- given that I'm not explicitly making them 'SingleInstance'?
Note:: this is still the case if I manually dispose the business layer service within the lifetime scope after I close the ServiceHost
E.g. (IDisposable implementations omitted for brevity):
[ServiceContract]
public interface IMyService
{
void DoStuff();
}
public class MyService : IMyService
{
IBusinessLayer _bl;
public MyService(IBusinessLayer bl)
{
_bl = bl;
}
public void DoStuff()
{
_bl.BLDoStuff();
}
}
public interface IBusinessLayer
{
void BLDoStuff();
}
public class BusinessLayer : IBusinessLayer
{
IPersistentService _service;
public BusinessLayer(IPersistentService service)
{
_service = service;
}
public void BLDoStuff()
{
// Do something that requires a 'cached' / persistent component
_service.DoSomethingWithPersistentConnection();
}
}
public interface IPersistentService : IDisposable
{
void DoSomethingWithPersistentConnection();
}
With Autofac registrations looking something like:
builder.RegisterType<BusinessLayer>()
.SingleInstance();
builder.RegisterType<MyPersistentService>()
.As<IPersistentService>()
.OnActivated(e => e.Instance.Start());
As Steven mentioned, what you experience here is a Captive Dependency problem. In other words, a singleton (BusinessLayer, registered with .SingleInstance()) keeps a lifetime-scoped or transient dependency (MyPersistentService, registered by default as transient).
Put it this way, dependencies of singleton services will always be singletons themselves, no matter how they were registered in the container. The diagram in Mark Seeman's article that Steven linked to gives a good view of this.
I think you can achieve what you expect, but your registrations are wrong.
My business layer uses components that cannot be re-created each time a new request comes in (let's say it needs a persistent database connection).
As such, when building the container I'd like to register the BL service as a Single instance
This is where the problem is. It's the dependency of the business service that has to be registered as a singleton, not the business service itself. This means you could have Autofac create a different instance of BusinessLayer for each WCF call, but the MyPersistentService instance injected into would always be the same. Does this make sense? Your registrations would then look like:
builder
.RegisterType<BusinessLayer>()
.As<IBusinessLayer>()
.InstancePerLifetimeScope(); // a new instance per WCF call
builder
.RegisterType<MyPersistentService>()
.As<IPersistentService>()
.OnActivated(e => e.Instance.Start())
.SingleInstance(); // one same instance for the lifetime of the application
The one instance of MyPersistenService would then be disposed of only after disposing the root container (that you created by calling builder.Build()) after you close the Service Host.