Cleaner way to choose LifetimeScope in a dependency chain with Autofac - c#

I'm using a web app with Autofac injecting services into controllers. Those services are sometimes injected with other services, and repositories. Repositories are injected with DbContexts. These 3 layers (service, repository, context) are all registered with Autofac. My default lifetime for these is InstancePerLifetimeScope.
Unfortunately, I have some code in a specific controller that I want to execute in parallel threads. Since DbContext is not thread-safe, this means I need to give a factory method to each thread to resolve a Service in a per dependency lifetime scope, which in turn will need to resolve per dependency repositories and db contexts.
The options I am considering are to create a new lifetime scope per thread, or to use a separate registration using a named or keyed registration to resolve the per-dependency services.
The challenge with creating a new lifetime scope per thread is that I need access to some per-scope objects. Some objects would need to be inherited and to not have a new instance created in the new scope, but other objects (the non-thread-safe DbContexts) need to have new instances generated in the new scope. I have no idea how to control this behavior implicitly when creating my new lifetime scope.
The other method would be to use a registration key so that when I execute the factory method to resolve a service on each thread, it would resolve one in the per-dependency scope. This would work if the service had no dependencies, but since it depends on a bunch of repositories or services for which the default lifetime scope is set to InstancePerLifetimeScope, I have to write something like this:
builder.RegisterType<MyService>()
.As<IMyService>()
.Named<IMyService>(RegistrationKeys.PerDependency)
.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IMyRepository),
(pi, ctx) => ctx.ResolveNamed<IMyRepository>(RegistrationKeys.PerDependency))
).InstancePerDependency();
Since the repositories depend on the DbContext, each repository has to be registered separately using this registration name. And it needs to be configured to resolve the DbContext using the registration name. And the DbContext needs to be registered using the registration name.
With 10 services each using about 4-5 repositories, I wager the amount of boilerplate registration code I will have to write will be around 10-20 full pages. It's not going to be maintainable.
So my question is, is there a way to create a specific type of lifetime scope that will allow me to easily control which objects will have a new instance or which will be inherited from the parent lifetime scope that won't break the asp.net per-request lifetime scope?
Or is there a way I can register or resolve a service to explicitly resolve all of its dependencies in the same scope without relying on their default registrations and without having to hard code an entire second set of registrations for everything?

The challenge with creating a new lifetime scope per thread is that I need access to some per-scope objects. Some objects would need to be inherited and to not have a new instance created in the new scope, but other objects (the non-thread-safe DbContexts) need to have new instances generated in the new scope. I have no idea how to control this behavior implicitly when creating my new lifetime scope.
This is the challenge InstancePerRequest solve. You can create child scope and object scoped to Request will be shared amongst child scope. To do this, tagged lifetimescope and InstancePerMatchingLifetimeScope is used.
You can see InstancePerRequest and Tagging a lifetime scope in the official documentation.
Example :
builder.RegisterType<Service>().As<IService>().InstancePerMatchingLifetimeScope("KEY");
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
// ...
using (ILifetimeScope scope = container.BeginLifetimeScope("KEY"))
{
scope.Resolve<IService>(); // Instance #1
using (ILifetimeScope childScope = scope.BeginLifetimeScope())
{
childScope.Resolve<DbContext>();
childScope.Resolve<IService>(); // shared instance (#1)
}
}
but that's mean you have to change all your InstancePerLifetimeScope to InstancePerMatchingLifetimeScope and can control the creation of the unit of work lifetime scope which can be quite difficult.
Another way of doing this is by using Owned<T> with Func<T>. You can get more information here : Owned instance
builder.RegisterType<Service>().As<IService>().InstancePerLifetimeScope();
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
builder.RegisterType<Operation>().As<IOperation>().InstancePerLifetimeScope();
public class Operation : IOperation
{
public Operation(Func<Owned<DbContext>> contextFactory, IService service)
{
this._contextFactory = contextFactory;
this._service = service;
}
private readonly Func<Owned<DbContext>> _contextFactory;
private readonly IService _service;
public void Do()
{
using Owned<DbContext> context = this._contextFactory();
context.Value // => new instance
this._service // shared instance (#1)
}
}
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<IService>(); // Instance #1
IEnumerable<IOperation> operations = scope.Resolve<IEnumerable<IOperation>>();
operations.AsParallel()
.ForAll(operation => operation.Do());
}
The only downside of this solution is that your service will have dependency on Autofac but if you don't want it, it is quite easy to create your own abstraction over Owned
If you don't want to use Owned<T> or your own abstraction instead of trying to make DbContext a special case you can reverse the problem and manually share some dependency between your custom scope.
Something like :
using ILifetimeScope childScope = scope.BeginLifetimeScope(b => {
b.Register<XContext>(c => scope.Resolve<XContext>()).ExternallyOwned();
});
var operation = childScope.Resolve<IOperation>();
operation.Do();
This way IOperation would be resolved in a new scope but XContext will be from parent scope

