C# 团结。在InjectionFactory中返回泛型类型

C# 团结。在InjectionFactory中返回泛型类型,c#,unity-container,C#,Unity Container,在WebAPI项目中,我有两个相同通用接口的不同实现: public interface Interface<T> {} public class Class1<T> : Interface<T> {} where T : class public class Class2<T> : Interface<T> {} where T : class 有没有办法实现container.Resolve(typeTo1)以便返回一个封闭类型

在WebAPI项目中,我有两个相同通用接口的不同实现:

public interface Interface<T> {}

public class Class1<T> : Interface<T> {} where T : class
public class Class2<T> : Interface<T> {} where T : class

有没有办法实现
container.Resolve(typeTo1)
以便返回一个封闭类型实例?我不想为我拥有的每个posible类型重写register语句

看来你把解决方案复杂化了。相反,一种更简单的方法是不将其推入IoC容器。原因是DI是为了维护独立于依赖项及其逻辑的逻辑

在本例中,您有一个确定依赖项堆栈的开关,然后将其推入解决方案,在容器和实际逻辑之间创建依赖项。这可能会导致问题,因为它只是在推动解决问题,而不是实际解决问题。在这种情况下,确定开关的逻辑可能非常适合对象分辨率(这是该逻辑最有可能做的……什么是“切换”) 看起来,如果你有一个逻辑块中的开关,它应该是决定解决的第一步的所有者。当然,您仍然会使用一些IoC作为具体类解析的最终接口,但不要将开关逻辑推送到IoC

换句话说,您是否可以拥有另一层接口,IoC在其上解析,然后调用解析程序为基本接口使用类型开关

IBaseInterface resolvedObject = Toggle.IsOn(request) ? di.Get<IToggledInterface>() : di.Get<INotToggledInterface>();
IBaseInterface resolvedObject=Toggle.IsOn(请求)?di.Get():di.Get();

注:正在解决的接口使用IBaseInterface进行最终曝光。它们都实现了接口,但是它们自己除了“类型”之外可能不会给符号添加任何东西允许IoC解决问题的定义。

有几种方法可以用来获得您想要的结果,但较简单的方法无法很好地扩展,因为逻辑变得更复杂,类的数量也在增加。我在考虑按约定注册、有条件注册和命名注册

Unity允许您注册开放泛型,并自动将它们解析为封闭泛型。但是,使用
InjectionFactory
的发布方法不起作用,因为
InjectionFactory
没有Unity用来创建封闭泛型类型的解析
BuildContext

实现所需功能的一个解决方案是使用Unity容器扩展,因为容器扩展将具有可用的构建上下文,并允许您确定要构建的正确类型。在这种情况下,可以在对象解析的TypeMapping阶段完成简单的类型映射

public class ToggleExtension : UnityContainerExtension
{
    private Toggle toggle;

    public ToggleExtension(Toggle toggle)
    {
        this.toggle = toggle;
    }

    protected override void Initialize()
    {
        Context.Strategies.Add(new ToggleBuildUpStrategy(this.toggle),
            UnityBuildStage.TypeMapping);
    }
}

public class ToggleBuildUpStrategy : BuilderStrategy
{
    private Toggle toggle;
    public ToggleBuildUpStrategy(Toggle toggle)
    {
        this.toggle = toggle;
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        // If we have an Interface<> then do some type mapping to the applicable concrete class
        // Note that I'm using Toggle here because something similar was used in the question
        if (context.BuildKey.Type.IsGenericType && 
            context.BuildKey.Type.GetGenericTypeDefinition() == typeof(Interface<>))
        {
            Type target = null;

            // Luckily HttpContext.Current.Request request context is available here
            // For other non-static contexts might have to work out how to inject into the extension
            if (this.toggle.IsOn(HttpContext.Current.Request))
            {
                target = typeof(Class1<>);
            }
            else
            {
                target = typeof(Class2<>);
            }
            // Get generic args
            Type[] argTypes = context.BuildKey.Type.GetGenericArguments();

            // Replace build key type Interface<> => Class1<> or Class2<>
            // So that the correct type is resolved
            context.BuildKey = new NamedTypeBuildKey(target.MakeGenericType(argTypes), 
                context.BuildKey.Name);
        }
    }
}
最后,进行一些测试,以确保解决正确的类型:

