2016-12-13 157 views
0

註冊泛型類型與Autofac我都Autofac和ASP.NET核心的一個相對較新的用戶。我最近將一個「經典」ASP.NET WebAPI項目的小項目移植到ASP.NET Core。我在Autofac上遇到了麻煩,特別是在註冊泛型類型時。問題在ASP.NET核心

此項目使用一個命令模式中,每個命令處理程序是一個封閉的通用等

public class UpdateCustomerCommandHandler: ICommandHandler<UpdateCustomerCommand> 

這些命令處理程序被注入到像控制器:

readonly private ICommandHandler<UpdateCustomerCommand> _updateCustomerCommand; 
public ValuesController(ICommandHandler<UpdateCustomerCommand> updateCustomerCommand) 
{ 
    _updateCustomerCommand = updateCustomerCommand; 
} 

Autofac構造(部分)作爲:

var builder = new ContainerBuilder(); 
var assemblies = AppDomain.CurrentDomain.GetAssemblies(); 
//This doesn't seem to be working as expected. 
builder.RegisterAssemblyTypes(assemblies) 
    .As(t => t.GetInterfaces() 
     .Where(a => a.IsClosedTypeOf(typeof(ICommandHandler<>))) 
     .Select(a => new KeyedService("commandHandler", a))); 

上面似乎沒有註冊t他如預期一般。如果我使用下面的方法註冊,它運作良好。

builder.RegisterType<UpdateCustomerCommandHandler>().As<ICommandHandler<UpdateCustomerCommand>>(); 

當我說「它不工作」,我的意思是,試圖實例化控制器的時候,我得到「InvalidOperationException異常:無法爲類型解析服務「BusinessLogic.ICommandHandler`1 [BusinessLogic。 UpdateCustomerCommand]',同時嘗試激活'AutoFac_Test.Controllers.ValuesController'「。

這在該項目的全部的WebAPI版效果不錯,但不是在ASP.NET核心重建之後。清楚的是,在移植到ASP.NET Core之前,這一切都非常完美。

這裏是我已經用來重建這個問題的代碼的鏈接: https://dl.dropboxusercontent.com/u/185950/AutoFac_Test.zip

****編輯解決方案DISCOVERED後****

中沒有任何事實與錯我的Autofac配置,當然不是Autofac本身。發生了什麼事是,我已經改名爲我的依賴組件的輸出,努力使裝配掃描的東西(取代的AppDomain.CurrentDomain.GetAssemblies()更優雅,但我從來沒有修改API項目的依賴,以引用新的組件,所以Autofac是掃描其發生的正確加載的程序集是舊版本,其中不包含接口和實現我的預期......

+0

的[可能的複製可我基於Open Generic的類型爲AutoFac創建一個鍵控服務(http://stackoverflow.com/questions/13636492/can-i-make-a-keyed-service-for-autofac-based-on-the-類型的開放式通用) – Tseng

+0

感謝您的建議,但這個問題不是重複的。在這種情況下,請求者正在尋找註冊從特定基礎派生的泛型並實現特定的封閉泛型接口。就我而言,我只是想註冊一個特定的封閉泛型接口的實現。而且,上面的語法在傳統的ASP.NET中已經證明可以很好地工作,但在ASP.NET Core中卻沒有。我想知道是否有可能導致這種情況的反射發生了變化? – QuietSeditionist

回答

0

Autofac已經內置支持註冊封閉類型的開放式通用。

builder 
    .RegisterAssemblyTypes(ThisAssembly) 
    .AsClosedTypesOf(typeof(ICommandHandler<>)); 

這將掃描您的裝配,發現關閉打開的通用ICommandHandler<>接口類型,並登記他們每個人的反對關閉他們實現的通用接口 - 在你的情況下,ICommandHandler<UpdateCustomerCommand>

什麼在你的例子不工作是你一鍵關聯您的服務。試圖實例化ValuesController,這就是爲什麼你得到的異常時Autofac不找你ICommandHandler<UpdateCustomerCommand>的密鑰版本。 QuietSeditionist的評論後

編輯:

我會盡量詳細闡述默認服務了一下。您註冊處理程序的方式是將commandHandler鍵與它們關聯。

這意味着,一旦集裝箱建成,這裏是可以解決這樣的處理程序的唯一方法:

// container will look for a registration for ICommandHandler<UpdateCustomerCommand> associated with the "commandHandler" key 
container.ResolveKeyed<ICommandHandler<UpdateCustomerCommand>>("commandHandler"); 

當實例ValuesController,Autofac不查找的ICommandHandler<UpdateCustomerCommand>一個鍵控註冊,因爲它沒有被要求。

它執行的等同的代碼 - 你可以嘗試自己運行代碼來獲取異常:

// BOOM! 
container.Resolve<ICommandHandler<UpdateCustomerCommand>>(); 

你的第二個註冊成功運作的原因是因爲你沒有關鍵服務:

// No key 
builder 
    .RegisterType<UpdateCustomerCommandHandler>() 
    .As<ICommandHandler<UpdateCustomerCommand>>(); 

// commandHandler key 
builder 
    .RegisterType<UpdateCustomerCommandHandler>() 
    .Keyed<ICommandHandler<UpdateCustomerCommand>>("commandHandler"); 

但既然你不想註冊所有的處理程序一個接一個,這裏是如何他們沒有鍵控他們註冊:

builder 
    .RegisterAssemblyTypes(ThisAssembly) 
    .AsClosedTypesOf(typeof(ICommandHandler<>)); 

/編輯

我可以看到兩個場景中鍵控服務可能是有用的:

  • 你有幾種類型實現相同的接口和要注入不同的實現在不同的服務。假設您將SqlConnectionDB2Connection都註冊爲IDbConnection。然後你有2個服務,其中一個應該是SQL Server的目標,另一個是DB2。如果它們都依賴於IDbConnection,則需要確保在每個服務中注入正確的一個。

  • 如果使用decorators,方式登記工作是你定義的服務,其裝飾將由關鍵應用 - 第一個例子是不言自明

+0

事實上,我在這裏使用了Decorator模式,所以我確實需要指定該密鑰。爲什麼Autofac不能找到這個密鑰版本?道歉,但它似乎並不像你的答案有一個答案。 – QuietSeditionist

+0

您確定使用裝飾模式嗎?原來的問題並沒有提到你使用它,並且鏈接到的.zip不包含裝飾器註冊或者ICommandHandler的裝飾器實現。 默認情況下,Autofac會嘗試解析* default *服務,即未明確*命名*或*鍵*的服務。如果你想要它來解決這些服務,你必須明確它。 –

+0

當我創建示例項目時,它是我的項目的簡化版本。當我刪除該行以將其註冊爲鍵控服務時,您是正確的,它已成功解決。我只是繼續前進,並添加了裝飾註冊,並且它按預期工作。在我的項目和提供的示例之間必須有別的不同。謝謝,我會努力重新創建並回復你。 – QuietSeditionist