Issue when upgrading Autofac with async tasks and owned instances - c#

I have an issue with Autofac after upgrading from 4.9.2 to 5.2 in my ASP.NET MVC application.
I make use of Func<Owned<T>> factory pattern in the Controller because an Controller Action starts a Long running Task and will run longer than the request exists. In that Task I am resolving other instances.
This worked fine in Autofac 4.9.2. But after upgrading to Autofac 5.2 the parent Lifetime scope (AutofacWebRequest) gets disposed and it not possible to resolve instances within the owned instance anymore.
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
Is there something I can do to work around this or is there a best practice?
Controller Code:
private readonly Func<Owned<IBusinessLogic>> _businessLogicFactory;
public ActionResult Index()
{
var businessLogic = _businessLogicFactory();
var unitOfWorkFactory = _unitOfWorkFactory;
Task.Run(() =>
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
using (businessLogic)
{
var task = businessLogic.Value.DoHardBusinessAsync();
task.Wait();
}
});
return View();
}
Business Logic Code (also using a factory):
public class BusinessLogic : IBusinessLogic
{
private readonly Func<Owned<OtherBusinessLogic>> _otherBusinessLogicFactory;
public BusinessLogic(Func<Owned<OtherBusinessLogic>> otherBusinessLogicFactory)
{
_otherBusinessLogicFactory = otherBusinessLogicFactory;
}
public async Task DoHardBusinessAsync()
{
using (var otherBusiness = _otherBusinessLogicFactory())
{
await otherBusiness.Value.DoHardBusinessAsync();
}
}
}

You could try to create a new lifetime scope that is independent of the request scope to be used with your long running task like so
Task.Run(() =>
{
using (var scope = container.BeginLifetimeScope())
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
using (businessLogic)
{
var task = businessLogic.Value.DoHardBusinessAsync();
task.Wait();
}
}
});
Look at this question for ideas on how to get a hold of the container
Retrieving Autofac container to resolve services

#NataliaMuray's approach is awesome - one downside of it is that it tends to encourage Service Locator style resolving rather than constructor injection. This can tend to "hide" dependencies, making it harder to identify the dependencies of a given class.
One potential solution is to introduce the notion of a dependency that is explicit that it wraps another dependency that you want to resolve outside the normal web request's lifetime scope.
The code might look something like:
public class AsyncRunner : IAsyncRunner
{
public ExecutionResult TryExecute<TService>(Action<TService> toEvaluate, string #exceptionErrorMessage, int timeoutMilliseconds, string additionalErrorInformation = "")
{
try
{
var task = new Task(() =>
{
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<TService>();
toEvaluate(service);
}
});
task.ContinueWith(t => { /* logging here */, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously).SuppressExceptions();
task.Start();
var completedWithinTime = task.Wait(timeoutMilliseconds);
return completedWithinTime ? ExecutionResult.Ok : ExecutionResult.TimedOut;
}
catch (Exception e)
{
/* logging here */
return ExecutionResult.ThrewException;
}
}
}
Register IAsyncRunner with Autofac as well.
And then your dependency, instead of
private readonly Func<Owned<IBusinessLogic>> _businessLogicFactory;
would be
private readonly IAsyncRunner<IBusinessLogic>> _businessLogic;
And instead of:
var businessLogic = _businessLogicFactory();
var unitOfWorkFactory = _unitOfWorkFactory;
Task.Run(() =>
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
using (businessLogic)
{
var task = businessLogic.Value.DoHardBusinessAsync();
task.Wait();
}
});
would be:
//var businessLogic = _businessLogicFactory();
var unitOfWorkFactory = _unitOfWorkFactory;
Task.Run(() =>
{
System.Threading.Thread.Sleep(5000); // Sleep simulates that it may take some time until other instances are resolved
_businessLogic.TryExecute(z => {
var task = z.Value.DoHardBusinessAsync();
task.Wait();
});
});
The advantage of this style is that the property and constructor injection makes clear what the dependencies are, and how they are being used (i.e. the declaration makes clear that it will be resolved outside the context of the standard lifetime scope). Note you don't need to use Owned with my suggestion (disposal of the lifetime scope that is manually constructed will be sufficient). I have removed the use of Func, but you could use Func or Lazy if you really needed it alongside my suggestion.

