Autofac 依賴注入小知識
Autofac 依賴注入小知識
控制反轉/依賴注入 IOC/DI
依賴介面而不依賴於實現,是面向物件的六大設計原則(SOLID)之一。即依賴倒置原則(Dependence Inversion Principle
)
生命週期分為三種,具體如下
Singleton
單例(全域性唯一例項)Scoped
範圍 (在同一個生命週期內是同一個例項)Transient
瞬時(每次請求都是一個新的例項)
使用說明
建立ASP.NET Core 3.0+
的專案,並安裝Autofac
包
dotnet add package Autofac.Extensions.DependencyInjection
在Program 中Host主機指定 .UseServiceProviderFactory(new AutofacServiceProviderFactory())
UseServiceProviderFactory呼叫Autofac提供程式,附加到通用宿主機制。
public class Program { public static void Main(string[] args) { var host = Host.CreateDefaultBuilder(args) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webHostBuilder => { webHostBuilder .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>(); }) .Build(); host.Run(); } }
在StartUp中配置
public class Startup { public Startup(IConfiguration configuration) { this.Configuration = configuration; } public IConfiguration Configuration { get; private set; } + public ILifetimeScope AutofacContainer { get; private set; } public void ConfigureServices(IServiceCollection services) { services.AddOptions(); } // ConfigureContainer is where you can register things directly // with Autofac. This runs after ConfigureServices so the things // here will override registrations made in ConfigureServices. // Don't build the container; that gets done for you by the factory. public void ConfigureContainer(ContainerBuilder builder) { // Register your own things directly with Autofac here. Don't // call builder.Populate(), that happens in AutofacServiceProviderFactory // for you. + builder.RegisterModule(new MyApplicationModule()); } public void Configure( IApplicationBuilder app, ILoggerFactory loggerFactory) { + this.AutofacContainer = app.ApplicationServices.GetAutofacRoot(); loggerFactory.AddConsole(this.Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseMvc(); } }
定義注入實現
public class MyApplicationModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
}
}
- 註冊泛型倉儲
builder.RegisterGeneric(typeof(AuditBaseRepository<>)).As(typeof(IAuditBaseRepository<>)).InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(AuditBaseRepository<,>)).As(typeof(IAuditBaseRepository<,>)).InstancePerLifetimeScope();
- 一個介面多個實現,使用Named,區分、引數為字串即可。
註冊服務
builder.RegisterType<IdentityServer4Service>().Named<ITokenService>(typeof(IdentityServer4Service).Name).InstancePerLifetimeScope();
builder.RegisterType<JwtTokenService>().Named<ITokenService>(typeof(JwtTokenService).Name).InstancePerLifetimeScope();
根據Name獲取哪個服務
private readonly ITokenService _tokenService;
public AccountController(IComponentContext componentContext, IConfiguration configuration)
{
bool isIdentityServer4 = configuration.GetSection("Service:IdentityServer4").Value?.ToBoolean() ?? false;
_tokenService = componentContext.ResolveNamed<ITokenService>(isIdentityServer4 ? typeof(IdentityServer4Service).Name : typeof(JwtTokenService).Name);
}
可通過appsettings.json中配置,可決定是哪個服務
"Service": {
"IdentityServer4": false
}
- 基於介面的注入
AsImplementedInterfaces
Specifies that a type from a scanned assembly is registered as providing all of its implemented interfaces.
指定將掃描程式集中的型別註冊為提供其所有實現的介面。
根據介面ITransientDependency
可以得到有哪些類繼承了此介面,並判斷是類,不是抽象類,不是泛型。
所有繼承類介面的類,將以介面的方式自動注入例項。可直接使用介面即可。
- InstancePerDependency 瞬時 (每次請求都是一個新的例項)
- InstancePerLifetimeScope 範圍(在同一個生命週期內是同一個例項)
- SingleInstance 單例(全域性唯一例項)
public class DependencyModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(r => r.FullName.Contains("LinCms.")).ToArray();
//每次呼叫,都會重新例項化物件;每次請求都建立一個新的物件;
Type transientDependency = typeof(ITransientDependency);
builder.RegisterAssemblyTypes(currentAssemblies)
.Where(t => transientDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsGenericType)
.AsImplementedInterfaces().InstancePerDependency();
//同一個Lifetime生成的物件是同一個例項
Type scopeDependency = typeof(IScopedDependency);
builder.RegisterAssemblyTypes(currentAssemblies)
.Where(t => scopeDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsGenericType)
.AsImplementedInterfaces().InstancePerLifetimeScope();
//單例模式,每次呼叫,都會使用同一個例項化的物件;每次都用同一個物件;
Type singletonDependency = typeof(ISingletonDependency);
builder.RegisterAssemblyTypes(currentAssemblies)
.Where(t => singletonDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract &&!t.IsGenericType)
.AsImplementedInterfaces().SingleInstance();
}
}
如果不寫繼承,如何批量注入呢。
1.類名有規則
2.基於特殊標籤
3.繼承介面。
- 類名有規則
比如倉儲字尾,全是Repository
,其中Assembly
為倉儲的實現所在程式集。將自動注入所有的倉儲,倉儲必須有介面。
Assembly assemblysRepository = Assembly.Load("LinCms.Infrastructure");
builder.RegisterAssemblyTypes(assemblysRepository)
.Where(a => a.Name.EndsWith("Repository"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
- 注入服務後就執行一段邏輯
builder.RegisterType<MigrationStartupTask>().SingleInstance();
builder.RegisterBuildCallback(async (c) => await c.Resolve<MigrationStartupTask>().StartAsync());
動態代理
dotnet add package Autofac.Extras.DynamicProxy
dotnet add package Castle.Core.AsyncInterceptor
- 服務註冊
AOP+屬性注入+以後綴為Service的服務實現,注入Scope 範圍的生命週期+啟用介面的攔截器。
- 使用
EnableInterfaceInterceptors
建立執行攔截的介面代理, - 使用
EnableClassInterceptors()
動態對子類進行重寫, 執行virtual方法的攔截
builder.RegisterType<UnitOfWorkInterceptor>();
builder.RegisterType<UnitOfWorkAsyncInterceptor>();
List<Type> interceptorServiceTypes = new List<Type>()
{
typeof(UnitOfWorkInterceptor),
};
Assembly servicesDllFile = Assembly.Load("LinCms.Application");
builder.RegisterAssemblyTypes(servicesDllFile)
.Where(a => a.Name.EndsWith("Service") && !a.IsAbstract && !a.IsInterface && a.IsPublic)
.AsImplementedInterfaces()//介面注入
.InstancePerLifetimeScope()//生命週期:範圍
.PropertiesAutowired()// 屬性注入
.InterceptedBy(interceptorServiceTypes.ToArray())//宣告攔截器
.EnableInterfaceInterceptors();//啟用介面的攔截器。
這二個類,請參考如下程式碼
- 同步:UnitOfWorkInterceptor.cs https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Middleware/UnitOfWorkInterceptor.cs
- 非同步攔截:UnitOfWorkAsyncInterceptor.cs https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Middleware/UnitOfWorkInterceptor.cs
Autofac.Extras.DynamicProxy
依賴Castle.Core,即只支援同步方法的攔截。
非同步方法的攔截需要安裝包:Castle.Core.AsyncInterceptor
。
- 非同步方法,分為有/無返回值:
async Task RunAsync()
,asyn Task<Result> RunAsync()
- 同步方法:
void Run()
,Result Run()
同步攔截
1.定義攔截器
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.Write("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
2.註冊攔截器。
// Named registration
builder.Register(c => new CallLogger(Console.Out))
.Named<IInterceptor>("log-calls");
// Typed registration
builder.Register(c => new CallLogger(Console.Out));
將攔截器與要攔截的型別 關聯
[Intercept(typeof(CallLogger))]
public class First
{
public virtual int GetValue()
{
// Do some calculation and return a value
}
}
// This attribute will look for a NAMED
// interceptor registration:
[Intercept("log-calls")]
public class Second
{
public virtual int GetValue()
{
// Do some calculation and return a value
}
}
連結
- 官網 https://autofac.org/
- GitHub https://github.com/autofac/Autofac
- 文件 https://autofac.readthedocs.io/en/latest/
作者: 、天上有木月OvO
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。如有問題或建議,請多多賜教,非常感謝。