Using Hangfire, I want to dynamically enqueue the jobs to different queues, using some criteria.
So far we are not using queues and do something like this
BackgroundJob.Enqueue<SimpleUploadWorkflow>(j=> j.DoJob())
In the documentation I've seen that I can use the following code to set the queue programmatically
var client = new BackgroundJobClient();
var state = new EnqueuedState("critical"); // Use the "critical" queue
client.Create(() => Console.WriteLine("Hello!"), state);
My question is should I use the client like this?
If I understand correctly method BackgroundJob.Enqueue creates the client as a singleton, so it feels wrong to create a new BackgroundJobClient everytime.
What is the recommended way to implement something like this?
I know I can use the [Queue] attribute, but I want my queues to be taken from the configuration.
If you don't want to create BackgroundJobClient instance everytime, you can DI it using IBackgroundJobClient either through ctor:
private readonly IBackgroundJobClient _backgroundJobClient;
public Service(IBackgroundJobClient client)
{
_backgroundJobClient = backgroundJobClient;
}
Or if you want scoped lifetime, you can inject IServiceScopeFactory in ctor and then in your method you can resolve IBackgroundJobClient in a following way:
using var scope = _serviceScopeFactory.CreateScope();
var backgroundJobClient = scope.ServiceProvider.GetRequiredService<IBackgroundJobClient>();
And regarding queue name, you could inject IOptions or IOptionsMonitor to retrieve it from your appsettings/secrets. Here's link to Options pattern in ASP.NET Core
Related
Ive been trying to upgrade to latest Autofac/Multitenant/Dependency injection.
Release 8.0.0 states that there is a breaking change:
https://github.com/autofac/Autofac.Extensions.DependencyInjection/releases/tag/v8.0.0
IServiceScopeFactory is now a singleton and child scopes are flat, not hierarchical.
Right now my Controllers fail when they try to resolve a dependency which is inside the "Parent lifetime scope", as the parent lifetime scope is just "root" now.
Previously the "Parent lifetime scope" was a "tenant lifetime" (as im using multitenant package) and then it worked.
I just cannot figure out how/where to apply the below "fix" in ASP.NET Core and have hierachical scopes as in previous versions.
I have copied the samples from here:
https://github.com/autofac/Autofac.AspNetCore.Multitenant/tree/develop/samples/Sandbox.AspNetCore5_To_6
and it works as expected (scopes are flat and resolved from root (not tenant-scope))
Where do I put this code to control the created scopes etc. ?
// Based on an IServiceProvider...
IServiceProvider provider = CreateServiceProvider();
// You'll need to get an Autofac lifetime scope.
var autofacScope = provider.GetService<ILifetimeScope>();
// Use the Autofac constructs to create hierarchical lifetimes.
var unitOfWorkOutside = autofacScope.BeginLifetimeScope();
// And later have a sub-unit-of-work scope inside that...
var unitOfWorkInside = unitOfWorkOutside.BeginLifetimeScope();
// Now they're related so they'll share a hierarchy. If you dispose the outer scope...
unitOfWorkOutside.Dispose();
// ...stuff in the inner scope will not resolve because you disposed its parent.
unitOfWorkInside.Resolve<MyService>();
If you need a hierarchical lifetime scope now, you need to get the current request scope and manually create it. This means either resolving ILifetimeScope from an IServiceProvider or injecting ILifetimeScope into your controller and resolve things using service location.
Here's getting it from the service provider:
public class MyController
{
public IActionResult DoWork()
{
var requestScope = this.Context.RequestServices.GetService<ILifetimeScope>();
using var unitOfWorkScope = this._requestScope.BeginLifetimeScope();
var thing = unitOfWorkScope.Resolve<Thing>();
return thing.DoWork();
}
}
Here's injecting ILifetimeScope into the controller:
public class MyController
{
private ILifetimeScope _requestScope;
public MyController(ILifetimeScope requestScope)
{
this._requestScope = requestScope;
}
public IActionResult DoWork()
{
using var unitOfWorkScope = this._requestScope.BeginLifetimeScope();
var thing = unitOfWorkScope.Resolve<Thing>();
return thing.DoWork();
}
}
In both cases, the request lifetime scope should be a child of the tenant scope. The only way to make a child of that child is to use Autofac directly.
Thanks for the reply.
I actually think my problem is/was elsewhere, but trying to figure it out :)
Apparently my current code is outdated, i believe.
I can see the code has followed an other "guide" from you (from 2017):
Autofac.Multitenant in an aspnet core application does not seem to resolve tenant scoped dependencies correctly
This means that my current code does not use:
public void ConfigureServices(IServiceCollection services)
services.AddAutofacMultitenantRequestServices()
or
.UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(MultitenantContainerSetup.ConfigureMultitenantCont
but instead follow the guide from 2017 with the "RequestMiddleware" etc.
services.Insert(0, new ServiceDescriptor(typeof(IStartupFilter), typeof(RequestStartupFilter), ServiceLifetime.Transient));
When following the code from the samples:
https://github.com/autofac/Autofac.AspNetCore.Multitenant/tree/develop/samples/Sandbox.AspNetCore5_To_6
Then the AutofacMultitenantServiceProviderFactory has code like:
containerBuilder.Register(componentContext =>
{
var scope = componentContext.Resolve<MultitenantContainer>().GetCurrentTenantScope();
var autofacChildLifetimeScopeServiceProviderFactory =
new AutofacChildLifetimeScopeServiceProviderFactory(scope);
var adapter =
autofacChildLifetimeScopeServiceProviderFactory.CreateBuilder(new ServiceCollection());
var serviceProvider =
autofacChildLifetimeScopeServiceProviderFactory.CreateServiceProvider(adapter);
var factory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
return new MultitenantServiceScopeFactoryAdapter(factory);
})
.InstancePerTenant();
Maybe its this code that solves the tenant-scope issue?
Because if I just upgrade my application to use the newest packages, then ParentLifetimeScope will always be "root", but if using the samples then ParentLifetimeScope is "tenantLifetime" and then my "PerTenant" registrations can be resolved from ParentLifetimeScope
Does any of this makes sense? :)
Obviously I want to re-write my current code to use the new .NET 6 samples, but I too want to understand what the difference is
I've started to implement health checks in my .NET Core Web API. There are two health checks, one for checking if the SQL Server has any pending migrations and the other is checking if another API is live. Both added within ConfigureServices in Startup class.
In order to do the migration check, I need to access the DbContext which has already been added to DI using AddDbContext and to check the API, I need to get the API base url from configuration which is already in DI using services.Configure<>. I use the following code to get access to the DbContext.
I'm using AspNetCore.HealthChecks.Uris package to use AddUrlGroup health check.
var sp = services.BuildServiceProvider();
var dbContext = sp.GetService<AppDbContext>();
var apis = sp.GetService<IOptions<InternalServicesConfiguration>>().Value;
services.AddHealthChecks().AddCheck("Database", new SqlDatabaseHealthCheck(dbContext), tags: new[] { "ready" })
.AddUrlGroup(new Uri(new Uri(apis.Api1BaseUri), "/health/live"), HttpMethod.Get, "API 1", HealthStatus.UnHealthy, new []{"ready"});
But services.BuildServiceProvider() shows the following warning:
Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'
I can get the api base urls using
_configuration.GetSection("InternalServicesConfiguration").Get(typeof(InternalServicesConfiguration));
But I can't think of an alternative way to access the DbContext.
Any help much appreciated.
You can register your healthcheck like this:
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("Database");
And then just inject your DbContext into ExampleHealthCheck class, which has to implement IHealthCheck interface
There are some healthchecks you can use directly for EF in the official docs
But if you want to write any custom or more complex checks, your best bet might be to create a class that implements the IHealthCheck interface, where you can inject anything you want.
Also from the docs about Custom health checks, an example:
public class ExampleHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var healthCheckResultHealthy = true;
if (healthCheckResultHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
HealthCheckResult.Unhealthy("An unhealthy result."));
}
}
which, as kebek alerady answered, you will register like
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("example_health_check");
With regard to accessing IOptions (please see updated health check). Is there another way other than getting it using _configuration.GetSection("").Get<>()
You could register those options in a following way (in ConfigureServices):
services.Configure<InternalServicesConfiguration>(Configuration.GetSection("InternalServicesConfiguration"));
And to get those options, in your class just inject IOptions<InternalServicesConfiguration> options, where the options.Value prop is the configuration value
I am using scoped service called IOperationContextProvider to hold some information about my current execution context (called OperationContext).
Whenever I start a new execution path (not only HTTP request, but some async impulses such as queue message, change feed change..), I create a dedicated DI service scope.
Any class can inject the provider and has access to this context (such as correlation ID).
For outgoing requests, I would like to configure to add the correlation ID to outgoing HTTP header, like this:
services.AddHttpClient<IMyClass, MyClass>((serviceProvider, httpClient) =>
{
var contextProvider = serviceProvider.GetRequiredService<IOperationContextProvider>();
var corrId = contextProvider.Context.CorrelationId;
httpClient.DefaultRequestHeaders.Add("x-corr-id", corrId);
});
However, I am unable to do this, because IHttpClientFactory creates scope for each handler it is creating and my context is not reachable from inside the HTTP client configuration. Same goes for adding HTTP message handlers, they are created in the same scope as the handler too.
Official documentation:
The IHttpClientFactory creates a separate DI scope for each handler. Handlers are free to depend upon services of any scope.
Is there any way to reach the same scope as in which the HttpClient itself is being built?
I only have found a way to where for the MyClass, where I also inject HttpClient, I inject the IOperationContextProvider too and configure manually the HttpClient but that is a bit cumbersome because it needs to be done everywhere:
public MyClass(HttpClient httpClient, IOperationContextProvider contextProvider)
{
var corrId = contextProvider.Context.CorrelationId;
httpClient.DefaultRequestHeaders.Add("x-corr-id", corrId);
this._httpClient = httpClient;
}
If you absolutely don’t want the HttpClientFactory to create a service scope, then you can disable this behavior through the HttpClientFactoryOptions.SuppressHandlerScope property. There isn’t a nice API to configure this though, so you will have to do something like this:
var httpClientBuilder = services.AddHttpClient<IMyClass, MyClass>(…);
services.Configure<HttpClientFactoryOptions>(httpClientBuilder.Name, options =>
{
options.SuppressHandlerScope = true;
});
Alternatively, you could also create the delegating handler directly, without going through DI:
services.AddHttpClient<IMyClass, MyClass>(…)
.AddHttpMessageHandler(sp =>
{
var contextProvider = sp.GetService<IOperationContextProvider>()
return new MyHandlerWithoutDI(contextProvider);
});
One of the things thats also suggested is that the shared settings like the defaultrequestheaders be properly setup to avoid race conditions, if you are planning to use this client as a shared resource. This is in reference to your initial proposed workaround.
public MyClass(HttpClient httpClient, IOperationContextProvider
contextProvider)
{
var corrId = contextProvider.Context.CorrelationId;
httpClient.DefaultRequestHeaders.Add("x-corr-id", corrId);
this._httpClient = httpClient;
}
https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/
Context:
I am using DI in my Web application. (I am using NInject, but hopefully this should not matter)
Some places constructor injection is not possible, for example in my custom log4net database logger (that's not me, who instantiates my custom logger instead the log4net framework). So I am using my DI container there in service locator DP mode, and asking an instance resolve explicitly in the logger code.
Note this is just a sample, in many other cases I had to use NInject as service locator DP instead of constructor injection.
Now the problem:
I have an IAuditContextProvider which serves current request's audit data, like IP etc. The question arises how I configure my DI container to instantiate a concrete provider. So far I've used a request scope (singleton by request) what is supported out of box by NInject.
However recently I faced the fact I had to start a background processing initiated by a request. This is done by
// This is 'outside' it's actually a request serving method running in the request context, btw it is an MVC action method,
// Pseudo code:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// I would like to get the very same resolved auditProvider instance here as outside.
// Please note: outer local variables are not solution, because of implicit calls here inside, for example if there is a logging statement here, then the service locator in the custom logger must resolve the very same instance as outside
// Some how I must 'stamp' this thread to be the 'same' as the outside
// request thread in point of view of my custom scope resolver (see below)
}
Note: Configuring the DI container a wide scoped singleton are not solution because of multiple requests are server parallel, and they can not use a common auditProvider.
OK, I thought this is what for custom (resolving) scopes are for. Here is the pseudo code how I am configuring my DI container:
kernel
.Bind(typeof(IAuditContextProvider))
.To(typeof(WebAuditContextProvider)).InScope(dummy =>
{
// Here I have to return a very same object/number/id when in
// 'outside' the request thread, and inside the worker thread.
// This way I hopefully get the very same instance when resolving.
// To be short: I have no idea how?
});
I don't think there is a good answer for your question within the current bounds.
I do have an alternative suggestion - just perform the work synchronously in another process. This would require a form of inter-process communication (IPC) but shouldn't be too difficult.
A simple but effective form of IPC is just writing a record to a database table (acting like a queue) and having a windows service/daemon polling for new records to "process". In this example, you would put a record in the table with the contextual information (user id, etc) and the service would utilize this context to perform the work synchronously, but the workflow would be asynchronous to the Web UI.
This also has a nice side benefit: You can start to build monitoring, retry logic, etc into the service. These things are much harder to do reliably within an ASP.NET model.
You could forgo the database queue completely by using something like message queues/buses/events, but the basic concept is the same.
Update:
Did you try to use closures in C#? Like this:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// with closure you'll get that very variable you need:
auditProvider.SomeMethod();
}
You should read whole article about closures by John Skeet and how they can help you together with TPL.
Other useful information:
Such DI is being called as Ambient Context in famous book Dependency Injection by M. Seeman:
A truly universal CROSS-CUTTING CONCERN can potentially pollute a large part of the API for an application if you have to pass an instance around to every collaborator. An alternative is to define a context that’s available to anyone who needs it and that can be ignored by everyone else.
The AMBIENT CONTEXT is available to any consumer via a static property
or method. A consuming class might use it like this:
public string GetMessage() { return SomeContext.Current.SomeValue; }
In this case, the context has a static Current property that a consumer can access. This property may be truly static, or may be associated with the currently executing thread. To be useful in DI scenarios, the context itself must be an ABSTRACTION and it must be possible to modify the context from the outside—in the previous example, this means that the Current property must be writable. The context itself might be implemented as shown in the following listing.
The context is an abstract class, which allows us to replace one context with another implementation at runtime.
public abstract class SomeContext
{
public static SomeContext Current
{
get
{
// Get current context from TLS
var ctx = Thread.GetData(Thread.GetNamedDataSlot("SomeContext")) as SomeContext;
if (ctx == null)
{
ctx = SomeContext.Default;
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), ctx);
}
return ctx;
}
set
{
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), value);
}
}
public static SomeContext Default = new DefaultContext();
public abstract string SomeValue { get; }
}
TLS here stands for Thread Local Storage, which can be useful idea for you here.
Also I suggest you to read about OperationContext class, which can be helpful for you if you want to pass some context for your Task, something like this:
// save current context before task start
var operationContext = OperationContext.Current;
Task.Run(() =>
{
// set current operation context inside your Task with closure
OperationContext.Current = operationContext;
// Your work here
}
I am trying to set up my AutoFac registration in such a way that this test passes:
[Test]
public void Autofac_registration_test()
{
// Given
var builder = new ContainerBuilder();
RegisterServices(builder);
var container = builder.Build();
// When
var firstHub = container.Resolve<Hub>();
var secondHub = container.Resolve<Hub>();
// Then
firstHub.Should().NotBe(secondHub);
firstHub.FooRepo.Context.Should().Be(firstHub.BarRepo.Context);
firstHub.FooRepo.Context.Should().NotBe(secondHub.FooRepo.Context);
}
i.e. I want to use the same Context object all the way down within a single Hub, but use a different one when a new Hub is created.
RegisterServices is currently just:
private void RegisterServices(ContainerBuilder builder)
{
builder.RegisterType<MyHub>();
builder.RegisterType<FooRepo>();
builder.RegisterType<BarRepo>();
builder.RegisterType<Context>(); // How should I scope this?
}
Which fails at firstHub.FooRepo.Context.Should().Be(firstHub.BarRepo.Context); because Context is transiently scoped.
But scoping context per lifetime also fails, this time at firstHub.FooRepo.Context.Should().NotBe(secondHub.FooRepo.Context);.
It feels like this is a reasonable thing to want to do, so am I missing anything obvious out-of-the-box here?
Or will I have to do something manual to track Hub creation?
(For context, this is for a SignalR app. Hubs are created per SignalR request, so this was an attempt to match the unit-of-work lifetime of an HTTP request in normal webby situations).
What #Steven said in his comment was correct, I needed a per-object-graph lifestyle.
Castle.Windsor supports this, so I swicthed to using that for my dependency injection instead of AutoFac. The registration now looks like:
container.Register(Component.For<Hub>().LifestyleTransient());
container.Register(Component.For<FooRepo>().LifestyleTransient());
container.Register(Component.For<BarRepo>().LifestyleTransient());
container.Register(Component.For<Context>().LifestyleBoundTo<Hub>()); // Important bit
For more information, see: http://docs.castleproject.org/Windsor.LifeStyles.ashx?HL=scope#Bound_8