I have a class with below constructor:
public MyClass(SomeRequiredService srs, string someRandomInput)
{
}
How can I register MyClass
with DI but with an option to pass a different someRandomInput
each time i need to get an instance of MyClass
?
or
With below constructor how can I get access to registered services or IServiceProvider
in MyClass
ctor while having a dynamic parameter in constructor?
public MyClass(string someRandomInput)
{
// how access a registered service here using DI?
}
How can I register MyClass with DI but with an option to pass someRandomInput each time i need to get an instance?
Assuming someRandomInput
only needs to be specified once, then you can do this in an implementationFactory:
in your service registration:
IConfiguration cfg = ...
//
services
.AddSingleton<MyClass>( implementationFactory: ( IServiceProvider sp ) =>
new MyClass(
srs : sp.GetRequiredService<SomeRequiredService>(),
someRandomInput: cfg.GetString("Foobars")
)
);
The same works for AddTransient
and AddScoped
- the catch being that they'll all use whatever value you pass into someRandomInput
in your factory - though your factory method itself could also use the IServiceProvider
to get another service as a source for those values.
More generalised, here's two approaches:
public interface IFoobarsServiceRequisite
{
String GetNextStringValue();
}
public class FoobarsService
{
public FoobarsService( SomeRequiredService srs, IFoobarsServiceRequisite requisiteData )
{
}
}
...then you'd register some implementation of that Requisite
service interface.
AbstractSingletonProxyFactoryBean
(In case anyone missed the reference)
services
.AddSingleton<SomeRequiredService>()
.AddSingleton<ISomeRandomStringSource,DefaultSomeRandomStringSource>()
.AddSingleton<IFoobarsServiceFactory,DefaultFoobarsServiceFactory>()
.AddTransient<FoobarsService>( sp => sp.GetRequiredService<IFoobarsServiceFactory>().CreateFoobar() );
//
public interface IFoobarsServiceFactory
{
FoobarsService CreateFoobar();
}
public class DefaultFoobarsServiceFactory : IFoobarsServiceFactory
{
private readonly SomeRequiredService srs;
private readonly ISomeRandomStringSource src;
public DefaultFoobarsServiceFactory(
SomeRequiredService srs,
ISomeRandomStringSource src
)
{
this.srs = srs;
this.src = src;
}
public FoobarsService CreateFoobar()
{
return new FoobarsService( this.srs, this.src.GetNextValue() );
}
}
public class FoobarsService
{
public FoobarsService( SomeRequiredService srs, String someRandomValue )
{
}
}
and it's factories all the way down...
I was surprised that MEDI still lacks a standardised IServiceFactory
interface to handle the awkward edge-cases that need more factory-logic than a simple sp =>
lambda, but which don't need to explicitly call their .CreateService()
methods in application code - especially as EF already has its own IDbContextFactory
type.
After clarifying things in the comments, I think the OP is after something like this:
FoobarServiceFactory
(or an interface
thereof) is a normal Singleton service which carries the FoobarService
dependencies - but also allows/requires the consumers of FoobarServiceFactory
to provide their own arguments for when they want to get an instance of FoobarService
, like so:services.AddSingleton<IFoobarServiceFactory,FoobarServiceFactory>()
//
public interface IFoobarServiceFactory // Note that this interface is entirely optional: you could just register `FoobarServiceFactory` as a service.
{
FoobarsService CreateFoobar( String callerProvidedData );
}
public class FoobarServiceFactory: IFoobarServiceFactory
{
private readonly SomeRequiredService srs;
public DefaultFoobarsServiceFactory( SomeRequiredService srs )
{
this.srs = srs;
}
public FoobarsService CreateFoobar( String callerProvidedData )
{
return new FoobarService( this.srs, callerProvidedData );
}
}
public class FoobarService
{
public FoobarService( SomeRequiredService srs, String someRandomValue )
{
// etc
}
}
Used like so:
public class SomeConsumer
{
private readonly IFoobarServiceFactory fbFactory;
public SomeConsumer( IFoobarServiceFactory fbFactory )
{
this.fbFactory = fbFactory;
}
public void DoSomething()
{
String someRandomString = Math.Random() > 0.5 ? "abc" : "0xdeadbeef";
FoobarService svc = this.fbFactory.Create( someRandomString );
// do stuff with `svc`
}
}
An alternative implementation of IFoobarServiceFactory
could use IServiceProvider
, if it isn't possible to retain a long-life'd (or unbound lifetime) for SomeRequiredService
(and IMO, this is the only acceptable reason to ever need IServiceProvider
as an actual dependency (other than breaking cycles in dependency graphs, ofc):
public class AltFoobarServiceFactory : IFoobarServiceFactory
{
private readonly IServiceProvider sp;
public AltFoobarServiceFactory( IServiceProvider sp )
{
this.sp = sp;
}
public FoobarsService CreateFoobar( String callerProvidedData )
{
// `ActivatorUtilities` is useful! https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.activatorutilities?view=dotnet-plat-ext-8.0
return ActivatorUtilities.CreateInstance<FoobarsService>( sp, callerProvidedData );
}
}