Related

Memory leak in Xamarin Forms app when using DI in a Task

I am creating a Xamarin Forms application, and I am using the Xamarin Profiler to show that I have a memory leak. I have tracked the memory leak down to where it is happening, but I can't understand WHY it is happening.
I have a class (we will call it MyClass for now). And that class is using a Timer to call a service once every second. That service makes a REST call to retrieve a bunch of information, and then serializes the results back into an object....
MyClass:
public class MyClass : ContentPage
{
private readonly IMyService myService;
public MyClass() : base()
{
}
protected override async void OnAppearing()
{
StartTimer();
}
private void StartTimer()
{
Task.Run(async() =>
{
while(true)
{
myService = ((App)App.Current)
.serviceProvider
.GetRequiredService<IMyService>();
//--- everytime I call myService.GetSystemStatus(), my allocated memory continues to rise
MyResponse response = await myService.GetSystemStatus();
Device.BeginInvokeOnMainThread(() =>
{
// update the UI here...
});
await Task.Delay(1000);
}
});
}
}
MyService (Singleton):
public class MyService : IMyService
{
private readonly IMyHttpClientFactory httpClientFactory;
public MyService(IMyHttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public async Task<MyResponse> GetSystemStatus()
{
return await httpClientFactory.Create().GetAsync<MyResponse>(
"http://example.com/api/status"
);
}
}
MyHttpClientFactory (Singleton):
public class MyHttpClientFactory : IMyHttpClientFactory
{
private readonly IServiceProvider _serviceProvider;
public MyHttpClientFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public MyHttpClient Create()
{
return _serviceProvider.GetRequiredService<MyHttpClient>();
}
}
MyHttpClient:
public class MyHttpClient : IDisposable
{
private HttpClient _httpClient;
public MyHttpClient ()
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(10);
}
public async Task<T> GetAsync<T>(string url) where T : new()
{
string s = await GetStringAsync(url);
return JsonConvert.DeserializeObject<T>(s);
}
public async Task<string> GetStringAsync(string url)
{
using (var response = await _httpClient.GetAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
My services are defined as follows:
public partial class App : Application
public ServiceProvider serviceProvider;
public App()
{
IServiceCollection services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
InitializeComponent();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<MyHttpClient>("MyHttpClient", x =>
{
x.Timeout = TimeSpan.FromSeconds(5);
});
services.AddSingleton<IMyHttpClientFactory, MyHttpClientFactory>();
services.AddSingleton<IMyService, MyService>();
}
}
Best I can tell, the memory is going up because I am referencing the DI MyService inside a separate thread. But I am not sure if this is the reason or if there is something else that would be causing the leak?
Any advice would be greatly appreciated!!!
Thanks!
From what I understand from your code and your comments, it looks like you're looping by calling StartTimer() inside the Device.StartTimer() method.
According to the documentation, Device.StartTimer() is recurring and will occur every X seconds, depending of your interval parameter.
By removing the call to StartTimer() (the one between t.Dispose() and return false of MyClass.StartTimer, your code should work as expected and you will not create a new timer every x seconds
What could be the cause of the leak:
Your MyHttpClient class implements the IDisposable interface, yet the code to use an instance of this class is not leveraging the disposable nature of the object.
Even though the internal HttpClient instance is wrapped in a using statement, the MyHttpClient instance will not be disposed of as you would expect.
// from MyHttpClient class
public async Task<MyResponse> GetSystemStatus()
{
// no using statement here
return await httpClientFactory.Create().GetAsync<MyResponse>(
"http://example.com/api/status"
);
}
// should be:
public async Task<MyResponse> GetSystemStatus()
{
using (var client = await httpClientFactory.Create())
{
return await client.GetAsync<MyResponse>("http://example.com/api/status");
}
}
Another thing to try is to change the location of the resolution of the MyService instance to inside the Task since this is where it is used. This will allow the task to own the resource, and allow it to be collected when the task is complete.
private void StartTimer()
{
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
Task t = Task.Run(async() =>
{
// resolve the service here
myService = ((App)App.Current)
.serviceProvider
.GetRequiredService<IMyService>();
MyResponse response = await myService.GetSystemStatus();
Device.BeginInvokeOnMainThread(() =>
{
// update the UI here...
});
});
t.Wait();
t.Dispose();
StartTimer();
return false;
});
}
A couple of additional observations of your code:
In your HttpClientFactory's Create() method, you are resolving an instance of your client from the DI container.
Your MyHttpClient class has a default constructor which means the resolution is not needed since there are no additional dependencies requiring DI support.
Your code could simply return a new MyHttpClient() instance from the Create() method without the need for DI.
Your MyHttpClient also implements the IMyHttpClient interface, but your factory returns the concrete type. This means you need to either remove the interface as unnecessary or change the return type to be the interface type since the interface is redundant unless it is used.
Thank you all for your answers....
I finally figured out the source of the memory leak.
The problem was that I was referencing "MyService" like this:
myService = ((App)App.Current)
.serviceProvider
.GetRequiredService<IMyService>();
The problem was that the serviceProvider object was a public property on my App. So each time I referenced the provider inside my loop, it was creating the leak.
To get around this, I added an abstract method to each of my pages that implemented MyClass to return the service correctly using DI. This has corrected my memory leak issue....
Thanks all for the help!
I don't think that your timer logic is the cause of the leak.
But in case it is useful to you, here is a clean way to do work periodically, yet if work takes a long time, avoid events "piling up".
Given await/async, no Timer is needed.
(There is an alternative solution that starts/stops a single System.Timers.Timer, but I won't go into that here.)
Replace StartTimer() declaration with the following:
/// <summary> Runs until keepRunning() returns false.
/// Delays by "msecDelay" AFTER finishing the previous loop's non-UI work. </summary>
private void StartTaskLoopWhileKeepRunning(Func<bool> keepRunning, int msecDelay = 250)
{
Task.Run(async () =>
{
while (keepRunning())
{
// Do non-UI work here.
// ... possibly slow work ...
Device.BeginInvokeOnMainThread(() =>
{
// NOTE: This work will run in parallel with the next delay.
// ... Do UI work here. ...
});
// Non-UI thread sleeps for msec.
await Task.Delay(msecDelay);
}
});
}

How to initialize scoped dependencies for consumers using MassTransit filters?

I would like to initialize some dependencies resolved from the MassTransit serviceProvider in the same way Asp.Net Core does with the pipeline's middlewares.
In particular I would like to inspect the incoming message before the consumer is called and extract the tenant from it (I'm currently working on a multitenant web application with single database per tenant).
With this informations I need to initialize some scoped instances (Ef Core DbContext for example).
I know that I can inject them in the Consumer through constructor but this means that I must do that everytime I write a new one, so I suppose that a filter should be the right place (correct me if I'm wrong).
The problem raises when I need to access the current consumer scope to resolve the dependencies that I need. I was thinking that the behavior of the MassTransit' pipeline was similar to the Asp.Net one regarding middleware injection but I was probably wrong.
I haven't found any documentation on how to do that clearly without cluttering the code of the filter, so any suggestion is going to be really appreciated.
This is the filter that I need to modify:
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
public void Probe(ProbeContext context) { }
public async Task Send(T context, IPipe<T> next)
{
//Resolve scoped instance here and do something before Consumer is called
var connectionStringProvider = scope.GetService<IConnectionStringProvider>();
await next.Send(context);
}
}
public class RegistrationsDeliveredEventConsumer : IConsumer<IRegistrationsDelivered>
{
private readonly IConnectionStringProvider _connectionStringProvider;
public RegistrationsDeliveredEventConsumer(IConnectionStringProvider connectionStringProvider)
{
//This should be the same instance that has been resolved in the filter' Send() method
_connectionStringProvider = connectionStringProvider;
}
public async Task Consume(ConsumeContext<IRegistrationsDelivered> context)
{
}
}
This is a simplified example of my code but this should be enough
There's two facets to consider: 1) are filters registered as services/pulled from the service collection when using the ASP.NET Core integration and 2) what lifetime do the filters have if they are. I'm not familiar with the MassTransit ASP.NET Core integration, but it looks like you should be good based on a cursory review. You'll need to confirm that both of those requirements are met.
For dependency injection, in general, constructor injection is the way to go unless there's a very specific need to do something different, which does not seem to be the case here. In short, you need a constructor for your filter.
What exactly you need to inject is a function of the lifetime of the filter. If it has a transient lifetime, then you can inject your scoped dependencies directly. If it has a singleton lifetime, then you'll need to inject IServiceProvider instead, and do the following whenever you need to use one of those dependencies:
using (var scope = _serviceProvider.CreateScope())
{
var dep = scope.ServiceProvider.GetRequiredService<MyDependency>();
// do something with `dep`
}
Here's a draft... I'm sure there are missing pieces, so let me know if you have questions.
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
private readonly Func<string, IDbConnection> _dbContextAccessor;
public void Probe(ProbeContext context) { }
public TenantContextInitializerFilter(Func<string, IDbConnection> dbContextAccessor)
{
_dbContextAccessor = dbContextAccessor;
}
public async Task Send(T context, IPipe<T> next)
{
var tenantId = ""; // place holder
using (var dbContext = _dbContextAccessor(tenantId))
{
//... do db logic
}
await next.Send(context);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IConnectionStringProvider>(
provider => null /* TODO figure out how to fetch scoped instance from a cache or some storage mechanism*/);
services.AddScoped(provider =>
{
IDbConnection Accessor(string tenantId)
{
if (provider.GetService<IConnectionStringProvider>()
.TryGetConnectionString(tenantId, out var connectionString, out var providerName))
return new SqlConnection(connectionString);
throw new Exception();
}
return (Func<string, IDbConnection>)Accessor;
});
}
}

How to call async method in Autofac registration?

I want to do call an awaitable async method during a registration like this:
// builder variable contains Autofac ContainerBuilder
builder.Register(
(async (context, parameters) => // need async here
{
var someClass = new SomeClass(context.Resolve<ISomeDependency>());
await someClass.SomeAsyncInitMethod(); // need to await result
return someClass;
})).As<ISomeClass>().SingleInstance();
SomeClass implements ISomeClass as Service.
The important part is the someClass.SomeAsyncInitMethod() call. This is async, so because of this I need to await it here and put the async keyword into the Register method. But now Autofac thinks this returns a Task<SomeClass> which is not registerable as Service ISomeClass.
How to achieve the above and register SomeClass as ISomeClass when awaiting the async init Method?
I think that doing any I/O intensive work at resolution phase is wrong design, because it's usually important to have full control over the order of this operations, catch their exceptions, repeat them, control time between some of them, etc.
Solution is to postpone them with factories. Let me replace SomeClass with a more meaningful NpgsqlConnection:
var builder = new ContainerBuilder();
builder.Register(context =>
{
// make sure not to capture temporary context:
// https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html#service-resolution
var connectionString = context.Resolve<IConfiguration>().GetConnectionString("MyDb");
return new Func<Task<NpgsqlConnection>>(async () =>
{
var connection = new NpgsqlConnection(connectionString);
await connection.OpenAsync();
return connection;
});
});
And here's how connection user can look like:
public sealed class Repository
{
private readonly Func<Task<NpgsqlConnection>> _connectionFactory;
public Repository(Func<Task<NpgsqlConnection>> connectionFactory)
{
_connectionFactory = connectionFactory;
}
public async Task<string> GetServerVersionAsync()
{
using (var openedConnection = await _connectionFactory())
return openedConnection.ServerVersion;
}
}
Even if Autofac would support async registrations, it is still would be beneficial to require a factory in Repository constructor, because the connection is a limited resource and it is better to limit the time it is opened.
This is an old question, but I think autofac does not support that.
We used:
builder.Register(c =>
{
var bar= c.Resolve<IBar>();
var foo = new Foo(bar);
return foo.ComputeAsync().ConfigureAwait(false).GetAwaiter().GetResult();
})
.As<IFoo>()
.SingleInstance();
But as mentioned on the comments: Registering async factory in Autofac
We ran into the same issue but found a different solution, I thought I'd share. You can wrap your async code within the Register method in a Task.Run and call .GetResult() or .Wait(). This will spawn a new context which ensures that any async code runs nicely within that context before being returned.
Sample taken from the other response snippet:
builder.Register(c =>
{
var bar= c.Resolve<IBar>();
var foo = new Foo(bar);
var resultTask = Task.Run(async () => {
return await foo.ComputeAsync();
});
return resultTask.GetResult();
})
.As<IFoo>()
.SingleInstance();

Akka.net deadlocking issue with ActorSelection

Having an issue with akka.net. I need to access an actor which I have already created with a specific name. I can retrieve the actor from IActorContext but I am struggling to access it from the ActorSystem.
I have created a method called GetOrCreateActor which attempts to get the actor using ActorSelection. If it doesn't exist, the catch creates a new actor with the name. If it does exist, I want it to return the reference. However, it never returns from '.Result'. Assuming this could be some sort of deadlocking issue.
public static IActorRef GetOrCreateActor<T>(this ActorSystem actorSystem, string actorPath, string name = null) where T : ActorBase
{
try
{
return actorSystem.ActorSelection(actorPath).ResolveOne(TimeSpan.FromSeconds(1)).Result;
}
catch
{
return actorSystem.ActorOf(actorSystem.DI().Props<T>(), name);
}
}
Edit
I've tried to include a simplified version of the calling code below.
The actor system is created in an IOC container using AutoFac (ExampleActor is the ReceiveActor I am trying to access):
containerBuilder.RegisterAssemblyTypes(typeof(ExampleActor).Assembly).Where(x => x.Name.EndsWith("Actor"));
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
return lazyContainer.Value;
ActorSystem is then injected into another class, where I call the GetOrCreateActor method (via the Execute method):
public class ExampleCommand : IExampleCommand
{
private readonly ActorSystem _actorSystem;
public ExampleCommand(ActorSystem actorSystem)
{
_actorSystem = actorSystem;
}
public void Execute()
{
SendMessage();
}
private void SendMessage()
{
string message = new Message();
_actorSystem.GetOrCreateActor<ExampleActor>("akka://ExampleActorSystem/user/ExampleActor", "ExampleActor").Tell(message);
}
}
The above command would be called from a RESTful endpoint
public ExampleGetModule(IExampleCommand exampleCommand)
{
Get["/api/createExample"] = parameters =>
{
exampleCommand.Execute();
};
}
Your deadlocking issue looks more like it has to do with how you're using your container than it does Akka.NET:
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
In terms of what can go wrong here, self-referential Lazy<T> types are an infamous source of race-conditions. You should not be calling lazyContainer.Value inside of this registration method if the output of containerBuilder.Build depends on the input of containerBuilder.Register.
Last thing is to use step-through debugging to make sure that your application actually calls into the ResolveOne method here - if you're not getting a timeout exception back then it means that your application is deadlocking on producing the actor system (because of how DI is configured).

Autofac - resolving dependencies in multi thread environment

public class MultithreadTester
{
public void Run()
{
var builder = new ContainerBuilder();
builder.RegisterType<ManualWork>().As<IWork>();
builder.RegisterType<ColabManualWork>().As<IColabWork>();
builder.RegisterType<RelaxAfterManualWork>().As<IRelax>();
var container = builder.Build();
//#1 - Simple single thread
using (var scope = container.BeginLifetimeScope())
{
var work = scope.Resolve<IWork>();
work.DoWork();
}
//#2 - Resolving dependecies in worker threads in scopes of these threads without passing lifetime scopes are container into implementation
using (var scope = container.BeginLifetimeScope())
{
var work = scope.Resolve<IColabWork>();
work.DoWork();
}
//#3 - Resolving dependecies in worker threads when original scope is already gone (simulates fast request on same service which spawns threads for request processing)
IColabWork workForSample3;
using (var scope = container.BeginLifetimeScope())
{
workForSample3 = scope.Resolve<IColabWork>();
}
workForSample3.DoWork();
Console.ReadKey();
}
public interface IRelax
{
void DoRelax();
}
public class RelaxAfterManualWork : IRelax
{
public void DoRelax()
{
Console.WriteLine("Relaxing after hard work...");
Thread.Sleep(1000);
Console.WriteLine("Relax is done...");
}
}
public interface IWork
{
void DoWork();
}
public class ManualWork : IWork
{
private readonly IRelax _relaxActivity;
public ManualWork(IRelax relaxActivity)
{
_relaxActivity = relaxActivity;
}
public void DoWork()
{
Console.WriteLine("Ufff, this is so hard...");
Thread.Sleep(5000);
Console.WriteLine("Work is done...");
_relaxActivity.DoRelax();
}
}
public interface IColabWork
{
void DoWork();
}
public class ColabManualWork : IColabWork
{
public void DoWork()
{
Console.WriteLine("We must discuss how to share the workload...");
Thread.Sleep(1500);
Action action = () =>
{
//IT WOULD BE FINE TO HAVE RESOLVED DEPENDENCIES PER THREAD AND IN THREAD OWN LIFETIMESCOPE
Console.WriteLine("Ufff, this is so hard but working with my buddies helps...");
Thread.Sleep(2500);
Console.WriteLine("Work is done...");
var relaxActivity = new RelaxAfterManualWork();
relaxActivity.DoRelax();
};
var thread1 = new Thread(() => { action(); });
var thread2 = new Thread(() => { action(); });
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
}
In sample marked as #1 I am resolving IWork and run some action. For single thread environment I understand what is going on in DI, how I should work with DI, lifetimescope and how to resolve dependencies.
But I have trouble to understand DI in multi thread environment. I try to demonstrate some issues I have is samples #2, #3. In these samples I would somehow need to solve dependencies in LifetimeScope which would be created for each threads in ColabManualWork. Of course I do not want references on any class from Autofac to prevent coupling.
I even created simple factory which would be suitable for creating nested LifetimeScopes from current one:
public interface IIsolatedLifetimeScopeFactory<TA>
{
void Create(Action<TA> action);
}
public class IsolatedLifetimeScopeFactory<TA> : IIsolatedLifetimeScopeFactory<TA>
{
private readonly ILifetimeScope _scope;
public IsolatedLifetimeScopeFactory(ILifetimeScope scope)
{
_scope = scope;
}
public void Create(Action<TA> action)
{
using (var subScope = _scope.BeginLifetimeScope())
{
var a = subScope.Resolve<TA>();
action(a);
}
}
}
But I do not like this solution well. There are three big issues - 1) All logic must be in lambda function (or equivalent method); 2) in future Autoflac can re-implement functionality of disposing child scopes if parent scope is disposed again (this functionality was already here for few months); 3) As demonstrated in sample #3 I can dispose parent LifetimeScope before any functionality in ColabManualWork is even started and thus my factory would be using already disposed LifetimeScope.
Can somebody help me how to effectively solve resolving issues in worker threads? I read something related to SimpleInjector named Work with dependency injection in multi-threaded applications but I do not fully get it plus it is not Autofac related. In that article is written
In a multi-threaded application, each thread should get its own object graph. This means that you should typically call container.GetInstance() once at the beginning of the thread’s execution to get the root object for processing that thread
How to solve dependencies in worker threads without coupling with Autofac and in thread-related lifetimescope?
To give each thread its own lifetime scope, you just need to register your IsolatedLifetimeScopeFactory as SingleInstance. This will solve your concerns 2) and 3)
[TestMethod]
public void MyTestMethod()
{
var cb = new ContainerBuilder();
cb.RegisterGeneric(typeof(IsolatedLifetimeScopeFactory<>))
.SingleInstance();
var container = cb.Build();
using (var scope1 = container.BeginLifetimeScope("scope1"))
using (var scope2 = scope1.BeginLifetimeScope("scope2"))
{
var factory = scope2.Resolve<IsolatedLifetimeScopeFactory<object>>();
var tag = factory._scope.Tag; // made _scope public for testing purposes
Assert.AreNotEqual("scope1", tag);
Assert.AreNotEqual("scope2", tag);
// This particular string "root" is probably not guaranteed behavior, but
// being in the root scope is guaranteed for SingleInstance registrations.
Assert.AreEqual("root", tag);
}
}
Your concern 1) could be solved by using a different abstraction. For example, you could add this to the IsolatedLifetimeScopeFactory
public Autofac.Features.OwnedInstances.Owned<TA> Create()
{
return _scope.Resolve<Autofac.Features.OwnedInstances.Owned<TA>>();
}
And you could hide Owned behind an abstraction if you really wanted to, although I would say that's overkill.

Categories