2009-09-04 27 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