一、依賴注入
引入依賴注入的目的是為了解耦和。說白了就是面向接口程式設計,通過調用接口的方法,而不直接執行個體化對象去調用。這樣做的好處就是如果添加了另一個種實作類,不需要修改之前代碼,隻需要修改注入的地方将實作類替換。上面的說的通過接口調用方法,實際上還是需要去執行個體化接口的實作類,隻不過不需要我們手動new 構造實作類,而是交給如微軟的DI、Autofac這些工具去建構實作類。我們隻需要告訴它們,某個類是某個接口的實作類,當用到的時候,工具會自動通過構造函數執行個體化類。
二、.Net Core中自帶的DI
本來想寫依賴注入源碼的講解的,看到網上有篇文章關于源碼講解的,很詳細、清楚,就不再寫了。位址:http://www.cnblogs.com/bill-shooting/p/5540665.html。我在這裡就說說使用吧。
依賴注入有三種生命周期,每種生命周期的注入方式大同小異,下面我以作用域生命周期舉例,其他兩種跟這個不同,我會特别說明。
下面為用到的兩個服務。
public class UserService : IUserService
{
public string GetName()
{
return "UserName";
}
}
public interface IUserService
{
string GetName();
}
public class ConfigReader : IConfigReader
{
private string configFilePath;//需要傳一個路徑,去讀取路徑下檔案的内容
public ConfigReader(string configFileName)
{
this.configFilePath = configFileName;
}
public string Reader()
{
return File.ReadAllText(configFilePath);
}
}
public interface IConfigReader
{
string Reader();
}
1、最常用的注入方式,以接口形式暴露服務
services.AddScoped(typeof(IUserService), typeof(UserService));
services.AddScoped<IUserService, UserService>();
兩種注入方式是一個意思,這種方式适合實作類為無參構造函數或者有參構造函數中參數已經被注入過了。
2、自己注入自己,以實作形式暴露服務
services.AddScoped<UserService>();
services.AddScoped(typeof(UserService));
這種注入方式适合隻有實作類,沒有借口類的注冊。
3、需要傳參的構造函數的類的注入
services.AddScoped<IConfigReader, ConfigReader>(x => { return new ConfigReader("c:/a.txt"); });
services.AddScoped<IConfigReader>(x => { return new ConfigReader("c:/a.txt"); });
services.AddScoped(typeof(IConfigReader), x => { return new ConfigReader("c:/a.txt"); });
前兩個匿名方法參數是IServiceProvider,傳回值為一個執行個體,第三個傳回值是Object。上面舉的例子沒有用到IServiceProvider ,下面再舉一個例子。修改上面的UserService類,将構造方法需要一個IConfigReader參數。
public class UserService : IUserService
{private IConfigReader configReader;
public UserService(IConfigReader configReader)
{
this.configReader = configReader;
}
public string GetName()
{
return "UserName" + configReader.Reader();
}
}
注冊的時候,如下:
services.AddScoped<IConfigReader, ConfigReader>(x => { return new ConfigReader("c:/a.txt"); });
//通過ServiceProvider擷取已經注冊的IConfigReader
services.AddScoped<IUserService, UserService>(x => { return new UserService(x.GetService<IConfigReader>()); });
//或者
services.AddScoped<IUserService, UserService>(x => { return new UserService(new ConfigReader("c:/a.txt")); });
單例類型的生命周期多了兩種注入方式:
services.AddSingleton<IConfigReader>(new ConfigReader("c:/a.txt"));
services.AddSingleton(typeof(IConfigReader), new ConfigReader("C:/a.txt"));
自帶的依賴注入工具也可以批量注入
var assembly = Assembly.GetExecutingAssembly()
.DefinedTypes
.Where(a => a.Name.EndsWith("Service") && !a.Name.StartsWith("I"));
foreach (var item in assembly)
{
services.AddTransient(item.GetInterfaces().FirstOrDefault(), item);
}
注意:當一個服務有多個實作時,調用的時候通過 IEnumerable<IPayService> PayServices 擷取所有的實作服務。
services.AddTransient<IPayService, AliPayService>();
services.AddTransient<IPayService, WeChatPayService>();
使用的時候:
三、Autofac
1、以接口形式暴露服務
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
var container = builder.Build();
return new AutofacServiceProvider(container);
}
2、通過實作類暴露服務
builder.RegisterType<UserService>();
builder.Register(c => new ConfigReader("c:/a.txt")).As<IConfigReader>();
4、通過程式集注入
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(c => c.Name.EndsWith("Service"))
.AsImplementedInterfaces();
總結:
不論是微軟的依賴注入元件還是Autofac 原理都是先将接口和對應的實作類注入到容器中,當要使用的時候,元件會自動通過構造函數建立執行個體。這裡有個問題如果有個實作類有多個構造函數,元件會找滿足參數最多的那個構造函數。