Related

Dependency injection using both AddSingleton and AddTransient C#

I'm having a huge problem with the configuration/dependency injection of an application.
I have a singleton class added through DI with AddSingleton, that has in its constructor a IRequestClient, that is scoped because
busConfigurator.AddRequestClient()
which among other things, has the same effect as AddScoped.
When I start the app, it says
"Cannot consume scoped service 'MassTransit.IRequestClient`1[...]' from singleton '...'.)"
Which absolutely makes sense.
The weirdest thing is that I have another app set up the exact same way, but it just works and I would really like for that class to remain singleton.
My colleague and I spent an entire day trying to find the differences between the two applications, but they are virtually the same in their configurations, so we are having trouble in understanding why one works while the other doesn't.
I'm not entirely sure on what details could be important to better define the problem, so feel free to ask.
We've looked all around the internet trying to find a solution, but it was always "Change singleton to transient", but that's not an option, first because it HAS to be a singleton, otherwise it wouldn't make sense in our app, as that thing is what caches lots of date from our db so we can't just go around keeping on collecting heaps of data, second because the first app works with singleton, not with transient and we'd like to keep it that way
// This method is called in Main()
private static void ConfigureMassTransit(IServiceCollection services)
{
services.AddMassTransit(busConfigurators =>
{
busConfigurators.AddRequestClient<ICacheRepository>();
busConfigurators.AddConsumers(typeof(Program).GetTypeInfo().Assembly);
busConfigurators.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri($"rabbitmq://{Config.Settings.RabbitMq_Host}"), hostConfigurator =>
{
hostConfigurator.Username(Config.Settings.RabbitMq_User);
hostConfigurator.Password(Config.Settings.RabbitMq_Password);
});
cfg.ReceiveEndpoint("myApp", e =>
{
e.ConfigureConsumers(context);
});
});
});
// CacheRepository
public class CacheRepository : ICacheRepository
{
private readonly IClient Client;
public CacheRepository(ICacheRepository client, ILogger<CacheRepository> logger)
{
this.client = client;
this.logger = logger;
}
}
When a dependency is scoped, the implication is that a new instance is needed for each scope (which is usually an incoming HTTP request or message.) It implies that the instance should not be re-used for multiple requests.
If you have a singleton that depends on that scoped dependency, that singleton will be created using an instance of that dependency (the request client.) Because that singleton "lives forever," so does the instance of the request client it contains.
The result is that the request client is not supposed to be re-used across different scopes, but now it is. One instance is used forever.
A likely solution is to modify the class that depends on that client so that it doesn't need to be a singleton. You mentioned that it has to be a singleton because it caches data.
How does it cache data? Does it do so by storing data in a private field? If so, perhaps you could make that field static. Now the class instance isn't re-used, but those fields are shared between instances. (Verify that interaction with those fields is thread safe if they may be accessed concurrently.)
Or if there's some other cache mechanism, you could move that into its own dependency and make that a singleton.
Then your class can be scoped. It will depend on the singleton cache, always using the same instance. It will also depend on the scoped request client, using a new instance for each scope.
You could inject IServiceProvider instead, and create a scope when the singleton needs to perform a request. That way, you're sticking to the expected use of the request client.
await using var scope = provider.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IRequestClient<T>>();
await client.GetResponse(...);

How to ensure 2 injected interfaces are instantiated as the same object instance

