JasperFx / lamar

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

Setter Injection of only one property of same type #282

Closed ChristianSteu closed 2 years ago

ChristianSteu commented 3 years ago

Not sure if that is a bug or feature, but working with some stuff, I stumbled into some to me un-intuitive behavior:

I have an interface with some get/set Properties that have the same Type but different names. The default-implementation provides initialization for them, but I want to replace one of those values without the need to re-implement/decorate the whole thing. So my thought was: Lamar has setter injection, so just inject the value I want into the specific property.

Unfortunately that didn't work that easily, as Lamar used the new values for all properties of this type :-/. To fix that I would need to provide values for all properties, while optimally I would just like not invoking these other setters at all.

Here is code to reproduce the issue (using net5.0 with Lamar 5.0.3):

using System;
using Lamar;

namespace DependencyShenanigans
{
    public class Program
    {
        static void Main(string[] args)
        {
            var container = new Container(new RegistryViaLamar());
            var thing = container.GetInstance<IThingWithProperties>();

            Console.WriteLine($"{thing.PropertyA}-{thing.PropertyB}-{thing.PropertyC}");
            // outputs "X-X-X", but I only want to set PropertyB to get the output "A-X-C"
            // (instead of the default "A-B-C")
        }
    }

    public class RegistryViaLamar : ServiceRegistry
    {
        public RegistryViaLamar()
        {
            For<IThingWithProperties>().Use<ThingWithProperties>()
                .Setter<object>(nameof(IThingWithProperties.PropertyB)).Is("X");
        }
    }

    public interface IThingWithProperties
    {
        object PropertyA { get; set; }
        object PropertyB { get; set; }
        object PropertyC { get; set; }
    }

    public class ThingWithProperties : IThingWithProperties
    {
        public object PropertyA { get; set; } = "A";
        public object PropertyB { get; set; } = "B";
        public object PropertyC { get; set; } = "C";
    }
}
jeremydmiller commented 3 years ago

@ChristianSteu Sorry for the delay, I just noticed this today. I'd honestly recommend against using setter injection in these kind of cases and go for some kind of explicit builder in your own code. However, you can use explicit, inline dependency configuration to disambiguate the setters. There's an example in the docs here: https://jasperfx.github.io/lamar/documentation/ioc/setter-injection/

ChristianSteu commented 3 years ago

A late reply is better than no at all :-)

What exactly do you mean with "explicit, inline dependency configuration"? I would assume that you mean the example "Inline Setter Configuration" on the page you linked. And that is exactly what I used in the code I posted for the issue (only using nameof instead of the magic string "PropertyB"), but it sets the value for all Properties, which is not the behavior I would expect.

Or do you mean the attribute and/or policy example?

jeremydmiller commented 2 years ago

@ChristianSteu If it's trying to set the values of the other properties, it's a bug. Unless you've got some kind of "setter injection policy" that's not shown in the code above. And again, my honest advice is to avoid using setter injection whenever possible because of exactly this kind of thing.

ChristianSteu commented 2 years ago

@jeremydmiller I appreciate you saying I shouldn't use setter injection (I don't like it myself), but it was the most sensible solution for the problem I had.

The code I provided is everything that is to the project that reproduces that behavior, so I guess that makes it a bug (although from what I read I have the feeling that it has a pretty low/minimal priority)

jeremydmiller commented 2 years ago

@ChristianSteu I've got a backlog of issues to sweep up soon in Lamar, so maybe in the next week or so.