2009-09-04 26 views
10

我正在使用Unity IoC容器,并且需要拦截任何调用以解析某个基本接口,然后运行我自己的自定义代码来构造这些类型。Unity的自定义对象工厂扩展

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

标准统一容器无法构造这些对象(更新:,因为它们是.NET远程对象,所以具体的类不会在本地计算机上的任何组件存在),而我不希望先创建它们并用RegisterInstance存储它们。

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

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

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

我假设我能创造一个团结的扩展要做到这一点,但我不知道是否已经有一个解决方案在那里我可以借用。

回答

8

为了完整起见,我要补充的另一个答案是统一2下工作,因为我的其他答案不再起作用。由于您需要制定自定义构建策略,因此涉及的内容略有增加。由于从谁在执行这一规定很多的帮助上this thread统一项目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; 
     } 
    } 
} 
-1

Unity容器已经充当了知道如何为任意类型构造(并执行依赖注入)的工厂,所以接受类型t的工厂看起来是多余的。你能详细说明为什么这是不可能的吗?

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

+0

原因是我们使用.NET Remoting来创建类。所以我们不能简单地调用它的构造函数 - 客户端上的任何程序集中都不存在具体类型。我们必须连接到服务器,并请求它的对象来满足有问题的接口。 – 2009-09-04 20:08:10

5

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


好的,我自己实现了扩展。在构建器中,我缓存对象,因为我希望它是一个单例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)); 
+0

你能解释一下你的缓存策略吗?为什么需要在创建后向容器添加创建的类型? – 2011-02-13 00:50:35

+0

@Dmitriy - 因为我只想创建每个.NET远程接口一次 – 2011-02-14 15:27:09

5

统一(V2)允许您指定一个工厂。它允许一对夫妇不同的功能,包括采取类型建立/名称等简单的例子:

UnityContainer cont = new UnityContainer(); 

有一个简单的创建测试方法 - 不过这可以通过任何你想要的工厂进行扩展。
这将规避正常的创建过程(为了简洁起见)称为 最长的构造函数 所有后来的行为(包括属性设置)仍将被执行。

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

var svc = cont.Resolve<TestClass>(); 
+0

看起来您必须使用RegisterType的一般形式 brendanjerwin 2013-09-05 15:36:16