Given the following classes
public class RWRepository<TEntity, TEntityId> : IRWRepository<TEntity, TEntityId>
and
internal sealed class EagerLoadingRWRepository<TEntity>
: RWRepository<TEntity, Guid>
, IEagerLoadingRWRepository<TEntity>
I am registering these in my DI container like this (where service is IServiceCollection)
services.AddScoped(typeof(IRWRepository<,>), typeof(RWRepository<,>));
services.AddScoped(typeof(IEagerLoadingRWRepository<>), typeof(EagerLoadingRWRepository<>));
Say I inject both of these into 2 different handlers, both within the same scope. How I can ensure that both handlers will use the same object instance for each injected interface?
I.e. if both interfaces are injected, then both should use the same instance of EagerLoadingRWRepository, which inherits from RWRepository
This will be very cumbersome to do with MS.DI (ASP.NET Core's built-in DI Container), because it requires you to make each closed registration explicitly.
For instance:
services.AddScoped<IEagerLoadingRWRepository<Person>, EagerLoadingRWRepository<Person>>();
services.AddScoped<IRWRepository<Person, Guid>>(
c => (IRWRepository<Person, Guid>)c.GetRequiredService<IEagerLoadingRWRepository<Person>>());
services.AddScoped<IEagerLoadingRWRepository<Order>, EagerLoadingRWRepository<Order>>();
services.AddScoped<IRWRepository<Order, Guid>>(
c => (IRWRepository<Order, Guid>)c.GetRequiredService<IEagerLoadingRWRepository<Order>>());
services.AddScoped<IEagerLoadingRWRepository<Shipment>, EagerLoadingRWRepository<Shipment>>();
services.AddScoped<IRWRepository<Shipment, Guid>>(
c => (IRWRepository<Shipment, Guid>)c.GetRequiredService<IEagerLoadingRWRepository<Shipment>>());
How I can ensure that both handlers will use the same object instance?
Scoped Lifetime works exactly as you describe. You don't need to ensure anything.
Microsoft Docs
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scoped
For web applications, a scoped lifetime indicates that services are created once per client request (connection). Register scoped services with AddScoped.
In apps that process requests, scoped services are disposed at the end of the request.
Lifetimes:
Transient objects are always different. The transient OperationId value is different in the request handler and in the middleware.
Scoped objects are the same for a given request but differ across each new request.
Singleton objects are the same for every request.

Injecting IServiceProvider into Factory Class with Autofac

I have a factory class in a Net Core 3 console app which needs to be able to resolve against a DI container at runtime:
public class OptionFactory : IOptionFactory
{
private readonly IServiceProvider _svcProvider;
public OptionFactory( IServiceProvider svcProvider )
{
_svcProvider = svcProvider;
}
public IOption<T>? CreateOption<T>( params string[] keys )
{
// code eliminated for brevity
try
{
return retVal = _svcProvider.GetRequiredService<Option<T>>();
}
catch( Exception e )
{
return null;
}
}
}
I'm using Autofac to define the DI container and then "assign" it to IServiceProvider via new AutofacServiceProvider( builder.Build() ) in a provider class:
public class TestServiceProvider
{
public static IServiceProvider Instance { get; private set; }
static TestServiceProvider()
{
var builder = new ContainerBuilder();
builder.RegisterType<OptionFactory>()
.As<IOptionFactory>()
.SingleInstance();
// code omitted for brevity
Instance = new AutofacServiceProvider( builder.Build() );
}
}
I'm unclear about how to register IServiceProvider itself with the DI container so that it can be injected into the constructor. Is that even possible? It seems a little self-referential, which could be problematic.
All the examples I've seen online call for referencing back to the Autofac IContainer itself, (or to TestServiceProvider.Instance in my example). I can do that, but it would end tie my library to a concrete service provider class. Which I think I'd like to avoid if I can.
I realize injecting IServiceProvider is considered an anti-pattern by some/many, although others deem it acceptable in a factory class because the factory is "simply" extending the DI container. I'm open to other approaches which don't rely on a factory class, provided they allow me to create concrete instances of open generic types at runtime.
You have a couple of options (no pun intended 😃).
Easiest: Call builder.Populate() with an empty collection
The Autofac.Extensions.DependencyInjection package (which you're using, since you have AutofacServiceProvider) has an extension method ContainerBuilder.Populate() which handles registering stuff from an IServiceCollection and auto-registering the AutofacServiceProvider. You could call that method with an empty service collection and it'll work.
builder.Populate(Enumerable.Empty<ServiceDescriptor>());
This will get you exactly the thing you're looking for. However, there's an alternative to consider...
Alternative: Use ILifetimeScope
If it doesn't matter whether your OptionFactory is tied to Autofac, you can inject ILifetimeScope. Autofac has the current lifetime scope auto-registered, so this will work:
public OptionFactory(ILifetimeScope scope)
{
// scope is whatever lifetime scope the
// factory itself came from - if that's the
// root container, then the scope is the
// container
}
The benefit here is you'll get the richer resolve options Autofac offers without any extra work. The drawback would be you're tied to Autofac at this level, which may or may not matter.
Beware!
It may just be your example, but there's something important to know if you're resolving directly from the root container the way the example shows:
You could easily end up with a big memory leak.
Autofac holds on to all IDisposable instances it resolves so they can be safely disposed when the lifetime scope is disposed. If you are resolving from the container, that means any IDisposable will be held onto until the container itself is disposed, which, for most, is the lifetime of the application. That means - hypothetically - every resolution could be adding just a tiny little bit of memory that won't be disposed until the container is disposed. Memory leak.
For this reason we recommend always resolving from a nested lifetime scope rather than from the container. In a web app, that request-level lifetime scope is perfect because it disappears after a request. In an example like this, it's up to you and your app code to determine the best way to integrate lifetime scopes.
And, of course, if you're definitely, 100% guaranteed never resolving anything IDisposable, no worries.

Injecting a factory Func with ASP.NET Core DI

I'm trying to get something like this working:
public class FooService : IFooService {
public FooService(Func<IBarService> barFactory) { ... }
}
public class BarService : IBarService, IDisposable { ... }
services.AddSingleton<IFooService, FooService>();
services.AddTransient<IBarService, BarService>();
services.AddSingleton<Func<IBarService>>(ctx => () => ctx.GetService<IBarService());
This works as far as resolving the BarService instance, but I can't figure out how to properly manage its lifetime. When I do this inside one of the members of FooService:
using (var bar = _barFactory())
{
...
}
I get an ObjectDispoedException:
System.ObjectDisposedException: 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.
However, if I just do var bar = _barFactory();, without the using statement, I have no way to signal to the DI container that I'm done with the instance, and it can be disposed.
What's the correct approach here?
(Side note: yes, I know that some will object that a singleton service should not be dependent on a transient service. That's not what's happening here; the singleton service is dependent on a singleton factory, that produces transient instances. The singleton then uses the transient service for one or two statements and then is done with it, so there should be no actual lifetime problems here.)
As described in documentation:
The container will call Dispose for IDisposable types it creates. However, if you add an instance to the container yourself, it will not be disposed.
So just don't use using statement and all should be OK.

Autofac Lifetimes and the Default Provider within a Matching Lifetime Scope

I have an ASP.NET MVC web application using Autofac for dependency injection. Occasionally, this web application will start a thread to do some work separate from the request thread. When this background thread starts up, it establishes a new Autofac lifetime scope from the root container and runs some action.
public IAsyncResult Run<T>(Action<T> action)
{
var NewTask = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
using (var Scope = Runtime.Container.BeginLifetimeScope())
{
var Input = Scope.Resolve<T>();
action(Input);
}
});
return NewTask;
}
One of my dependencies registered with Autofac has two different implementations: one appropriate for http-request lifetimes and another appropriate for all other lifetimes. I tried to register them as follows:
builder
.Register(c => new Foo())
.As<IFoo>()
.InstancePerLifetimeScope();
builder
.Register(c => new FooForHttp(HttpContext.Current))
.As<IFoo>()
.InstancePerMatchingLifetimeScope(WebLifetime.Request);
Autofac selects FooForHttp for http requests (as expected). However, when my background thread spins up, any attempt to resolve IFoo results in an exception:
No scope with a Tag matching 'httpRequest' is visible from the scope
in which the instance was requested. This generally indicates that a
component registered as per-HTTP request is being reqested by a
SingleInstance() component (or a similar scenario.) Under the web
integration always request dependencies from the
DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime,
never from the container itself.
I know that Autofac always uses the last registered provider as the default provider for a particular component. I made an assumption here that it would use the last registered suitable provider.
Am I missing something, or is there a better approach to selecting a provider based on the tag of the current lifetime scope?
Register the web Foo as normal, but don't register the other Foo. When creating the lifetime scope for the async task, use the overload of BeginLifetimeScope() that takes an action on ContainerBuilder. Register the background Foo in this action (b => b.Register()) and this should override the web one. (Small keyboard here sorry :))
This can also be solved by using a tagged life time scope.
Register your fisrt Foo as instance of your tagged scope:
builder.RegisterType<Foo>().As<IFoo>.InstancePerMatchingLifetimeScope("YourScopeTag");
And create the scope with the same tag you registered your dependencie:
using (var Scope = Runtime.Container.BeginLifetimeScope("YourScopeTag"))
{
var Input = Scope.Resolve<T>();
action(Input);
}
Haven't tested it, but it should work
http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope

Categories