如何根据最终注入链目标注入实例?

How to inject instance depending on the final injection chain target?

我有 classes:X1 <- Y <- Z <- Config(箭头表示通过构造函数注入)和 X2 <- Y <- Z <- ConfigZ 需要一些配置(Config class),但实例取决于最终类型:X1X2(类型本身或它们定义的键不知何故)。在此示例中,每个 YZConfig class.

应该有两个不同的实例

如何根据最终使用位置(X1X2)在 Z 中使用不同的 Config

    class X1
    {
        public X1(Y y)
        {
            int c = y.Z.Config.C; // This config variable is connected with X1.
        }
    }

    class X2
    {
        public X2(Y y)
        {
            int c = y.Z.Config.C; // This config variable is different than the one for X1.
        }
    }

    class Y
    {
        public Z Z { get; }

        public Y(Z z)
        {
            Z = z;
        }
    }


    class Z
    {
        public Config Config { get; }

        public Z(Config config)
        {
            Config = config;
        }
    }

    class Config
    {
        public int C { get; set; }
    }

我可以像下面那样做,但它看起来很腥很臭(一个粗略的例子):

    Bind<Config>().ToMethod(x =>
    {
        // Return proper config object depending on the classes found in the injection chain...
        IRequest req = x.Request;
        while (req != null)
        {
            if (req.Service.UnderlyingSystemType == typeof(X1))
            {
                return configForX1;
            }

            if (req.Service.UnderlyingSystemType == typeof(X2))
            {
                return configForX2;
            }

            req = req.ParentRequest;
        }

        throw new Exception("Oh no.");
    });

或者我应该这样做来减少腥味:

    class X1
    {
        public X1([Named("X1")] Config config, Y y)
        {
            y.SetConfig(config);
        }
    }

    class Y
    {
        private readonly Z _z;

        public Y(Z z)
        {
            _z = z;
        }

        public void SetConfig(Config config)
        {
            _z.SetConfig(config);
        }
    }

    class Z
    {
        private Config _config;

        public void SetConfig(Config config)
        {
            _config = config;
        }
    }

    Bind<MetricsApiConfiguration>().To().Named("X1");
    Bind<MetricsApiConfiguration>().To().Named("X2");

还有其他(更好的)想法吗?

WhenInjectedInto 很接近,但如您所述,仅查看当前请求。我创建了一个扩展方法来解决这个确切的用例:

    public static IBindingInNamedWithOrOnSyntax<T> WhenAnyAncestorIs<T>(this IBindingWhenSyntax<T> binding, params Type[] types)
    {
        bool Matches(IRequest request)
        {
            Type target = request.Target?.Member?.ReflectedType;
            return (target != null && types.Any(t => t == target))
                || (request.ParentRequest != null && Matches(request.ParentRequest));
        }

        return binding.When(Matches);
    }

并像这样使用:

Bind<IConfigSource>().To<DataConfigSource>()
    .WhenAnyAncestorIs(
        typeof(DataConfigurationRepository),
        typeof(ManifestRepository),
        typeof(DataManager)
    )
    .InSingletonScope();

对于不使用这些类型的请求,您将需要一个额外的绑定。