2014-02-05 15 views
2

我在單個Web應用程序下運行多個多語言「網站」。每個「語言環境」都有自己的Solr核心(在所有語言環境中都有相同的字段)。我正在使用SolrNet來執行我的搜索查詢。消除工廠中的IoC容器耦合

爲了切換在每個請求的基礎核心,我註冊命名實例在我的每個區域設置Autofac容器ISolrOperations<SearchResult>(對於那些不熟悉SolrNet,這實際上是我的入口點爲庫查詢目的)。

然後我有一個ISearchService接口,一個具體SolrSearchService實現,它的構造簽名如下所示:

public SolrSearchService(ISolrOperations<SearchResult> solr) 

爲了動態切換對每個請求內核,我控制器需要一個(注入) ISearchServiceFactory而不是簡單的ISearchService。我SolrSearchServiceFactory看起來是這樣的:

public class SolrSearchServiceFactory : ISearchServiceFactory 
{ 
    private readonly IComponentContext _ctx; 

    public SolrSearchServiceFactory(IComponentContext ctx) 
    { 
     _ctx = ctx; 
    } 

    public ISearchService Create(string id) 
    { 
     var result = _ctx.ResolveNamed<ISolrOperations<SearchResult>>(id); 
     return new SolrSearchService(result); 
    } 
} 

id簡直是區域設置標識符。這是我爲了將Autofac從我的服務/控制器中分離出來而設法做的最好的,但是保持了每個請求(基於控制器中執行的邏輯)切換內核的能力。

但是,我仍然不太滿意,因爲工廠本身仍然與Autofac耦合。有沒有辦法解決這個問題(從SolrNet或Autofac的角度來看)?

我看過使用Autofac的工廠代表,但似乎沒有辦法在這種情況下應用它們。

+1

您是否絕對需要命名實例? – DavidN

+0

@DavidN不一定命名的實例,但是我需要一種切換同一類型實例的方法(使用相同的類型參數)。我一直在試圖切換單個SolrNet連接的端點,但我不想回頭!本質上,實際的端點URL被注入到ISolrOperations的依賴關係的依賴中,並且庫本身的引導非常繁重,所以在提供的DI模塊之外的每個請求上構建並不是很容易。 –

回答

1

您可以使用鍵控/命名服務和IIndex來做到這一點,在autofac頁面上有一個很好的條目here

下面是它什麼樣子一個簡單的例子:

public class Factory 
    { 
     private readonly IIndex<string, ISearchService> _index; 

     public Factory(IIndex<string, ISearchService> index) 
     { 
      _index = index; 
     } 

     public ISearchService Resolve(string id) 
     { 
      return _index[id]; 
     } 
    } 

     // Registrations 
     var builder = new ContainerBuilder(); 
     builder.Register<ASearchService>(c => new ASearchService()).Keyed<ISearchService>("A"); 
     builder.Register<BSearchService>(c => new BSearchService()).Keyed<ISearchService>("B"); 
     builder.Register<Factory>(c => new Factory(c.Resolve<IIndex<string, ISearchService>>())); 
     // as per autofac's page linked above, autofac automatically implements IIndex types 

     // Usage 

     var aSearchService = fact.Resolve("A"); 

編輯:我想我們可以做的比這更好的,所以這裏有一對夫婦的擴展方法來避免一個明確的工廠自創建類:

public delegate TReturn AutoFactoryDelegate<TParam, TReturn>(TParam param); 

    public class AutoFactory<TParam, TReturn> 
    { 
     private readonly IIndex<TParam, TReturn> _index; 

     public AutoFactory(IIndex<TParam, TReturn> index) 
     { 
      _index = index; 
     } 

     public TReturn Resolve(TParam param) 
     { 
      return _index[param]; 
     } 
    } 

    public delegate TReturn AutoFactoryDelegate<TParam, TReturn>(TParam param); 

    public static class AutofacExtensions 
    { 
     public static void RegisterAutoFactoryDelegate<TParam, TReturn>(this ContainerBuilder builder) 
     { 
      builder.Register(c => new AutoFactory<TParam, TReturn>(c.Resolve<IIndex<TParam, TReturn>>())); 

      builder.Register<AutoFactoryDelegate<TParam, TReturn>>(c => 
       { 
       var fact = c.Resolve<AutoFactory<TParam, TReturn>>(); 
       return fact.Resolve; 
       }); 
     } 
    } 

// Registration 
var builder = new ContainerBuilder(); 
builder.Register<ASearchService>(c => new ASearchService()).Keyed<ISearchService>("A"); 
builder.Register<BSearchService>(c => new BSearchService()).Keyed<ISearchService>("B"); 
builder.RegisterAutoFactoryDelegate<string, ISearchService>(); 

// Usage 
// fact is an AutoFactoryDelegate<string, ISearchService> 
var aSearchService = fact("A"); 

編輯2:這再次尋找之後,我們不需要工廠類,因爲IIndex實際上是工廠了。這導致了一個相當簡化從實施方:

public static void RegisterAutoFactoryDelegate<TParam, TReturn>(this ContainerBuilder builder) 
{ 
    builder.Register(c => 
         { 
          var iindex = c.Resolve<IIndex<TParam, TReturn>>(); 
          return (Func<TParam, TReturn>) (id => iindex[id]); 
         }); 
} 

// Registration 
var builder = new ContainerBuilder(); 
builder.Register<ASearchService>(c => new ASearchService()).Keyed<ISearchService>("A"); 
builder.Register<BSearchService>(c => new BSearchService()).Keyed<ISearchService>("B"); 
builder.RegisterAutoFactoryDelegate<string, ISearchService>(); 

// Usage 
// fact is an Func<string, ISearchService> 
var aSearchService = fact("A"); 
+0

這與我在汽車工廠的想法是一致的,很可能是缺失的環節 - 我明天再看看並回復你。謝謝! –

+0

不*完全*我最終做的,但指出我在正確的方向。我註冊了'Func鍵<字符串,ISearchService>'只是調用'c.ResolveNamed >(ID)'和實例SolrSearchService'的'一個新的實例。注入到我現有的工廠,所以我的控制器不採取Func <字符串,ISearchService>(我認爲是一個有點醜陋)。 –

+0

如果我正確理解你的解決方案,那麼你有一個關於c關閉的Func ,它在autofac中是一個no-no。不知怎的,你有沒有想過? – DavidN

1

我沒有使用Autofac,但基於溫莎是怎麼做的,什麼我剛剛發現,你應該能夠做這樣的事

builder.Register(c => 
    new SolrSearchServiceFactory(HttpContext.Current.GetIdFromUrlOrSomething) 
    ) 
    .As<ISearchServiceFactory>() 
    .InstancePerHttpRequest(); 

如果您可以從http上下文獲取Id,那麼autofac應該爲每個請求連線工廠。

/Michael