【问题标题】:Custom object factory extension for UnityUnity 的自定义对象工厂扩展
【发布时间】:2010-11-25 16:58:40
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

    标签: c# unity-container ioc-container unity2.0


    【解决方案1】:

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

    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;
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      更新此答案适用于 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));
      

      【讨论】:

      • 您能解释一下您的缓存策略吗?为什么需要在创建后将创建的类型添加到容器中?
      • @Dmitriy - 因为我只想创建每个 .NET 远程处理接口一次
      【解决方案3】:

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

      UnityContainer cont = new UnityContainer();
      

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

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

      【讨论】:

      • 看来你必须使用RegisterType的通用形式
      【解决方案4】:

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

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

      【讨论】:

      • 原因是我们使用 .NET 远程处理来创建类。所以我们不能简单地调用它的构造函数——具体类型不存在于客户端的任何程序集中。我们必须连接到一个服务器,并请求它的对象来满足相关接口。
      猜你喜欢
      • 2011-04-20
      • 1970-01-01
      • 1970-01-01
      • 2016-04-22
      • 1970-01-01
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多