• .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

  • 相关阅读:
    第二章 万变不离其踪--收割自己的深度图
    2.1 光照系统
    2.2 深度渲染机制
    2.3 来点实际--日照分析实现
    2.4 通视分析
    2.5 Cesium视域分析的实现
    2.6
    第三章 讲真,没几个搞得清楚的经纬度——GIS坐标
    3.1 地理坐标系统
    3.2 渲染坐标系统
  • 原文地址:https://www.cnblogs.com/yanweidie/p/autofac.html
Copyright © 2020-2023  润新知