Perhaps I'm just using the wrong terms while searching, but I haven't found any solid guidance around how to do what I'm seeking to do.
All the guidance around DI registration follows something like the following:
builder.Services.AddSingleton<MyService>(() => new MyService("connectionString"));
But this seems too simple for me to use over here in the real world. I don't store my various credentials in my applications, but rather put them somewhere else like Azure Key Vault or authenticate using a managed identity that itself retrieves connection strings and keys.
This introduces the need then to access the credentials/connection string first, which increasingly is exposed only as an asynchronous operation and introduces the problem I regularly face: namely, asynchronous registration isn't a thing.
I could register a service that itself retrieves and exposes the credential in an async method, but now every downstream service is going to need to know about that method in order to utilize it - I can't simply abstract it away in a DI registration.
I could just use .Result or Wait(), but there's plenty of solid guidance that suggests this shouldn't be done for deadlocking reasons. Because this code may be intended for a library that's consumed by an app with a UI, that's a no-go.
So the question is: When I'm unable to synchronously provide my credentials, how do I register my services?
Real-world example
For example, let's say I've got a web app that needs to access Cosmos DB, but via a managed identity, following the instructions here. I need to store some information about the Cosmos DB instance which means a dependency on IConfiguration and I'd like to use a singleton HttpClient to retrieve the necessary keys.
I want to put this into a separate service responsible for setting up the Cosmos DB client so that downstream usages can just inject the CosmosClient, so my class looks like:
public class CosmosKeyService
{
private readonly MyCosmosOptions _cosmosOptions;
private readonly HttpClient _http;
public CosmosKeyService(IOptions<MyCosmosOptions> options, HttpClient http)
{
_cosmosOptions = options.Value;
_http = http;
}
private async Task<string> GetCosmosKey()
{
//Follow instructions at https://learn.microsoft.com/en-us/azure/cosmos-db/managed-identity-based-authentication#programmatically-access-the-azure-cosmos-db-keys
//...
var keys = await result.Content.ReadFromJsonAsync<CosmosKeys>();
return keys.PrimaryMasterKey;
}
public async Task<CosmosClient> GetCosmosClient()
{
var key = await GetCosmosKey();
return new CosmosClient(_cosmosOptions.CosmosDbEndpoint, key);
}
}
To support the DI used in this class, my registration then looks like:
builder.Services.Configure<MyCosmosOptions>(builder.Configuration.GetSection("cosmosdb"));
builder.Services.AddSingleton<HttpClient>();
And of course I'm going to need to register this service:
builder.Services.AddSingleton<CosmosKeyService>();
But now I'd also like to register the CosmosClient as created by the method in that service and this is where I start getting confused about the best way forward.
I can't retrieve an instance of the CosmosKeyService from the builder because I haven't yet built it, and after I do, I can't then register new services.
I can't use async methods in the registration itself or I could easily do something like:
builder.Services.AddSingleton<CosmosClient>(async services => {
var keyService = services.GetService<CosmosKeyService>();
return await keyService.GetCosmosClient();
});
...and downstream services could simply inject CosmosClient in their various constructors.
Again, any downstream consumer can just inject a CosmosKeyService, but now they're all going to have to "remember" to call the initialization method first so they can retrieve the CosmosClient and utilize it. I'd rather that be handled in registration so that 1) this initialization is hidden and centrally located and 2) the CosmosClient is truly a singleton and not just an artifact of every utilization.
I could create another intermediate service that injects this Key resolver service and retrieve the keys, but it too will need to have this async method that retrieves the keys since I can't just hide that initialization in a registration somewhere (for lack of async support).
For example, I could make another service:
public class CosmosBuilder
{
private readonly CosmosKeyService _keySvc;
public CosmosBuilder(CosmosKeyService keySvc)
{
_keySvc = keySvc;
}
public async Task<CosmosClient> GetCosmosClient()
{
return async _keySvc.GetCosmosClient();
}
}
But this ultimately still requires a downstream service to inject this service and call that initialization method and if that's necessary, I might as well just stick with injecting the CosmosKeyService and call the method there.
What I'd ideally like to see is some way to hide any async initialization in the registration so that downstream consumers can simply inject CosmosClient and it works, but it's beyond me how that's intended to happen. Can anyone shed some light on this?
Edit to address comment:
I don't want to comment on a 4-year old answer, but the issue I assert with the accepted answer boils down to this part:
Move [initialization] into the Composition Root. At that point, you can create an initialize those classes before registering them in the container and feed those initialized classes into the container as part of registrations.
That's all well and good except:
I only get to "build" my container a single time. I can't build it, then utilize the registrations to accomplish the initialization, then append still more registrations to it for later use.
In my example above, I explicitly utilize elements registered in DI by ASP.NET Core itself (namely IConfiguration), so there's simply no way to even access these except via DI (which, per #1, precludes me from being able to initialize and later supplement my registrations with more implementations).
Related
I'm building an application that performs actions initiated by a user and one particular class has dependencies on things I can wire up in DI such as an ILogger instance as well as an HttpClient in addition to runtime arguments that identify the user and the instance of the action (mostly to be used while logging to help with debugging).
The trouble I have is that I'm not entirely sure how to inject this class into the other classes that need it as a result of the runtime dependencies.
Here's a simplified example of one of my classes:
public class Dependency : IDependency
{
private readonly HttpClient httpClient;
private readonly ILogger<Dependency> logger;
private readonly RuntimeDeps runtimeDeps
public Dependency(
ILogger<Dependency> logger,
HttpClient httpClient,
RuntimeDeps runtimeDeps)
{
// set private fields
}
public Result DoStuff()
{
// use Http client to talk to external API
// something fails so log the failure and some helpful info
logger.log($"{runtimeDeps.InstanceId} failed. " +
"Initiated by {runtimeDeps.UserName}");
}
}
This feels like it requires a factory to create but then is it best to request the HttpClient and Logger in the factory method or declare it as a dependency of the factory? If the latter, I presume the factory would have to be registered as a transient or as a scoped resource since registering it as a singleton would result in a captive dependency (I think).
Any suggestions on redesigns are also welcome if this is a symptom of a poor design. I'd love to implement Mark Seeman's Pure DI to get some more assistance from the compiler but I don't know if that's possible in Azure functions.
A transient factory with the transient dependencies injected into the constructor and the runtime dependencies as parameters of the Create method will work fine.
DI is baked into the Azure Functions library in the sense that parameters are injected into the trigger methods, but beyond these you should be able to use Pure DI to manage your own dependencies by calling into some composition root helper class from the trigger function which knows how to build your dependency graph in a pure manner.
Instead of requiring runtime data during the construction of a component, it's better to let runtime data flow through method calls on an initialized object graph by either:
passing runtime data through method calls of the API or
retrieving runtime data from specific abstractions that allow resolving runtime data.
I formalized this in 2015 in this blog post, which I referred to in the comments.
After reading your additional comments, I came to the conclusion that in your case option 2 is most suited, as the data you are sending is likely an implementation detail to the component, and should not be part of the public API.
In that case, you can redesign your component as follows:
public class Dependency : IDependency
{
public Dependency(
ILogger<Dependency> logger,
HttpClient httpClient,
IRuntimeDepsProvider provider) ...
public Result DoStuff()
{
// use Http client to talk to external API
// something fails so log the failure and some helpful info
logger.log($"{provider.InstanceId} failed. " +
$"Initiated by {provider.UserName}");
}
}
IRuntimeDepsProvider is an abstraction that hides the retrieval and storage of runtime data. This gives you the ability to postpone the decision to either use a Closure Composition Model or an Ambient Composition Model until the Last Responsible Moment.
Using the IRuntimeDepsProvider abstraction, you can chose to set the incoming runtime values after the object graph is constructed. For instance:
public class MyFunction
{
// Notice the different abstraction here
public MyFunction(
IRuntimeDepsInitializer initializer,
IHandler<Something> handler) ...
public void TheFunction(Guid instanceId, string userName, Something cmd)
{
// Setting the runtime data *after* the object graph is constructed,
initializer.SetData(instanceId, userName);
// but before the graph's public methods are invoked.
handler.Handle(cmd);
}
}
Here, a second abstraction is introduced, namely IRuntimeDepsInitializer. Now you can have one class implementing both interfaces:
public class RuntimeDepsStorage : IRuntimeDepsInitializer, IRuntimeDepsProvider
{
public Guid InstanceId { get; private set; }
public string UserName { get; private set; }
public void SetData(Guid id, string name)
{
InstanceId = id;
UserName = name;
}
}
TIP: Instead of using two interfaces, you can also use only IRuntimeDepsProvider and let MyFunction depend on the concrete RuntimeDepsStorage. Which solution is best depends on the context.
Now the main trick here is to make sure that RuntimeDepsStorage becomes a Scoped dependency, because you want to reuse it throughout a request, but not shared by multiple requests.
When applying Pure DI, this would look like this:
var storage = new RuntimeDepsStorage();
new MyFuncion(
initializer: storage,
handler: new SomethingHandler(
stuffDoer: new Dependency(
httpClient: client, // Did you notice this is a runtime dep as well?
logger: new Logger<Dependency>(),
provider: storage)))
If, on the other hand, you would be using MS.DI as your DI Container, registration would be similar to the following:
services.AddScoped(_ => new RuntimeDepsStorage());
services.AddScoped<IRuntimeDepsProvider>(
c => c.GetRequiredService<RuntimeDepsStorage>());
services.AddScoped<IRuntimeDepsInitializer>(
c => c.GetRequiredService<RuntimeDepsStorage>());
// etc, your usual registrations here
I have an ASP.NET Core web app, with WebAPI controllers. All I am trying to do is, in some of the controllers, be able to kick off a process that would run in the background, but the controller should go ahead and return before that process is done. I don't want the consumers of the service to have to wait for this job to finish.
I have seen all of the posts about IHostedService and BackgroundService, but none of them seem to be what I want. Also, all these examples show you how to set things up, but not how to actually call it, or I am not understanding some of it.
I tried these, but when you register an IHostedService in Startup, it runs immediately at that point in time. This is not what I want. I don't want to run the task at startup, I want to be able to call it from a controller when it needs to. Also, I may have several different ones, so just registering services.AddHostedService() won't work because I might have a MyServiceB and MyServiceC, so how do I get the right one from the controller (I can't just inject IHostedService)?
Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?
You have the following options:
IHostedService classes can be long running methods that run in the background for the lifetime of your app. In order to make them to handle some sort of background task, you need to implement some sort of "global" queue system in your app for the controllers to store the data/events. This queue system can be as simple as a Singleton class with a ConcurrentQueue that you pass in to your controller, or something like an IDistributedCache or more complex external pub/sub systems. Then you can just poll the queue in your IHostedService and run certain operations based on it. Here is a microsoft example of IHostedService implementation for handling queues https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background-tasks
Note that the Singleton class approach can cause issues in multi-server environments.
Example implementation of the Singleton approach can be like:
// Needs to be registered as a Singleton in your Startup.cs
public class BackgroundJobs {
public ConcurrentQueue<string> BackgroundTasks {get; set;} = new ConcurrentQueue<string>();
}
public class MyController : ControllerBase{
private readonly BackgroundJobs _backgroundJobs;
public MyController(BackgroundJobs backgroundJobs) {
_backgroundJobs = backgroundJobs;
}
public async Task<ActionResult> FireAndForgetEndPoint(){
_backgroundJobs.BackgroundTasks.Enqueue("SomeJobIdentifier");
}
}
public class MyBackgroundService : IHostedService {
private readonly BackgroundJobs _backgroundJobs;
public MyBackgroundService(BackgroundJobs backgroundJobs)
{
_backgroundJobs = backgroundJobs;
}
public void StartAsync(CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
if(_backgroundJobs.BackgroundTasks.TryDequeue(out var jobId))
{
// Code to do long running operation
}
Task.Delay(TimeSpan.FromSeconds(1)); // You really don't want an infinite loop here without having any sort of delays.
}
}
}
Create a method that returns a Task, pass in a IServiceProvider to that method and create a new Scope in there to make sure ASP.NET would not kill the task when the controller Action completes. Something like
IServiceProvider _serviceProvider;
public async Task<ActionResult> FireAndForgetEndPoint()
{
// Do stuff
_ = FireAndForgetOperation(_serviceProvider);
Return Ok();
}
public async Task FireAndForgetOperation(IServiceProvider serviceProvider)
{
using (var scope = _serviceProvider.CreateScope()){
await Task.Delay(1000);
//... Long running tasks
}
}
Update: Here is the Microsoft example of doing something similar: https://learn.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1#do-not-capture-services-injected-into-the-controllers-on-background-threads
As I understand from your question you want to create a fire and forget task like logging to database. In this scenario you don't have to wait for log to be inserted database. It also took much of my time to discover an easily implementable solution. Here is what I have found:
In your controller parameters, add IServiceScopeFactory. This will not effect the request body or header. After that create a scope and call your service over it.
[HttpPost]
public IActionResult MoveRecordingToStorage([FromBody] StreamingRequestModel req, [FromServices] IServiceScopeFactory serviceScopeFactory)
{
// Move record to Azure storage in the background
Task.Run(async () =>
{
try
{
using var scope = serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<ICloudStorage>();
await repository.UploadFileToAzure(req.RecordedPath, key, req.Id, req.RecordCode);
}
catch(Exception e)
{
Console.WriteLine(e);
}
});
return Ok("In progress..");
}
After posting your request, you will immediately receive In Progress.. text but your task will run in the background.
One more thing, If you don't create your task in this way and try to call database operations you will receive an error like this which means your database object is already dead and you are trying to access it;
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DBContext'.
My code is based on Repository pattern. You should not forget to inject service class in your Startup.cs
services.AddScoped<ICloudStorage, AzureCloudStorage>();
Find the detailed documentation here.
What is the simplest way to run a single background task from a controller in .NET Core?
I don't want the consumers of the service to have to wait for this job to finish.
Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?
The problem is that ASP.NET is a framework for writing web services, which are applications that respond to requests. But as soon as your code says "I don't want the consumers of the service to have to wait", then you're talking about running code outside of a request (i.e., request-extrinsic code). This is why all solutions are complex: your code has to bypass/extend the framework itself in an attempt to force it to do something it wasn't designed to do.
The only proper solution for request-extrinsic code is to have a durable queue with a separate background process. Anything in-process (e.g., ConcurrentQueue with an IHostedService) will have reliability problems; in particular, those solutions will occasionally lose work.
I've been using a trick for a while to help with maintaining an audit trail. In or before the controller, I create a User which is bound in some way to the request. I can use DI to create most of my application as singletons and I can just inject a Func<User> wherever I think I need User information. I get the per-request User from the Func and can easily add audit information to everything.
This keeps my domain classes User agnostic and lets my DI container act as a User management system.
Now I'm using asp.net 5 and I'm having trouble doing the same thing. Honestly I've never been sure I should be able to do this, but I've gotten used to it.
I'm trying to do something like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddScoped<IUser, User>();
services.AddSingleton<IDependantOnUser, DependantOnUser>
services.AddScoped<Func<IUser>(c => c.GetRequiredService<IUser>);
}
Then in or before my controller I create and populate the user instance.
public class ValuesController : Controller
{
public ValuesController(Func<User> userFunc)
{
user = userFunc();
// hydrate user instance as needed
}
}
Then finally, I should have access to the user instance in my singleton object.
public class DependantOnUser : IDependantOnUser
{
public DependantOnUser(Func<User> userFunc)
{
user = userFunc();
// I want this to be the same instance as that generated by the controller
}
}
But I can't get this to work. Before asp.net 5, I've been using Autofac to achieve this, but haven't had any luck there. I've tried playing around with transient/scoped/singleton a bit with no luck. I've even tried resolving my own IServiceProvider and using it directly instead of just generating a user with c => c.GetRequiredService<IUser>
Everything I do seems to be working with the wrong IServiceProvider instance. Is there a way resolve an instance from a different ServiceProvider? Any other suggestions would also be helpful.
Before you suggest I just register everything using AddScoped(), some of the objects between my presentation and persistence layers work a lot better as singletons.
Also I would prefer not to just pass User information as a parameter to every method in my domain (we record it with nearly every CRUD operation and pass it with most external calls we make)
I believe that it is antipattern to inject scope depedency to singleton one, please refer to Captive Dependencies
Autofac Captive Dependencies
Is there a way to define a scope for a specific lifestyle? I am attempting to implement my own scope that I want to persist across an application, but internally I also create another scope, and then a request to GetInstance returns the inner scoped instance instead.
I thought if I could define my lifestyle as:
public class MyScopedLifestyle : ExecutionContextScopeLifestyle
{
public MyScopedLifestyle(bool disposeInstanceWhenScopeEnds)
: base("MyScopedLifestyle", disposeInstanceWhenScopeEnds)
{
}
protected override int Length
{
get
{
return 100;
}
}
}
And my usage is:
var container = new Container();
container.Register<IRequestData, RequestData>(new MyScopedLifestyle());
// i had hoped I could say
// container.BeginExecutionContextScope(MyScopedLifestyle)
// or something similar
// this is controlled by me
using (var scope1 = container.BeginExecutionContextScope())
{
// do some stuff
container.GetInstance<IRequestData>().RequestMarket = "en-US";
// this is done via the webapi execution scope (using simpleinjector dependency resolver)
using (var scope2 = container.BeginExecutionContextScope())
{
Assert.Equal("en-US", container.GetInstance<IRequestData>().RequestMarket); // false
}
}
But I'm unsure how to utilize my custom lifestyle when creating the inner execution scope.
What I really want to happen, is that my instance of IRequestData used in scope1, is the same instance of IRequestData inside of scope2. Is this something I can achieve with SimpleInjector?
Edit
I removed the fact that I'm attempting to create an instance of an object per OWIN request, rather than per WebAPI request. Ideally I'm attempting to create:
container.RegisterOwinRequest<IRequestData, RequestData>();
So that when I resolve IFoo anywhere within my pipeline (be it an OWIN middleware, or in the WebAPI part, the same instance is returned for a particular request).
Edit 2
Swapped our IFoo/Foo/MyProperty for better names.
What you're trying to accomplish with a custom lifestyle is absolutely possible, but might not be that easy, because you will have to store that scope somewhere (probably in the CallContext) and need to create a BeginMyCustomScope method that creates a new scope and have a custom Scope implementation that removes itself from the CallContext when Dispose is called. I think this is too much work and too much complexity.
The problem exists because during the time you want to set the RequestMarket property, there is no Web API request scope started. The way to usually force such scope to be started is to call the GetDependencyScope method on the HttpRequestMessage:
message.GetDependencyScope();
Right after that you can resolve the IRequestData and it works as expected:
container.GetInstance<IRequestData>().RequestMarket = "en-US";
I'm unsure however, whether the HttpRequestMessage is available at that point in time, so if not, I think working with a DelegatingHandler as you expressed in the comments is a good alternative.
In the past, a good way to communicate data across the callstack was using thread-specific variables, but that obviously fails when using async/await models as with Web API and OWIN. So the new way to do this is using the CallContext. So instead of using a DelegatingHandler, you might be able to do something like this:
container.RegisterInitializer<IRequestData>(data =>
data.RequestMarket = (string)CallContext.LogicalGetData("RequestMarketKey"));
And when the OWIN request starts, you do this:
CallContext.LogicalSetData("RequestMarketKey", "en-US");
I am using a framework that allow me to intercept in some entry points.
The framework scope is not Web Request, not thread and not transient, its something like thread based, but i saw a lot of places with thread reuse.
So i need a custom scope, where i say where to start and where to end the scope.
Since I have a lot of dependencies, most of them are defined in static contractor, because they are stateless.
I have one dependency that actually need to be injected on every framework interception.
This is the interception method, and how I do the injection (I am not calling this method, the framework does). So what i need here is to inject the AppContext and make sure that Castle always resolve me the correct context (within the scope)
public void Execute(AppContext context)
{
using (var s = CastleContainer.Container.BeginScope())
{
CastleContainer.Container.Register(Component.For<AppContext>().LifestyleScoped().Instance(context));
var logic = CastleContainer.Container.Resolve<ICustomerLogic>();
// begin invocation
}
}
ICustomerLogic has dependency in ICustomreDal and ICustomreDal has dependency in AppContext.
So when I resolve Resolve<ICustomerLogic>() I want to be sure that ICustomreDal has the current AppContext.
ICustomerLogic and registered as singleton, and ICustomreDal registered as transient.
The first execution works fine, the second execution I get the error:
AppContext could not be registered. There is already a component with
that name. Did you want to modify the existing component instead? If
not, make sure you specify a unique name.
Isn't castle suppose to do the scope segmentation so each scope has its own dependencies?
What am I doing wrong?
Please note that we are talking about 50 executions in a second.
The BeginScope is not about registration, it is only about component resolving. It will make sure that any component that is created within the using statment, with lifestyle Scoped gets released (disposed if necessary) when the using statements end. It does not unregister components that are registered in the block. In general it is a bad idea to register your components in multiple places. Only register components at the startup of your application.
I've been struggling a lot with something similair and finally used this workaround with I was not totally happy with but if there is anyone with a better solution I would love to hear. Adapted to your situation it would look something like this:
in your registration code use:
Component.For<ICustomerLogic>().ImplementedBy<CustomerLogic>().LifestyleScoped
Component.For<AppContext >().UsingFactoryMethod(() => (AppContext)Thread.GetNamedDataSlot("context")).LifestyleTransient() // or scoped
adapt your Execute function to:
public void Execute(AppContext context)
{
using (var s = CastleContainer.Container.BeginScope())
{
Thread.SetData(Thread.GetNamedDataSlot("context"), context);
var logic = CastleContainer.Container.Resolve<ICustomerLogic>();
Thread.SetData(Thread.GetNamedDataSlot("context"), null);
// begin invocation
}
}
Goodluck,
Marwijn.