2017-04-13 12 views
0

如果我們將委託註冊爲組件,則AutowiringParam在解析時將採用與NamedParameter相同的優先級!Autofac無法在兩個構造函數之間進行解析(如果明確更具體)

這裏有一個歸結例如:

public class AParam { } 
public class BParam : IParam { } 

public interface IParam { } 

public interface IAThing { } 
public class AThing : IAThing 
{ 
    public AThing(AParam aParam) { } 
    public AThing(BParam anotherParam) { } 
} 

static void Main(string[] args) 
{ 
    IContainer c = (new ContainerBuilder()).Build(); 

    var anotherBuilder = new ContainerBuilder(); 
    anotherBuilder.RegisterType<AThing>().As<IAThing>().InstancePerDependency(); 
    anotherBuilder.Register((context, parm) => new BParam()).As<BParam>().InstancePerDependency(); 
    anotherBuilder.Update(c); 

    object aParam = new AParam(); 

    //Throws exception, it's unable to decide which constructor to use.... 
    var instance = c.Resolve(typeof(IAThing), new[] {new NamedParameter("aParam", aParam) }); 
} 

在這種情況下,我指定正是我想要的NamedParameter「aParam」,但AutowiritingParameter可以填寫在BParam,所以它不知道哪個構造函數可以選擇(因爲它們的參數長度都相等)。

我該怎麼做才能讓Autofac優先使用我提到的指定參數的特定構造函數?沒有理由爲什麼我想讓我的參數被忽略,如果它使用AutowiringParameter就會是這種情況。

我可以使用「UsingConstructor」,但只要BParam派生自AParam,我們就會回到相同的含糊之處。 在這裏,我明確要求具有命名參數的構造函數。 任何想法?

編輯:

使用在構造函數默認參數可以使Autofac忽略用戶指定的命名參數完全,沒有錯誤,你不會發現!

public class AThing : IAThing 
{ 
    public AThing(AParam aParam) { } 
    public AThing(BParam anotherParam, bool def = true) { } 
} 
+0

我注意到你的'Resolve'使用'typeof(IAThing)'但不是具體類'AThing'你試圖指定構造函數嗎?另外,[this](http://docs.autofac.org/en/latest/register/registration.html#selection-of-an-implementation-by-parameter-value)能爲您提供任何幫助嗎?它還說「如果創建實例的委託被聲明並且使用委託工廠,則可以實現清潔,類型安全的語法。」您可能可以通過代理和委託工廠解決此問題。 – ErikE

+0

嗨,Erik, 一般來說,我們使用Autofac進行依賴注入,所以它可能不是我們正在試圖解決的AThing(可能是測試模型),但絕對是帶有命名參數「aParam」的構造函數。 我不明白爲什麼Autofac使用「AutowiringParameter」作爲構造函數向「aParam」構造函數賦值相同的「特異性」(以下將其用於使用舊版本的Autofac):/它的某種忽略我想要一個特定的參數名稱(鍵入的參數結果相同的問題btw) 感謝您的回覆 – GettnDer

+1

這是您的應用程序組件具有多個構造函數的反模式,詳細解釋[這裏](https:// www。 cuttingedge.it/blogs/steven/pivot/entry.php?id=97)。 – Steven

回答

3

我不確定你在目前情況下能否真的贏。什麼是「明確更具體」給你不是必然 100%更具體 - 一旦你提供了參數,Autofac採用提供的參數以及自動佈線參數,並試圖確定,給出所有可用的信息,哪些構造函數它可以實現。所有參數相同 - 無論它們來自注冊時間(builder.RegisterType<T>().WithParameter(...)),解析時間(scope.Resolve<T>(...))還是自動裝配 - 應該調用哪個構造函數?

如果有優先級,比如參數X比參數Y更重要,它實際上可能開始變得非常複雜,並且創建一些難以排除故障的行爲。解決時間參數是否優先於註冊時間參數?你可以覆蓋這種行爲?爲什麼或者爲什麼不?它變得混亂。

無論如何,這就是爲什麼它的行爲如此並且可能會繼續以這種方式進入未來。

但是,你有兩個選擇:

選項1 - UsingConstructor:你只需指定在測試中使用的構造是一個需要BParam如果總是是怎麼回事。

builder.RegisterType<AThing>() 
    .As<IAThing>() 
    .UsingConstructor(typeof(BParam)); 

我會建議創建一個自定義IConstructorSelector潛在能力,但你沒有得到入參數那裏,只是一個構造函數列表可以根據已提供的一組參數滿足 。沒有足夠的信息繼續下去。有關更多信息,請參閱此答案的結尾。

選項2 - Lambda註冊:如果我是你,這可能是我會做的。它基於example in the docs here。它並不像一個班輪實現,但它可以讓你你想要的效果:這是不是漂亮

// This is the "default" behavior registration for when 
// no parameters are provided. Note it's named, though, so 
// the actual default registration for IAThing will be the 
// lambda. 
builder.RegisterType<AThing>().Named<IAThing>("default-thing"); 

// This is what will run when you Resolve<IAThing>() 
builder.Register((ctx, p) => { 
    var aorb = p 
    .OfType<NamedParameter>() 
    .Where(n => n.Name == "aParam") 
    .FirstOrDefault(); 
    if (aorb != null) 
    { 
    // You passed the parameter so use it. 
    return new AThing((BParam)aorb.Value); 
    } 
    else 
    { 
    // Use the default reflection-based registration, above. 
    return ctx.ResolveNamed<IAThing>("default-thing", p); 
    } 
}).As<IAThing>(); 

,但它能夠完成任務。如果你有很多這樣的內容,你可以將其中的一部分包裝到一個擴展方法中,該方法生成默認的註冊ID,處理if/else邏輯等等。


我認爲這可能是有價值的,以使更多的功能構造選擇,以便UsingConstructor可以做得更多。爲此,我opened up this issue for you看看,如果這將是一個有趣的增強。

+0

我認爲明顯有更高的「特異性」,因爲我想明確使用NamedParameter進行解析。如果我沒有,我會不會感到驚訝,如果我得到一個異常(或者在這種情況下不是真的,因爲唯一的構造函數是BParam)。自動填充已經試圖抓住最具體的構造函數(通過更高的長度),但在這種情況下,具有兩個自動佈線參數的長度爲2的構造函數將贏得我的構造函數,並帶有一個在解析調用中明確命名的參數。 我想在CSS選擇器中使用相同的特殊性規則,我不希望它神奇地工作。但一個想法 – GettnDer

+0

我認爲這可以用任何方式來論證,但我的解釋的重點是解釋它爲什麼是這樣,而不是開始一個側面討論。我希望我的回答有幫助。 –

+0

它的確如此,不幸的是,這個應用程序是一個支持很多潛在的野獸並且做出巨大修改的野獸。老版本的Autofac曾經工作過,有人更新過Autofac,我只好忍受支持這個更新的子彈,我不會這樣做自己。 我回復了你的票,非常感謝你的時間 – GettnDer

相關問題