.net core發布有一段時間了,最近兩個月開始使用.net core2.0開發項目,大大小小遇到了一些問題。准備寫個系列介紹一下是如何解決這些問題以及對應技術。先從IOC容器Autofac開始該系列。
閱讀目錄
Autofac基本使用
Autofac是一款輕量級的IOC框架,使用率上還是挺高的,官方網站http://autofac.org,源碼下載地址https://github.com/autofac/Autofac。
下面以狗的列子來介紹autofac,nuget搜索Autofac進行安裝

public interface IDog { /// <summary> /// 品種 /// </summary> string Breed { get; } /// <summary> /// 名稱 /// </summary> string Name { get; } } /// <summary> /// 薩摩耶 /// </summary> public class Samoyed : IDog { /// <summary> /// 品種 /// </summary> public string Breed { get { return "Samoyed(薩摩耶)"; } } /// <summary> /// 名稱 /// </summary> public string Name { get { return "小黃"; } } } /// <summary> /// 藏獒 /// </summary> public class TibetanMastiff : IDog { /// <summary> /// 品種 /// </summary> public string Breed { get { return "Mastiff Class(獒犬類)"; } } /// <summary> /// 名稱 /// </summary> public string Name { get { return "小黑"; } } }
1.RegisterType
public static void Register() { var builder = new ContainerBuilder(); //注冊Samoyed指定為IDog實現 builder.RegisterType<Samoyed>().As<IDog>(); builder.RegisterType<TibetanMastiff>().As<IDog>(); using (var container = builder.Build()) { var dogs = container.Resolve<IEnumerable<IDog>>(); foreach (var dog in dogs) { Console.WriteLine($"名稱:{dog.Name},品種:{dog.Breed}"); } } }
public static void RegisterAssemblyTypes() { var builder = new ContainerBuilder(); //注冊程序集下所有類型 builder.RegisterAssemblyTypes(typeof(Program).Assembly).AsImplementedInterfaces(); using (var container = builder.Build()) { var dogs = container.Resolve<IEnumerable<IDog>>(); foreach (var dog in dogs) { Console.WriteLine($"名稱:{dog.Name},品種:{dog.Breed}"); } } }
直接注冊程序集下的所有類型,AsImplementedInterfaces(讓具體實現類型,可以該類型繼承的所有接口類型找到該實現類型)
3.RegisterInstance
TibetanMastiff d = new TibetanMastiff(); builder.RegisterInstance(d).As<IDog>();
4.RegisterModule
這種模式需要使用配置文件進行注冊,個人更喜歡代碼直接注冊的方式,畢竟配置文件修改容易遺忘和出錯。這里就不介紹該方式了。
遺留問題:上面的注冊代碼,自己寫寫demo的時候沒啥問題。但是運用到項目里面就很繁瑣了,需要自己一個個類型注冊,后面會提供解決方案。
.net core MVC與Autofac
1.首先nuget下載Autofac和Autofac.Extensions.DependencyInjection
引用
2.替換mvc自帶的DI框架
將Startup.cs中的ConfigureServices
返回類型改為IServiceProvider
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); var builder = new ContainerBuilder(); builder.Populate(services); builder.RegisterAssemblyTypes(typeof(Startup).Assembly).AsImplementedInterfaces(); var Container = builder.Build(); return new AutofacServiceProvider(Container); }
屬性注入
Autofac默認是構造函數注入
[Route("api/[controller]")] public class ValuesController : Controller { private readonly IEnumerable<IDog> dogs; public ValuesController(IEnumerable<IDog> _dogs) { dogs = _dogs; } // GET api/values [HttpGet] public IEnumerable<string> Get() { List<string> list = new List<string>(); foreach (var dog in dogs) { list.Add($"名稱:{dog.Name},品種:{dog.Breed}"); } return list.ToArray(); ; } }

var IControllerType = typeof(ControllerBase); builder.RegisterAssemblyTypes(assembly).Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired();
上面這段代碼的解釋:注冊所有程序集下繼承ControllerBase的類型,PropertiesAutowired 允許屬性注入。
2.替換系統默認Controller創建器
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); services.AddMvc();
)
[Route("api/[controller]")] public class ValuesController : Controller { public IEnumerable<IDog> dogs { get; set; } [HttpGet] public IEnumerable<string> Get() { List<string> list = new List<string>(); foreach (var dog in dogs) { list.Add($"名稱:{dog.Name},品種:{dog.Breed}"); } return list.ToArray(); ; } }
Autofac+Castle實現AOP
public class LogInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("你正在調用方法 \"{0}\" 參數是 {1}... ", invocation.Method.Name, string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())); invocation.Proceed(); if (invocation.ReturnValue != null && invocation.ReturnValue is string) { //在返回接口上拼上LogInterceptor invocation.ReturnValue += " LogInterceptor"; } Console.WriteLine("方法執行完畢,返回結果:{0}", invocation.ReturnValue); Console.WriteLine("開始記錄日志...."); } }
builder.RegisterType<LogInterceptor>();
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces() .EnableInterfaceInterceptors(); var IControllerType = typeof(ControllerBase); builder.RegisterAssemblyTypes(assembly).Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired() .EnableClassInterceptors(); var Container = builder.Build();
開啟接口攔截器:EnableInterfaceInterceptors 開啟類攔截器:EnableClassInterceptors
[Intercept(typeof(LogInterceptor))] [Route("api/[controller]")] public class ValuesController : Controller { }
[Intercept(typeof(LogInterceptor))] public class Samoyed : IDog { }
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().EnableInterfaceInterceptors()
.InterceptedBy(typeof(LogInterceptor));
代碼封裝簡單使用
先列出使用過程中遇到的幾個問題,然后再給出解決方案
1:如何簡單注冊代碼里面的所有類型
2.如何注冊單例和普通對象
3.封裝好的代碼怎么支持用戶特殊化注冊需求
為了解決上述問題,這里給出了幾個約束
單例對象需繼承的接口:ISingletonDependency 普通對象需繼承的接口:ITransientDependency 特殊化注冊接口:IDependencyRegistrar
通過這幾個約束,在初始化時找所有程序集 繼承ISingletonDependency ,ITransientDependency 接口的對象進行類型注冊
/// <summary> /// 單例接口 /// </summary> public interface ISingletonDependency { }
/// <summary> /// 所有接口的依賴接口,每次創建新實例 /// </summary> /// <remarks> /// 用於Autofac自動注冊時,查找所有依賴該接口的實現。 /// 實現自動注冊功能 /// </remarks> public interface ITransientDependency { }
/// <summary> /// 依賴注冊接口 /// </summary> public interface IDependencyRegistrar { /// <summary> /// Register services and interfaces /// </summary> /// <param name="builder">Container builder</param> /// <param name="config">Config</param> void Register(ContainerBuilder builder,List<Type> listType); /// <summary> /// Order of this dependency registrar implementation /// </summary> int Order { get; } }
public interface IIocManager { IContainer Container { get; } bool IsRegistered(Type serviceType, ILifetimeScope scope = null); object Resolve(Type type, ILifetimeScope scope = null); T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class; T Resolve<T>(params Parameter[] parameters) where T : class; T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null); object ResolveOptional(Type serviceType, ILifetimeScope scope = null); object ResolveUnregistered(Type type, ILifetimeScope scope = null); T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class; ILifetimeScope Scope(); bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance); }
/// <summary> /// Container manager /// </summary> public class IocManager : IIocManager { private IContainer _container; public static IocManager Instance { get { return SingletonInstance; } } private static readonly IocManager SingletonInstance = new IocManager(); /// <summary> /// Ioc容器初始化 /// </summary> /// <param name="config"></param> /// <returns></returns> public IServiceProvider Initialize(IServiceCollection services) { var builder = new ContainerBuilder(); builder.RegisterInstance(Instance).As<IIocManager>().SingleInstance(); //所有程序集 和程序集下類型 var deps = DependencyContext.Default; var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系統程序集、Nuget下載包 var listAllType = new List<Type>(); foreach (var lib in libs) { try { var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); listAllType.AddRange(assembly.GetTypes().Where(type => type != null)); } catch { } } //找到所有外部IDependencyRegistrar實現,調用注冊 var registrarType = typeof(IDependencyRegistrar); var arrRegistrarType = listAllType.Where(t => registrarType.IsAssignableFrom(t) && t != registrarType).ToArray(); var listRegistrarInstances = new List<IDependencyRegistrar>(); foreach (var drType in arrRegistrarType) { listRegistrarInstances.Add((IDependencyRegistrar)Activator.CreateInstance(drType)); } //排序 listRegistrarInstances = listRegistrarInstances.OrderBy(t => t.Order).ToList(); foreach (var dependencyRegistrar in listRegistrarInstances) { dependencyRegistrar.Register(builder, listAllType); } //注冊ITransientDependency實現類 var dependencyType = typeof(ITransientDependency); var arrDependencyType = listAllType.Where(t => dependencyType.IsAssignableFrom(t) && t != dependencyType).ToArray(); builder.RegisterTypes(arrDependencyType) .AsImplementedInterfaces() .InstancePerLifetimeScope() .PropertiesAutowired().EnableInterfaceInterceptors(); foreach (Type type in arrDependencyType) { if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object)) { builder.RegisterType(type).As(type.BaseType) .InstancePerLifetimeScope() .PropertiesAutowired(); } } //注冊ISingletonDependency實現類 var singletonDependencyType = typeof(ISingletonDependency); var arrSingletonDependencyType = listAllType.Where(t => singletonDependencyType.IsAssignableFrom(t) && t != singletonDependencyType).ToArray(); builder.RegisterTypes(arrSingletonDependencyType) .AsImplementedInterfaces() .SingleInstance() .PropertiesAutowired(); foreach (Type type in arrSingletonDependencyType) { if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object)) { builder.RegisterType(type).As(type.BaseType) .SingleInstance() .PropertiesAutowired(); } } builder.Populate(services); _container = builder.Build(); return new AutofacServiceProvider(_container); } /// <summary> /// Gets a container /// </summary> public virtual IContainer Container { get { return _container; } } /// <summary> /// Resolve /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="key">key</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved service</returns> public virtual T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class { if (scope == null) { //no scope specified scope = Scope(); } if (string.IsNullOrEmpty(key)) { return scope.Resolve<T>(); } return scope.ResolveKeyed<T>(key); } /// <summary> /// Resolve /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="key">key</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved service</returns> public virtual T Resolve<T>(params Parameter[] parameters) where T : class { var scope = Scope(); return scope.Resolve<T>(parameters); } /// <summary> /// Resolve /// </summary> /// <param name="type">Type</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved service</returns> public virtual object Resolve(Type type, ILifetimeScope scope = null) { if (scope == null) { //no scope specified scope = Scope(); } return scope.Resolve(type); } /// <summary> /// Resolve all /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="key">key</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved services</returns> public virtual T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null) { if (scope == null) { //no scope specified scope = Scope(); } if (string.IsNullOrEmpty(key)) { return scope.Resolve<IEnumerable<T>>().ToArray(); } return scope.ResolveKeyed<IEnumerable<T>>(key).ToArray(); } /// <summary> /// Resolve unregistered service /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved service</returns> public virtual T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class { return ResolveUnregistered(typeof(T), scope) as T; } /// <summary> /// Resolve unregistered service /// </summary> /// <param name="type">Type</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved service</returns> public virtual object ResolveUnregistered(Type type, ILifetimeScope scope = null) { if (scope == null) { //no scope specified scope = Scope(); } var constructors = type.GetConstructors(); foreach (var constructor in constructors) { try { var parameters = constructor.GetParameters(); var parameterInstances = new List<object>(); foreach (var parameter in parameters) { var service = Resolve(parameter.ParameterType, scope); if (service == null) throw new Exception("Unknown dependency"); parameterInstances.Add(service); } return Activator.CreateInstance(type, parameterInstances.ToArray()); } catch (Exception) { } } throw new Exception("No constructor was found that had all the dependencies satisfied."); } /// <summary> /// Try to resolve srevice /// </summary> /// <param name="serviceType">Type</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <param name="instance">Resolved service</param> /// <returns>Value indicating whether service has been successfully resolved</returns> public virtual bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance) { if (scope == null) { //no scope specified scope = Scope(); } return scope.TryResolve(serviceType, out instance); } /// <summary> /// Check whether some service is registered (can be resolved) /// </summary> /// <param name="serviceType">Type</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Result</returns> public virtual bool IsRegistered(Type serviceType, ILifetimeScope scope = null) { if (scope == null) { //no scope specified scope = Scope(); } return scope.IsRegistered(serviceType); } /// <summary> /// Resolve optional /// </summary> /// <param name="serviceType">Type</param> /// <param name="scope">Scope; pass null to automatically resolve the current scope</param> /// <returns>Resolved service</returns> public virtual object ResolveOptional(Type serviceType, ILifetimeScope scope = null) { if (scope == null) { //no scope specified scope = Scope(); } return scope.ResolveOptional(serviceType); } /// <summary> /// Get current scope /// </summary> /// <returns>Scope</returns> public virtual ILifetimeScope Scope() { try { //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks) return Container.BeginLifetimeScope(); } catch (Exception) { //we can get an exception here if RequestLifetimeScope is already disposed //for example, requested in or after "Application_EndRequest" handler //but note that usually it should never happen //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks) return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag); } } }
public IServiceProvider ConfigureServices(IServiceCollection services) { services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); services.AddMvc(); return IocManager.Instance.Initialize(services); }
特殊場景介紹
通過上面的封裝后,我們可以把Controller的注冊單獨出來
/// <summary> /// /// </summary> public class ControllerRegistrar : IDependencyRegistrar { /// <summary> /// /// </summary> public int Order { get { return 0; } } /// <summary> /// /// </summary> /// <param name="builder"></param> /// <param name="listType"></param> public void Register(ContainerBuilder builder, List<Type> listType) { builder.RegisterType(typeof(LogInterceptor)); //注冊Controller,實現屬性注入 var IControllerType = typeof(ControllerBase); var arrControllerType = listType.Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).ToArray(); builder.RegisterTypes(arrControllerType).PropertiesAutowired().EnableClassInterceptors(); } }
builder.RegisterType(typeof(TestDemo)).AsSelf(); public class TestDemo { private readonly string _name; private readonly string _sex; private readonly int _age; public TestDemo(string name, string sex, int age) { _name = name; _age = age; _sex = sex; } public string Sex { get { return _sex; } } public string Name { get { return _name; } } public int Age { get { return _age; } } }
var iocManager = app.ApplicationServices.GetService<IIocManager>(); List<Parameter> cparams = new List<Parameter>(); cparams.Add(new NamedParameter("name", "張三")); cparams.Add(new NamedParameter("sex", "男")); cparams.Add(new TypedParameter(typeof(int), 2)); var testDemo = iocManager.Resolve<TestDemo>(cparams.ToArray()); Console.WriteLine($"姓名:{testDemo.Name},年齡:{testDemo.Age},性別:{testDemo.Sex}");
2.對象激活事件
Autofac暴露五個事件接口供實例的按如下順序調用
- OnRegistered
- OnPreparing
- OnActivated
- OnActivating
- OnRelease
這些事件會在注冊的時候被訂閱,或者被附加到IComponentRegistration 的時候。
builder.RegisterType(typeof(TestDemo)).AsSelf() .OnRegistered(e => Console.WriteLine("OnRegistered在注冊的時候調用!")) .OnPreparing(e => Console.WriteLine("OnPreparing在准備創建的時候調用!")) .OnActivating(e => Console.WriteLine("OnActivating在創建之前調用!")) .OnActivated(e => Console.WriteLine("OnActivated創建之后調用!")) .OnRelease(e => Console.WriteLine("OnRelease在釋放占用的資源之前調用!"));

總結
本篇介紹了Autofac在項目中的使用方式以及幾種特殊使用場景。其它未介紹知識如生命周期請參考http://autofac.readthedocs.io/en/latest/getting-started/index.html。
下面給出本文示例代碼:Ywdsoft.AutofacTest