代码之家  ›  专栏  ›  技术社区  ›  Mark Heath

统一的自定义对象工厂扩展

  •  10
  • Mark Heath  · 技术社区  · 15 年前

    我使用的是UnityIOC容器,我需要拦截任何解析某个基接口的调用,并运行我自己的自定义代码来构造这些类型。

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

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

    interface IFoo : IBase { ... }
    interface IFoo2 : IBase { ... }
    
    ...
    container.Resolve<IFoo2>();
    
    ...
    IBase MyFactoryFunction(Type t)
    {
        ...
    }
    

    我假设我可以创建一个统一扩展来实现这一点,但是我想知道是否已经有了一个解决方案可以借用。

    4 回复  |  直到 15 年前
        1
  •  8
  •   Mark Heath    13 年前

    为了完整性,我应该添加另一个在Unity2下工作的答案,因为我的另一个答案不再有效。因为您需要制定一个自定义的构建器策略,所以它稍微涉及一些。感谢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
  •  5
  •   Mark Heath    13 年前

    更新 这个答案是为了统一1.2。关于与Unity2一起工作的解决方案,请参阅我的另一个答案。


    好的,我自己已经实现了扩展。在构建器中,我缓存对象,因为我希望它是一个单独的w.r.t我的容器。原因 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));
    
        3
  •  5
  •   Peter    10 年前

    Unity(V2)允许您指定工厂。它允许两个不同的函数,包括使用类型构建/名称等。简单示例:

    UnityContainer cont = new UnityContainer();
    

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

    cont.RegisterType<TestClass>(new InjectionFactory(c => CreateTest()));
    
    var svc = cont.Resolve<TestClass>();
    
        4
  •  -1
  •   Jerry Bullard    15 年前

    Unity容器已经充当了一个知道如何构造(和执行)任意类型的工厂,因此接受T类型的工厂看起来是多余的。你能详细解释一下为什么这是不可能的吗?

    如果这真的是不可能的(更可能是太多的工作),那么也许你可以用容器来注册你的工厂?