private ILoginService<ApplicationUser> _loginService;
public AccountController()
{
_loginService = new EFLoginService()
}
public
AccountController(ILoginService<ApplicationUser> loginService)
{
_loginService = loginService;
}
把依賴的創建丟給其它人,自己只負責使用,其它人丟給你依賴的這個過程理解爲注入。
var controller = new AccountController(new EFLoginService());
controller.Login(userName, password);
// 用Redis來替換原來的EF登錄
var controller = new AccountController(new RedisLoginService());
controller.Login(userName, password);
var serviceCollection = new ServiceCollection()
.AddTransient<ILoginService, EFLoginService>()
.AddSingleton<ILoginService, EFLoginService>()
.AddScoped<ILoginService, EFLoginService>();
public interface IServiceCollection : IList<ServiceDescriptor>
{
}
我們上面的AddTransient、AddSignletone和Scoped方法是IServiceCollection的擴展方法, 都是往這個List裏面添加ServiceDescriptor。
private static IServiceCollection Add(
IServiceCollection collection,
Type serviceType,
Type implementationType,
ServiceLifetime lifetime)
{
var descriptor =
new ServiceDescriptor(serviceType, implementationType, lifetime);
collection.Add(descriptor);
return collection;
}
public enum ServiceLifetime
{
Singleton,
Scoped,
Transient
}
public interface IOperation
{
Guid OperationId { get; }
}
public interface IOperationSingleton : IOperation { }
public interface IOperationTransient : IOperation{}
public interface IOperationScoped : IOperation{}
我們的 Operation實現很簡單,可以在構造函數中傳入一個Guid進行賦值,如果沒有的話則自已New一個 Guid。
public class Operation :
IOperationSingleton,
IOperationTransient,
IOperationScoped
{
private Guid _guid;
public Operation() {
_guid = Guid.NewGuid();
}
public Operation(Guid guid)
{
_guid = guid;
}
public Guid OperationId => _guid;
}
在程序內我們可以多次調用ServiceProvider的GetService方法,獲取到的都是同一個實例。
var services = new ServiceCollection();
// 默認構造
services.AddSingleton<IOperationSingleton, Operation>();
// 自定義傳入Guid空值
services.AddSingleton<IOperationSingleton>(
new Operation(Guid.Empty));
// 自定義傳入一個New的Guid
services.AddSingleton <IOperationSingleton>(
new Operation(Guid.NewGuid()));
var provider = services.BuildServiceProvider();
// 輸出singletone1的Guid
var singletone1 = provider.GetService<IOperationSingleton>();
Console.WriteLine($"signletone1: {singletone1.OperationId}");
// 輸出singletone2的Guid
var singletone2 = provider.GetService<IOperationSingleton>();
Console.WriteLine($"signletone2: {singletone2.OperationId}");
Console.WriteLine($"singletone1 == singletone2 ? : { singletone1 == singletone2 }");
我們對IOperationSingleton註冊了三次,最後獲取兩次,大家要注意到我們獲取到的始終都是我們最後一次註冊的那個給了一個Guid的實例,前面的會被覆蓋。
這次我們獲取到的IOperationTransient爲兩個不同的實例。
var services = new ServiceCollection();
services.AddTransient<IOperationTransient, Operation>();
var provider = services.BuildServiceProvider();
var transient1 = provider.GetService<IOperationTransient>();
Console.WriteLine($"transient1: {transient1.OperationId}");
var transient2 = provider.GetService<IOperationTransient>();
Console.WriteLine($"transient2: {transient2.OperationId}");
Console.WriteLine($"transient1 == transient2 ? :
{ transient1 == transient2 }");
var services = new ServiceCollection()
.AddScoped<IOperationScoped, Operation>()
.AddTransient<IOperationTransient, Operation>()
.AddSingleton<IOperationSingleton, Operation>();
接下來我們用ServiceProvider.CreateScope方法創建一個Scope
var provider = services.BuildServiceProvider();
using (var scope1 = provider.CreateScope())
{
var p = scope1.ServiceProvider;
var scopeobj1 = p.GetService<IOperationScoped>();
var transient1 = p.GetService<IOperationTransient>();
var singleton1 = p.GetService<IOperationSingleton>();
var scopeobj2 = p.GetService<IOperationScoped>();
var transient2 = p.GetService<IOperationTransient>();
var singleton2 = p.GetService<IOperationSingleton>();
Console.WriteLine(
$"scope1: { scopeobj1.OperationId }," +
$"transient1: {transient1.OperationId}, " +
$"singleton1: {singleton1.OperationId}");
Console.WriteLine($"scope2: { scopeobj2.OperationId }, " +
$"transient2: {transient2.OperationId}, " +
$"singleton2: {singleton2.OperationId}");
}
接下來
scope1: 200d1e63-d024-4cd3-88c9-35fdf5c00956,
transient1: fb35f570-713e-43fc-854c-972eed2fae52,
singleton1: da6cf60f-670a-4a86-8fd6-01b635f74225
scope2: 200d1e63-d024-4cd3-88c9-35fdf5c00956,
transient2: 2766a1ee-766f-4116-8a48-3e569de54259,
singleton2: da6cf60f-670a-4a86-8fd6-01b635f74225
如果再創建一個新的Scope運行,
scope1: 29f127a7-baf5-4ab0-b264-fcced11d0729,
transient1: 035d8bfc-c516-44a7-94a5-3720bd39ce57,
singleton1: da6cf60f-670a-4a86-8fd6-01b635f74225
scope2: 29f127a7-baf5-4ab0-b264-fcced11d0729,
transient2: 74c37151-6497-4223-b558-a4ffc1897d57,
singleton2: da6cf60f-670a-4a86-8fd6-01b635f74225
大家注意到上面我們一共得到了 4個Transient實例,2個Scope實例,1個Singleton實例。
三、DI在ASP.NET Core中的應用
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ILoginService<ApplicationUser>,
EFLoginService>();
services.AddMvc();
)
ASP.NET Core的一些組件已經提供了一些實例的綁定,像AddMvc就是Mvc Middleware在 IServiceCollection上添加的擴展方法。
public static IMvcBuilder AddMvc(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
var builder = services.AddMvcCore();
builder.AddApiExplorer();
builder.AddAuthorization();
AddDefaultFrameworkParts(builder.PartManager);
...
}
private ILoginService<ApplicationUser> _loginService;
public AccountController(
ILoginService<ApplicationUser> loginService)
{
_loginService = loginService;
}
我們只要在控制器的構造函數裏面寫了這個參數,ServiceProvider就會幫我們注入進來。這一步是在Mvc初始化控制器的時候完成的,我們後面再介紹到Mvc的時候會往細裏講。
@using MilkStone.Services;
@model MilkStone.Models.AccountViewModel.LoginViewModel
@inject ILoginService<ApplicationUser> loginService
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
@loginService.GetUserName()
</body>
</html>
本文首發於公衆號jessetalk,