IUnityContainer container = new UnityContainer();
Toggle toggle = new Toggle();
toggle.SetToggleOn();

container.AddExtension(new ToggleExtension(toggle));

Interface<X> x = container.Resolve<Interface<X>>();
Debug.Assert(x.GetType() == typeof(Class1<X>));

toggle.SetToggleOff();

x = container.Resolve<Interface<X>>();
Debug.Assert(x.GetType() == typeof(Class2<X>));
IUnityContainer container=newunitycontainer();
切换切换=新切换();
toggle.SetToggleOn();
container.AddExtension(新的ToggleExtension(toggle));
接口x=container.Resolve();
Assert(x.GetType()==typeof(Class1));
toggle.SetToggleOff();
x=container.Resolve();
Assert(x.GetType()==typeof(Class2));

我认为您试图实现的目标是不可能的……您需要注册所有可能的类型。@Viru,您是对的,开箱即用的Unity不会实现OP想要的功能,但只需编写一点代码,您就可以将请求的功能添加到容器中。看到我的答案了。我明白你的意思,但问题是,依赖性在很多已经完成的控制器中使用,我无法更改。切换中没有业务逻辑,它是一种功能切换。我正在替换一些类,我需要能够决定运行时和每个请求使用哪个实现。这就是为什么我需要能够在容器中配置切换。在删除当前类之前,我需要在生产环境中的一组受控请求中测试新的实现。你的方法是对的,但是错误告诉你到底哪里出了问题。您的类型以中堂泛型符号传递,而解析器无法知道它应该为该泛型解析什么。从代码中不清楚泛型是什么或用于什么。我猜这个问题的一些细节仍然不见了,但解决办法是去掉泛型,因为它看起来没有必要。如果有必要,您还需要解释其目的。这就像在我的列表中声明一个列表;泛型是在编译时解析的,而不是在运行时完美地工作!谢谢你,兰迪!
public class ToggleExtension : UnityContainerExtension
{
    private Toggle toggle;

    public ToggleExtension(Toggle toggle)
    {
        this.toggle = toggle;
    }

    protected override void Initialize()
    {
        Context.Strategies.Add(new ToggleBuildUpStrategy(this.toggle),
            UnityBuildStage.TypeMapping);
    }
}

public class ToggleBuildUpStrategy : BuilderStrategy
{
    private Toggle toggle;
    public ToggleBuildUpStrategy(Toggle toggle)
    {
        this.toggle = toggle;
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        // If we have an Interface<> then do some type mapping to the applicable concrete class
        // Note that I'm using Toggle here because something similar was used in the question
        if (context.BuildKey.Type.IsGenericType && 
            context.BuildKey.Type.GetGenericTypeDefinition() == typeof(Interface<>))
        {
            Type target = null;

            // Luckily HttpContext.Current.Request request context is available here
            // For other non-static contexts might have to work out how to inject into the extension
            if (this.toggle.IsOn(HttpContext.Current.Request))
            {
                target = typeof(Class1<>);
            }
            else
            {
                target = typeof(Class2<>);
            }
            // Get generic args
            Type[] argTypes = context.BuildKey.Type.GetGenericArguments();

            // Replace build key type Interface<> => Class1<> or Class2<>
            // So that the correct type is resolved
            context.BuildKey = new NamedTypeBuildKey(target.MakeGenericType(argTypes), 
                context.BuildKey.Name);
        }
    }
}
public class Toggle
{
    private bool isToggleOn;

    public void SetToggleOn()
    {
        isToggleOn = true;
    }

    public void SetToggleOff()
    {
        isToggleOn = false;
    }

    public bool IsOn(HttpRequest request)
    {
        // Implement more complicated toggle logic
        return isToggleOn;
    }
}
IUnityContainer container = new UnityContainer();
Toggle toggle = new Toggle();
toggle.SetToggleOn();

container.AddExtension(new ToggleExtension(toggle));

Interface<X> x = container.Resolve<Interface<X>>();
Debug.Assert(x.GetType() == typeof(Class1<X>));

toggle.SetToggleOff();

x = container.Resolve<Interface<X>>();
Debug.Assert(x.GetType() == typeof(Class2<X>));