C# MS Unity:解析自动注册的类型

C# MS Unity:解析自动注册的类型,c#,unity-container,C#,Unity Container,我使用MS Unity和“按约定注册”(自动注册)来注册命名空间中的所有类。代码(如下所示)在它应该的位置工作,并返回预期的结果 var container = new UnityContainer(); container.RegisterTypes( AllClasses.FromLoadedAssemblies().Where( t => t.Namespace == "DependencyInjectionExample.Test"),

我使用MS Unity和“按约定注册”(自动注册)来注册命名空间中的所有类。代码(如下所示)在它应该的位置工作,并返回预期的结果

var container = new UnityContainer();
        container.RegisterTypes( AllClasses.FromLoadedAssemblies().Where(
            t => t.Namespace == "DependencyInjectionExample.Test"), 
            WithMappings.FromMatchingInterface, 
            WithName.Default, 
            WithLifetime.ContainerControlled);
结果

集装箱有4个注册号

  • IUnityContainer“[默认]”容器
  • TestClass1“[默认]”容器已控制
  • TestClass2“[默认]”容器受控
  • TestClass3“[默认]”容器受控
我的问题是我不知道如何解决它们。我试过了

var allRegistered = container.ResolveAll<ITestable>();
三个测试类之一。他们都做同样的事情

namespace DependencyInjectionExample.Test
{
 public class TestClass1 : ITestable
 {
    public TestClass1() { }

    public string SaySomething()
    {
        return "TestClass1 hello";
    }
 }
}

我仔细研究了RegisterTypes,它接受Func类型的参数,您可以使用该参数为每个实例提供名称。不需要创建扩展来实现这一点。创建属性后,为要在容器中注册的每个实例提供一个特定的名称非常容易

首先创建属性类:

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class UnityNamedInstanceAttribute : Attribute
{
    private readonly string instanceName;

    public UnityNamedInstanceAttribute(string instanceName)
    {
        this.instanceName = instanceName;
    }

    public string InstanceName
    {
        get { return this.instanceName; }
    }
}
然后设置一个类以帮助解析实例的名称:

public static class UnityNamedInstance
{
    public static string AttributeName(Type type)
    {
        var namedAttribute = type
            .GetCustomAttributes(typeof(UnityNamedInstanceAttribute), true)
            .FirstOrDefault()
            as UnityNamedInstanceAttribute;
        return namedAttribute != null ? namedAttribute.InstanceName : null;
    }

    public static string AttributeNameOrDefault(Type type)
    {
        return UnityNamedInstance.AttributeName(type) ?? WithName.Default(type);
    }
}
使用我们创建的属性声明测试类:

public interface ITest
{
    void DebugName();
}

[UnityNamedInstance("Test A")]
public class TestA : ITest
{
    #region ITest Members

    public void DebugName()
    {
        Debug.WriteLine("This is TestA");
    }

    #endregion
}
[UnityNamedInstance("Test B")]
public class TestB : ITest
{
    #region ITest Members

    public void DebugName()
    {
        Debug.WriteLine("This is TestB");
    }

    #endregion
}

[UnityNamedInstance("Test C")]
public class TestC : ITest
{
    #region ITest Members

    public void DebugName()
    {
        Debug.WriteLine("This is TestC");
    }

    #endregion
}
并将“按约定方式注册”更改为:

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "DependencyInjectionExample.Test"),
            WithMappings.FromAllInterfaces, // This way you have the same instance for each interface the class implements
            UnityNamedInstance.AttributeNameOrDefault, // Use our helper to solve the name of the instance
            WithLifetime.ContainerControlled);
或者,您可以避免创建属性和帮助器,并在类名后命名实例,如下所示:

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "DependencyInjectionExample.Test"),
            WithMappings.FromAllInterfaces,
            WithName.TypeName, // Use the type name for the instances
            WithLifetime.ContainerControlled);
现在,您可以使用类声明中传递给属性构造函数的名称或类名访问每个类实例:

        // Access each instance using the name you gave in the attribute
        container.Resolve<ITest>("Test A").DebugName();
        container.Resolve<ITest>("Test B").DebugName();
        container.Resolve<ITest>("Test C").DebugName();
