JasperFx / lamar

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

Replace existing service not working #348

Closed DMDTT closed 1 year ago

DMDTT commented 1 year ago

Affected version: 8.0.1 | Net6

Afais the lamar container provides an api to replace a service.

I register a specific implementation of IDependency, afterwards I get an Instance of IImplementation which holds a dependency of ÌDependency.

In my example the ToString() returns A as expected through DependencyA.

Now, as Im replaceingDependencyAbyDependencyBI'd expect, that the nextContainer.GetInstancecall would return a new Instance of ÌImplenentation with DependencyB. In fact this is not a case: I get a new instance of IImplementation (verifiable via .GetHashCode()) but still with an instance of DependencyA

All dependencies are transient

I attach my source here. You can check out also on https://dotnetfiddle.net/uKSwwH

using System;
using System.Linq;
using Lamar;
using Microsoft.Extensions.DependencyInjection;

namespace RegistryTest
{
    public static class Program
    {
        public static void Main()
        {
            var registry = new ServiceRegistry();
            registry.For<IDependency>().Use<DependencyA>();
            registry.For<IImplementation>().Use<Implementation>();

            Container c = new Container(registry);

            Console.WriteLine(string.Join(", ", c.GetAllInstances<IDependency>().Select(x => x.ToString())));
            Console.WriteLine($"$dep should be \"A\" --> {c.GetInstance<IDependency>()}");
            Console.WriteLine($"$dep should be \"A\" --> {c.GetInstance<IImplementation>().Dependency}"); //// true

            var sc = new ServiceCollection();
            sc.AddTransient<IDependency, DependencyB>();
            c.Configure(sc);

            Console.WriteLine(string.Join(", ", c.GetAllInstances<IDependency>().Select(x => x.ToString())));
            Console.WriteLine($"$dep should be \"B\" --> {c.GetInstance<IDependency>()}");
            Console.WriteLine($"$dep should be \"B\" --> {c.GetInstance<IImplementation>().Dependency}"); //// false

            sc = new ServiceCollection();
            sc.AddTransient<IDependency, DependencyC>();
            c.Configure(sc);

            Console.WriteLine(string.Join(", ", c.GetAllInstances<IDependency>().Select(x => x.ToString())));
            Console.WriteLine($"$dep should be \"C\" --> {c.GetInstance<IDependency>()}");
            Console.WriteLine($"$dep should be \"C\" --> {c.GetInstance<IImplementation>().Dependency}"); //// false
            Console.ReadLine();

        }
    }

    public interface IDependency
    {
    }

    public class DependencyA : IDependency
    {
        public override string ToString() => "A";
    }

    public class DependencyB : IDependency
    {
        public override string ToString() => "B";
    }

    public class DependencyC : IDependency
    {
        public override string ToString() => "C";
    }

    public interface IImplementation
    {
        IDependency Dependency { get; set; }
    }

    public class Implementation : IImplementation
    {
        public IDependency Dependency { get; set; }

        public Implementation(IDependency dependency)
        {
            Dependency = dependency;
        }
    }
}
DMDTT commented 1 year ago

In fact i noticed that the problem is, that the TryToCreateMissingFamily doesn't have a look into the dependencies of a concrete implementation.

So a type isn't removed of ServiceGraph._byType Hashset. --> @jeremydmiller any suggestions?

Imho --> this would behave like old structuremap configure?

jeremydmiller commented 1 year ago

@DMDTT

"In fact i noticed that the problem is, that the TryToCreateMissingFamily doesn't have a look into the dependencies of a concrete implementation." -- I'm not sure I'd say that's true. The concrete type policy absolutely looks into the dependencies of the concrete type.

I'm extremely unenthusiastic about reopening the old StructureMap Container.Configure() can of worms because it's a non-stop source of edge case bugs. Like you just found here:)

Lamar purposely doesn't try to make the container configuration reentrant because it's all kinds of performance killing to do so.

My strong advice is to avoid using Configure() and instead make alterations at bootstrapping time before you build the container. If this is for testing, Lamar's got this: https://jasperfx.github.io/lamar/guide/ioc/registration/overrides.html

jeremydmiller commented 1 year ago

Nothing is going to change here.