在.NET中查看Unity依赖关系配置

时间:2016-01-21 15:25:56

标签: c# .net dependency-injection unity-container

我们在Unity Container内有很多注册。事实上大约有800行。

由于Unity没有验证方法,我们在测试应用程序之前引入了单元测试以验证配置,该应用程序看起来像下面的内容非常有效并且在配置中发现了很多问题:

IUnityContainer container = UnityContainerBuilder.Build();
foreach (ContainerRegistration mapping in container.Registrations)
{
    container.Resolve(mapping.RegisteredType, mapping.Name);
}

但是,我们有许多类遵循我们使用命名注册的decorator pattern。然后使用这些命名注册为其他注册构建InjectionConstructor,例如:

container.RegisterType<IMyType2, Concrete1MyType2>("Concrete1MyType2", new ContainerControlledLifetimeManager());
container.RegisterType<IMyType2, Concrete2MyType2>("Concrete2MyType2", new ContainerControlledLifetimeManager());
container.RegisterType<IMyType1, Concrete1MyType1>(
    "Concrete1MyType1", 
    new ContainerControlledLifetimeManager(),
    new InjectionConstructor(
        new ResolvedParameter<IMyType2>("Concrete2MyType2")));

是否可以通过某些代码访问您在InjectionConstructor中为注册配置的内容?

2 个答案:

答案 0 :(得分:0)

This article似乎建议你可以。

给出的例子是:

void DisplayContainerRegistrations(IUnityContainer theContainer)
{
    string regName, regType, mapTo, lifetime;
    Console.WriteLine("Container has {0} Registrations:",
            theContainer.Registrations.Count());
    foreach (ContainerRegistration item in theContainer.Registrations)
    {
    regType = item.RegisteredType.Name;
    mapTo = item.MappedToType.Name;
    regName = item.Name ?? "[default]";
    lifetime = item.LifetimeManagerType.Name;
    if (mapTo != regType)
    {
        mapTo = " -> " + mapTo;
    }
    else
    {
        mapTo = string.Empty;
    }
    lifetime = lifetime.Substring(0, lifetime.Length - "LifetimeManager".Length);
    Console.WriteLine("+ {0}{1}  '{2}'  {3}", regType, mapTo, regName, lifetime);
    }
}

答案 1 :(得分:0)

好的,在没有得到太多关于此的信息之后,我看了Unity的内部,因为我看不到任何公开的方式。我想出了以下内容,它使用Reflection来访问Unity各个部分的私有成员。

这样做的主要原因是将Transient实例注入Singletons有点不匹配。 Transient应该只持续很短的时间,而Singletons可能会在DI Container的整个生命周期中保留。 Simple Injector here提供了一些很好的信息。这是尝试获取注入信息的原因之一,因为Unity没有像Simple Injector那样的验证。

所以,这是我正在注册的服务接口和类。有3个接口和3个具体实现,IService2除外,它有2个实现。

public class Service1 : IService1
{
    private readonly IService2 _service2;
    private readonly IService3 _service3;

    public Service1(IService2 service2, IService3 service3)
    {
        _service2 = service2;
        _service3 = service3;
    }

    public int DoSomethingForService1()
    {
        return 1;
    }
}

public interface IService1
{
    int DoSomethingForService1();
}

public class Service2 : IService2
{
    public int DoSomethingForService2()
    {
        return 1;
    }
}

public class Service3 : IService3
{
    public int DoSomethingForService3()
    {
        return 1;
    }
}

public interface IService3
{
    int DoSomethingForService3();
}

public class Service2_1 : IService2
{
    public int DoSomethingForService2()
    {
        return 1;
    }
}

public interface IService2
{
    int DoSomethingForService2();
}

现在Unity容器已经建立起来了。我们倾向于使用单独的项目,因为我们不希望所有引用都保存在UI中。我们正在使用IService2的两个命名注册。

public static class UnityContainerBuilder
{
    public static IUnityContainer BuildDirectInUnity()
    {
        IUnityContainer container = new UnityContainer();

        container.RegisterType<IService2, Service2>("Service2OneAndOnly", new ContainerControlledLifetimeManager());
        container.RegisterType<IService2, Service2_1>("Service2_1", new ContainerControlledLifetimeManager());
        container.RegisterType<IService3, Service3>(new ContainerControlledLifetimeManager());
        container.RegisterType<IService1, Service1>(new ContainerControlledLifetimeManager(),
            new InjectionConstructor(
                new ResolvedParameter<IService2>("Service2OneAndOnly"),
                new ResolvedParameter<IService3>()));
    }
}

现在有趣的部分。这里我们得到Unity配置:

static void Main(string[] args)
{
    Console.WriteLine("Building Unity Container...");
    var container = (UnityContainer)UnityContainerBuilder.BuildDirectInUnity();

    Console.WriteLine("Listing Registrations...");

    FieldInfo policiesField = typeof(UnityContainer).GetFields(
                    BindingFlags.NonPublic |
                    BindingFlags.Instance).First(f => f.Name == "policies");

    FieldInfo parameterValuesField = typeof(SpecifiedConstructorSelectorPolicy).GetFields(
                    BindingFlags.NonPublic |
                    BindingFlags.Instance).First(f => f.Name == "parameterValues");

    FieldInfo paramNameField = typeof(ResolvedParameter).GetFields(
                    BindingFlags.NonPublic |
                    BindingFlags.Instance).First(f => f.Name == "name");

    var policies = (PolicyList)policiesField.GetValue(container);

    // build up a dictionary for loop below to use to get the lifetime manager
    var typeToRegistration = new Dictionary<Tuple<Type, string>, ContainerRegistration>();

    foreach (ContainerRegistration registration in container.Registrations)
    {
        typeToRegistration.Add(new Tuple<Type, string>(registration.RegisteredType, registration.Name), registration);
    }

    // now output the list
    foreach (ContainerRegistration registration in container.Registrations)
    {
        Console.WriteLine("{0} to {1}, {2}, {3}", 
            registration.RegisteredType.Name, 
            registration.MappedToType.Name, 
            registration.Name ?? "[default]",
            registration.LifetimeManagerType.Name);

        // need to check for our InjectionConstructor - I need local = false
        IConstructorSelectorPolicy constructorPolicy = policies.Get<IConstructorSelectorPolicy>(
            new NamedTypeBuildKey(registration.MappedToType, registration.Name), false);

        // and I need SpecifiedConstructorSelectorPolicy as we are not using the default constructor
        if (constructorPolicy is SpecifiedConstructorSelectorPolicy)
        {
            var specifiedConstructorPolicy = constructorPolicy as SpecifiedConstructorSelectorPolicy;

            // now output the ResolvedParameters for type, name, lifetime manager
            var paramValues = (InjectionParameterValue[])parameterValuesField.GetValue(specifiedConstructorPolicy);

            foreach (var param in paramValues)
            {
                if (param is ResolvedParameter)
                {
                    var resolvedParam = param as ResolvedParameter;

                    var name = (string)paramNameField.GetValue(resolvedParam);
                    string lifeTimeManagerName = 
                        typeToRegistration[new Tuple<Type, string>(resolvedParam.ParameterType, name)].LifetimeManagerType.Name;

                    Console.WriteLine("\t{0}, {1}, {2}", param.ParameterTypeName, name ?? "[default]", lifeTimeManagerName);
                }
                else
                {
                    Console.WriteLine("\t{0}", param.ParameterTypeName);
                }
            }
        }
    }

    Console.WriteLine("Complete");
    Console.ReadLine();
}

这样做的缺点是它纯粹是基于反射的黑客攻击,并且它不支持您可以配置类型的新实例的InjectionFactory。