2017-03-20 72 views
1

我認爲這個問題不會重複Autofac: Resolving dependencies with parameters。請閱讀下面的目的部分。需要幫助,我想將參數傳遞給相關組件

我宣佈一個IContentProvider和一個名爲WebContentProviderurl作爲構造函數的參數執行。

interface IContentProvider 
{ 
    string Content { get; } 
} 

class WebContentProvider { 
    public WebContentProvider(string url) { ... } 
    public string Content => GetHtmlFrom(this.url); 
} 

現在,有兩個消費者類需要WebContentProvder來提供來自不同url的內容。它們全部實現接口IContentUser

class FirstContentUser : IContentUser 
{ 
    IContentProvider provider; 

    public FirstContentUser(IContentProvider provider) 
    { 
     this.provider = provider; 
    } 

    public void Use() 
    { 
     Console.WriteLine("[First Content User]"); 
     Console.WriteLine(provider.Content); 
    } 
} 

SecondContentUser與此類似。

這裏是我的註冊代碼:

public AttempAutofac() 
{ 
    var builder = new ContainerBuilder(); 

    builder.RegisterType<WebContentProvider>() 
     .As<IContentProvider>(); 
     // HERE!!! how can I use `url` metadata given below, 
     // or some other way to get the url while resolving 

    builder.RegisterType<FirstContentUser>() 
     .As<IContentUser>() 
     .WithMetadata("url", "http://the.1st.url"); 

    builder.RegisterType<SecondContentUser>() 
     .As<IContentUser>() 
     .WithMetadata("url", "http://the.2nd.url"); 

    container = builder.Build(); 
} 

後來,我想用scope.Resolve<IEnumerable<IContentUser>>()讓所有消費者實例。

我希望在不久的將來,只需更改一個註冊行,就可以輕鬆地將新類FasterWebContentProvider配置爲代替WebContentProvider

這是我的目的:

  1. Autofac用於根據接口組成的實施方式。我希望一切都在註冊中決定,而不是在解決階段。這意味着消費者提供資源位置,然後Autofac選擇IContentProvider實施從該位置加載內容(資源),並將內容提供給特定的消費者。

  2. 所有接口和實現都是預定義的,所以我不能修改它們(不能使用「Func隱式關係」方式)。

  3. 解析IContentProvider的參數是動態的,來自不同的消耗,不僅僅是配置值。所以我不能使用該答案中提到的「lambda表達式註冊」方式。

  4. 也許Autofac模塊可以解決問題,但我不知道該怎麼做。

我不清楚我的設計是否正確。我會感謝能指出如何改進設計的人。

+1

作爲一個側面說明,如果你是手動調用scope.Resolve在代碼中的某個點你已經掉進了服務定位器反模式的陷阱。 –

+2

[Autofac:解決與參數的依賴關係]的可能重複(http://stackoverflow.com/questions/26327177/autofac-resolving-dependencies-with-parameters) –

回答

0

我思考過這個問題再弄清楚一兩件事:

IContentProvider的情況下,應該沒有自己的消費者被創建,所以他們不能得到消費者的任何信息。因此,我必須註冊不同的IContentProvider實例或創建者,那麼消費者可以通過鍵或名稱找到一個實例或創建者。

'How do I pick a service implementation by context?',我得到了一個解決方案:

private void Register() 
{ 
    var builder = new ContainerBuilder(); 

    builder.Register(c => new WebContentProvider("http://the.1st.url")) 
     .Keyed<IContentProvider>(typeof(FirstContentUser)); 

    builder.Register(c => new WebContentProvider("http://the.2nd.url")) 
     .Keyed<IContentProvider>(typeof(SecondContentUser)); 

    builder.RegisterType<FirstContentUser>() 
      .As<IContentUser>() 
      .WithParameter(
      CreateResolvedParameter<IContentProvider, FirstContentUser>()); 

    builder.RegisterType<SecondContentUser>() 
     .As<IContentUser>() 
      .WithParameter(
      CreateResolvedParameter<IContentProvider, SecondContentUser>()); 

    container = builder.Build(); 
} 

private static ResolvedParameter CreateResolvedParameter<ParamType, KeyType>() 
{ 
    return new ResolvedParameter(
     (pi, ctx) => pi.ParameterType == typeof(ParamType), 
     (pi, ctx) => ctx.ResolveKeyed<ParamType>(typeof(KeyType))); 
}