//使用您在属性中给出的名称访问每个实例
container.Resolve(“测试A”).DebugName();
container.Resolve(“测试B”).DebugName();
container.Resolve(“Test C”).DebugName();
如果要获取实现特定接口的所有已注册实例:

        foreach (var test in container.Resolve<ITest[]>()) {
            test.DebugName();
        }
foreach(container.Resolve()中的var测试){
test.DebugName();
}

foreach(container.ResolveAll()中的var测试){
test.DebugName();
}

我很想知道这是否适合你的需要。我感谢你的反馈

丹尼斯·布拉特(Denis Brat)的评论中有一个关键点,帮助我解决了这个问题

如果您使用的泛型存储库只有接受接口的构造函数,那么注册类型的GetInstance方法将失败,并出现依赖性异常

如果您在自动注册具体类之前手动注册接口,那么它可以工作,即

视图模型定位器:

unityContainer.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));

unityContainer.RegisterTypes(AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("Mynamespace.")),
            WithMappings.FromAllInterfaces,
            WithName.TypeName,
            WithLifetime.ContainerControlled,null,true);
public MainViewModel(IGenericRepository<Client> clientRepository, IExceptionManager exceptionManager )
    { whatever }
return ServiceLocator.Current.GetInstance<MainViewModel>("MainViewModel");
unityContainer.RegisterType(typeof(IGenericRepository)、typeof(genericpository));
unityContainer.RegisterTypes(AllClasses.FromAssembliesInBasePath().Where(t=>t.Namespace.StartWith(“Mynamespace”),
使用mappings.fromalinterfaces,
WithName.TypeName,
WithLifetime.ContainerControl,null,true);
型号:

unityContainer.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));

unityContainer.RegisterTypes(AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("Mynamespace.")),
            WithMappings.FromAllInterfaces,
            WithName.TypeName,
            WithLifetime.ContainerControlled,null,true);
public MainViewModel(IGenericRepository<Client> clientRepository, IExceptionManager exceptionManager )
    { whatever }
return ServiceLocator.Current.GetInstance<MainViewModel>("MainViewModel");
public main视图模型(IGenericRepository clientRepository、IEExceptionManager例外管理器)
{随便什么}
视图:

unityContainer.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));

unityContainer.RegisterTypes(AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("Mynamespace.")),
            WithMappings.FromAllInterfaces,
            WithName.TypeName,
            WithLifetime.ContainerControlled,null,true);
public MainViewModel(IGenericRepository<Client> clientRepository, IExceptionManager exceptionManager )
    { whatever }
return ServiceLocator.Current.GetInstance<MainViewModel>("MainViewModel");
返回ServiceLocator.Current.GetInstance(“MainViewModel”);

它将找到它们并返回正确的实例。

如果仔细查看,您将看到容器中只注册了具体的类。容器中注册的唯一接口是默认的IUnityContainer引用。您是否在这些类(TestClass1等)中实现接口?此外,不能为同一接口注册多个类,因此如果要在这三个TestClass中实现ITestable,它将无法工作。发布在容器中注册的类的代码,以便我们更好地了解发生了什么。如果我只能为一个接口注册一个类,那么这有什么意义?我希望能够添加实现名称空间接口的类,然后自动解析它们。例如,通过获取具体类的列表,这就是为什么您有一个用名称注册每个接口的方法。如果您想为一个接口拥有3个不同的实例,那么必须使用该方法手动注册每个实例。很抱歉,这是你实现自己目标的唯一途径。你可能想看看MEF。它与框架一起提供,我认为它支持您开箱即用的功能。另一种方法是创建一个属性,使您能够指定实例的名称,然后您只需挂接Unity build策略并使用属性中给定的名称注册该特定实例。听起来非常值得一试。我一到家(现在在办公室)可能会看一看。我现在有点困惑。如果无法注册实现接口的类型,则按约定注册用于什么?谢谢,我真的很期待你的解决方案。这正是我一直在寻找的解决方案。非常感谢你。没问题,伙计,很抱歉花了这么长时间才找到解决办法。