.net core2.0下Ioc容器Autofac使用


  .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 "小黑";
            }
        }
    }
View Code

  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}");
        }
    }
}        
  2.RegisterAssemblyTypes
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下載AutofacAutofac.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(); ;
    }
}
 
使用過mef的可能更喜歡屬性注入的方式,那么使用autofac怎么實現屬性注入呢?
1.注冊系統所有Controller,由Autofac創建
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();
注意:Replace代碼放在AddMvc之前
Replace代碼的意思:使用ServiceBasedControllerActivator替換DefaultControllerActivator(意味着框架現在會嘗試從IServiceProvider中解析控制器實例,也就是return new AutofacServiceProvider(Container);
3.使用屬性注入
  [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實現屬性注入

Autofac+Castle實現AOP

1.首先nuget下載Autofac.Extras.DynamicProxy引用
2.編寫攔截器
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("開始記錄日志....");
    }
}
3.開啟攔截(接口攔截器  類攔截器)
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();
        }
    }
下面介紹幾種特殊使用方式
1.創建實例時給指定參數賦值
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暴露五個事件接口供實例的按如下順序調用

  1. OnRegistered
  2. OnPreparing
  3. OnActivated
  4. OnActivating
  5. 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM