ASP.NET5設計的時候就是以DI為基礎的,它可以利用內建的框架在Startup類的方法中,把依賴注入進去。應用服務也可以被配置的注入。默認的服務容器提供一些基本的功能,它并不打算代替現代主流的DI框架。
1. 什么是Dependency Injection?
DI的概念相信大家已經了解了,不了解的可以查一下資料。我們來講一講ASP.NET 5內建的DI容器。
ASP.NET5包含一個簡單的內建容器,它的表現形式是IServiceProvider接口, 默認支持構造函數的注入,ASP.NET通過它注入相關的服務類。ASP.NET的容器引用的類型,在它里面叫做服務,在下面的內容當中,服務就當然于ASP.NET Ioc容器當中管理的類型。你可以通過Startup類中的CongureServices注入內建的服務。
2. 構架提供的服務
Startup類中的ConfigureServices方法定義了應用程序需要的服務,像Entity Framework, MVC等,它由IServiceCollection的擴展方法來添加. 例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework().AddSqlServer().AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
????? servies.AddMvc();
????? services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
3. 注冊你自己的服務
上面的代碼當中:
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
AddTransient方法用來添加抽象類型到具體的類型映射,并且申明了它的生命周期,在你注冊服務時,選擇合適的對象生命周期很重要。
我們來看一個例子:
public class CharactersController : Controller
{
private readonly ICharacterRepository _characterRepository;
public CharactersController(ICharacterRepository characterRepository)
{
_characterRepository = characterRepository;
}
public IActionResult Index()
{
var characters = _characterRepository.ListAll();
return View(characters);
}
}
public interface ICharacterRepository
{
? IEnumberable<Character> ListAll();
}
public class CharacterRepository : ICharacterRepository
{
private readonly ApplicationDbContext _dbContext;
public CharacterRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public IEnumerable<Character> ListAll()
{
return _dbContext.Characters.AsEnumberable();
}
}
注意CharacterRepository的構造函數當中需要一個ApplicationDbContext,像它這樣的注入方式并不常見,在每個申請當中,容器負責提供每一個對象的具體依賴對象。
在這個例子當中,ICharacterRepository和ApplicationDbContext都必須在ConfigureServices當中被注冊。
services.AddEntityFramework().AddSqlServer().AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration["Data:DefaultCOnnection:ConnectionString"]));
services.AddScoped<ICharacterRepository, CharacterRepository>();
EntityFramework context 應該被以Scoped的生命周期增加到容器當中,Repositories用到EntityFramework, 所以它應該使用同一個生命周期。
4. 服務的生命周期和注冊選項
ASP.NET 服務可以配置對象的生命周期如下所示:
Transient
它是意思每個請求都創建一個新的對象,這個適合輕量的,無狀態的服務。
Scoped
每個申請只創建一個對象。
Singleton
它是在首次被申請調用時創建一次, 以后所有的請求都會被重用,如果你的應用程序需要單例,推薦使用些方法,而不要自己實現單例模式。
Instance
它與Singleton相似 ,唯一的區別是,Instance在ConfigureServices的時候就創建了,而Singleton要在第一次請求的時候才創建。
5. 請求的服務和應用服務
ASP.NET當中服務在HttpContext的ApplicationServices和RequestServices中能夠得到
RequestServices里的服務是配置和請求你的應用程序的一部分,ApplicationServices里的服務是被限制在應用程序啟動的時候的服務,任何Scoped的應用程序都能在RequestServices得到,但是在ApplicationServices里得不到。當你的對象申明依賴時,這些依賴在RequestServices里能夠得到,在ApplicationServices里得不到,
一般地,你不需要直接用這些屬性,而可以通過構造函數注入。
6. 用DI設計你的服務
你應該用DI來設計你的應用,不要用函數狀態的靜態方法調用,或者直接地實例化你的服務。用DI ,你的類比較小,而且是靈活的,可測試的。
當你一個類依賴很多的時候,你就要意識到是否違反了單一職責原則。你可以重構你的代碼,把一些依賴移到其它的新類當中。注意在你Controler 類當中應該注意在UI上面,因此你的業務邏輯和數據訪問通過UI的職責的分來被相應地合理分開。
當用到數據訪問時,你可以注入EntityFramework的DbContext類型到你的controllers里面,不過首先你要確保EF在Startup類中被配置了,然而,避免在UI里直接使用DbContext, 你應該把它放到抽象當中去,例如Repository的接口中去。這樣可以減少你的應用和數據的耦合。也能使你的應用程序可以很容易地被測試。
7 替代默認的服務容器
在ASP.NET當中,你可以很容易地替代內建的服務容器,在ConfigureServices方法當中一般返回void, 但是如果它返回IServiceProvider, 一個不同的容器可以被返回,我們以autofac為例。
首選,你必須在project.json加如下的配置:
"dependencies":{
"Autofac": "4.0.0-beta8",
"Autofac.Framework.DependencyInjection": "4.0.0-beta8"
},
接下來,改寫ConfigureServices
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Add Autofac
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return container.Resolve<IServiceProvider>();
}
最后,配置Autofac在DefaultModule里面
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<CharacterRepository>.As<ICharacterRepository>();
}
}
現在,Autoface被用來生成你的服務在DI里面。
ASP.NET 5/DNX Containers
Autofac.Dnx???? http://autofac.org
StructureMap.Dnx?? http://structuremap.github.io
8. 建議
* DI用于復雜的依賴,控制器、服務、倉儲等
* 不要直接利用DI存儲數據和配置
* 不要靜態地訪問服務
* 不要在應用程序當中手動使用服務定位
* 不要靜態地訪問HttpContext
記住,不要把DI和static/global對應混用,否則你就感覺不到DI的好處了