10

我正在使用 Unity IoC 容器,我需要拦截对某个基本接口的任何对 Resolve 的调用,并运行我自己的自定义代码来构造这些类型。

换句话说,在下面的示例代码中,当我调用 时container.Resolve<IFooN>(),如果它没有具体实现类型的实例,则调用MyFactoryFunction构造一个实例,否则我希望它返回缓存的副本。

标准 Unity 容器无法构造这些对象(更新:因为它们是 .NET 远程处理对象,因此本地计算机上的任何程序集中都不存在具体类),我不想预先创建它们并且将它们与 RegisterInstance 一起存储。

interface IFoo : IBase { ... }
interface IFoo2 : IBase { ... }

...
container.Resolve<IFoo2>();

...
IBase MyFactoryFunction(Type t)
{
    ...
}

我假设我可以创建一个 Unity 扩展来执行此操作,但我想知道是否已经有可以借用的解决方案。

4

4 回答 4

8

为了完整起见,我应该添加另一个在 Unity 2 下有效的答案,因为我的另一个答案不再有效。由于您需要制定自定义构建器策略,因此涉及程度更高。感谢 Unity 项目的 ctavares,他们在这个线程上提供了很多帮助来实现这个:

public class FactoryUnityExtension : UnityContainerExtension
{
    private ICustomFactory factory;
    private CustomFactoryBuildStrategy strategy;

    public FactoryUnityExtension(ICustomFactory factory)
    {
        this.factory = factory;
    }

    protected override void Initialize()
    {
        this.strategy = new CustomFactoryBuildStrategy(factory, Context);
        Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
        Context.Policies.Set<ParentMarkerPolicy>(new ParentMarkerPolicy(Context.Lifetime), new NamedTypeBuildKey<ParentMarkerPolicy>());
    }
}

public class ParentMarkerPolicy : IBuilderPolicy
{
    private ILifetimeContainer lifetime;

    public ParentMarkerPolicy(ILifetimeContainer lifetime)
    {
        this.lifetime = lifetime;
    }

    public void AddToLifetime(object o)
    {
        lifetime.Add(o);
    }
}

public interface ICustomFactory
{
    object Create(Type t);
    bool CanCreate(Type t);
}

public class CustomFactoryBuildStrategy : BuilderStrategy
{
    private ExtensionContext baseContext;
    private ICustomFactory factory;


    public CustomFactoryBuildStrategy(ICustomFactory factory, ExtensionContext baseContext)
    {
        this.factory = factory;
        this.baseContext = baseContext;
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        var key = (NamedTypeBuildKey)context.OriginalBuildKey;

        if (factory.CanCreate(key.Type) && context.Existing == null)
        {
            context.Existing = factory.Create(key.Type);
            var ltm = new ContainerControlledLifetimeManager();
            ltm.SetValue(context.Existing);

            // Find the container to add this to
            IPolicyList parentPolicies;
            var parentMarker = context.Policies.Get<ParentMarkerPolicy>(new NamedTypeBuildKey<ParentMarkerPolicy>(), out parentPolicies);

            // TODO: add error check - if policy is missing, extension is misconfigured

            // Add lifetime manager to container
            parentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
            // And add to LifetimeContainer so it gets disposed
            parentMarker.AddToLifetime(ltm);

            // Short circuit the rest of the chain, object's already created
            context.BuildComplete = true;
        }
    }
}
于 2011-05-16T10:02:03.500 回答
5

更新此答案适用于 Unity 1.2。有关适用于 Unity 2 的解决方案,请参阅我的其他答案。


好的,我自己实现了扩展。在构建器中,我缓存对象,因为我希望它成为我的容器中的单例。原因baseContext是我希望它被缓存在顶级容器中,而不是在请求它的任何子容器中。

 public class FactoryMethodUnityExtension<T> : UnityContainerExtension
 {
     private Func<Type,T> factory; 

     public FactoryMethodUnityExtension(Func<Type,T> factory)
     {
         this.factory = factory;
     }

     protected override void Initialize()
     {
         var strategy = new CustomFactoryBuildStrategy<T>(factory, this.Context);
         Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);             
     }
 } 

 public class CustomFactoryBuildStrategy<T> : BuilderStrategy
 {
     private Func<Type,T> factory;
     private ExtensionContext baseContext;

     public CustomFactoryBuildStrategy(Func<Type,T> factory, ExtensionContext baseContext)
     {
        this.factory = factory;
        this.baseContext = baseContext;
     }

     public override void PreBuildUp(IBuilderContext context)
     {
         var key = (NamedTypeBuildKey)context.OriginalBuildKey;

         if (key.Type.IsInterface && typeof(T).IsAssignableFrom(key.Type))
         {
             object existing = baseContext.Locator.Get(key.Type);
             if (existing == null)
             {
                 // create it
                 context.Existing = factory(key.Type);
                 // cache it
                 baseContext.Locator.Add(key.Type, context.Existing);
             }
             else
             {
                 context.Existing = existing;
             }
         }
     }
 }

添加扩展非常简单:

MyFactory factory = new MyFactory();
container = new UnityContainer();
container.AddExtension(new FactoryMethodUnityExtension<IBase>(factory.Create));
于 2009-09-07T09:51:00.460 回答
5

Unity (v2) 允许您指定工厂。它允许几个不同的功能,包括使用类型来建立/命名等。简单的例子:

UnityContainer cont = new UnityContainer();

有一个简单的创建测试方法 - 但这可以用你想要的任何工厂进行扩展。
这将绕过正常的创建过程,即(为简洁起见)调用最长的构造函数,包括属性设置在内的所有后续行为仍将被执行。

cont.RegisterType<TestClass>(new InjectionFactory(c => CreateTest()));

var svc = cont.Resolve<TestClass>();
于 2013-03-13T13:54:33.523 回答
-1

Unity 容器已经充当了知道如何构造(并执行依赖注入)任意类型的工厂,因此接受 Type t 的工厂似乎是多余的。您能否详细说明为什么这是不可能的?

如果这确实是不可能的(很可能只是工作量太大),那么也许您可以将您的工厂注册到容器中?

于 2009-09-04T19:06:59.867 回答