JasperFx / lamar

Fast Inversion of Control Tool and Successor to StructureMap
https://jasperfx.github.io/lamar
MIT License
563 stars 118 forks source link

Injected injectable on an `INestedContainer` is not resolved from a singleton dependent #351

Closed Fube closed 1 year ago

Fube commented 1 year ago

C# Version: 8.0 Target: .NET Core 3.1 Lamar version: 8.0.1

When marking something as injectable on a container then getting a nested container and injecting the implementation on that nested container and resolving a singleton dependent from said nested container, the injectable dependency is always null.

However:

Below is an example of a non-working configuration

using Lamar;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using WebApplication1.Services;

namespace WebApplication1
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            var registry = new ServiceRegistry();
            registry.Injectable<IWorker>();
            registry.AddTransient<SomeService>();
            registry.AddSingleton<JobTracker>();

            var container = new Container(registry);

            var nestedContainer = container.GetNestedContainer();
            nestedContainer.Inject<IWorker>(new NoOpWorker(), replace: true);

            // SomeService depends on JobTracker who depends on IWorker
            var transitiveDependent = nestedContainer.GetService<SomeService>(); // transitiveDependent.JobTracker.Worker == null
            var directDependent = nestedContainer.GetService<JobTracker>(); // directDependent.Worker == null
            var directResolution = nestedContainer.GetService<IWorker>(); // directResolution != null
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();
        }
    }
}

I am uncertain if this is expected behavior or not. I am trying to use this in a testing scenario where I would like to inject some services at runtime.

Here is the repo of the above example: https://github.com/Fube/lamar_injectable_singleton_issue

jeremydmiller commented 1 year ago

@Fube You need to inject a value into the root container. The Inject() functionality was strictly a workaround for frameworks like MassTransit at that time that needed to add specific services to a nested (scoped) container. What you're seeing is expected behavior, but also, the Inject() wasn't meant to be used quite like you're trying to do there.

Fube commented 1 year ago

Thank you for your response!

I ended up recreating the container for the tests that needed that kind of injection and injected the problematic service into the root container like you mentioned.