如何重构过度注射到更干燥的形式

时间:2016-12-15 15:18:44

标签: c# dependency-injection scope ninject

我想将我注入的对象聚合到一个Data对象中,这样我就不必拥有大的构造函数列表。但是我仍然希望使用WhenInjectedInto来提供上下文绑定。

例如,下面的规范测试了一个我认为有帮助的场景

WhenInjectedIntoRequestChain

表示只应在源位置使用绑定 注入已被注入parent0,其本身已被注入parent1,依此类推

该方法应具有签名

public static IBindingInNamedWithOrOnSyntax<T> 
        WhenInjectedIntoRequestChain<T>
            ( this IBindingWhenInNamedWithOrOnSyntax<T> @this
            , params Type[] parentChain
            )

用于测试此规范的规范应为

using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Ninject;
using Weingartner.Controls.PluginFramework;
using Xunit;

namespace Weingartner.Controls.Spec.PluginFramework
{
    public class NinjectExtensionsSpec
    {
        public interface IData { }

        public class Data0 : IData {}
        public class Data1 : IData {}
        public class Data2 : IData {}

        public class Params
        {
            public IList<IData> Data { get; set; }

            public Params(IEnumerable<IData> data)
            {
                Data = data.ToList();
            }

        }

        public class Target0
        {
            public Params P { get; set; }
            public Target0(Params p) {P = p;}
        }

        public class Target1
        {
            public Params P { get; set; }
            public Target1(Params p){P = p;}
        }

        [Fact]
        public void WhenInjectedIntoHeirarchyShouldWork()
        {
            var k = new StandardKernel();

            k.Bind<IData>().To<Data0>()
             .WhenInjectedIntoRequestChain(typeof(Params),typeof(Target0));
            k.Bind<IData>().To<Data1>()
             .WhenInjectedIntoRequestChain(typeof(Params),typeof(Target1));
            k.Bind<IData>().To<Data2>()
             .WhenInjectedIntoRequestChain(typeof(Params),typeof(Target1));


            var target0 = k.Get<Target0>();
            var target1 = k.Get<Target1>();

            target0.P.Data.Count.Should().Be(1);
            target1.P.Data.Count.Should().Be(2);


        }

    }
}

1 个答案:

答案 0 :(得分:0)

以下是针对某些测试用例的上述问题的解决方案。它不是上述问题的精确解决方案,但这种新方法可以解决上述问题。

using System;
using System.Linq;
using Ninject.Activation;
using Ninject.Infrastructure.Language;
using Ninject.Syntax;

namespace Weingartner.Controls.PluginFramework
{
    public static class NinjectExtensions
    {

        /// <summary>
        /// Indicates that the binding should only be used where the source 
        /// has been injected into parentChain[0] which in turn has been injected 
        /// into parentChain[1] and son on
        /// </summary>
        /// <param name="parentChain">This list of parents in order</param>
        /// <returns>The fluent syntax.</returns>
        public static IBindingInNamedWithOrOnSyntax<T> 
            WhenInjectedIntoRequestChain<T>
                ( this IBindingWhenInNamedWithOrOnSyntax<T> @this
                , params Type[] parentChain
                )
        {

            @this.BindingConfiguration.Condition =
                request =>
                {
                    var result = true;
                    foreach (var parent in parentChain)
                    {
                        result = result && WhenInjectedInto(request, parent);
                        request = request?.ParentRequest;
                    }
                    return result;
                };

            return (IBindingInNamedWithOrOnSyntax<T>)@this;
        }

        private static bool WhenInjectedInto(IRequest request, Type parent)
        {
            if (!parent.IsGenericTypeDefinition)
                return request?.Target != null 
                    && parent.IsAssignableFrom(request.Target.Member.ReflectedType);

            if (!parent.IsInterface)
                return request
                    ?.Target?.Member.ReflectedType
                    .GetAllBaseTypes()
                    .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == parent) 
                    ?? false;

            return request
                ?.Target?.Member.ReflectedType?
                .GetInterfaces()
                .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == parent) 
                ?? false;
        }
    }
}