随笔 - 43  文章 - 0  评论 - 1154  0

  .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

posted on 2018-03-28 08:35 焰尾迭 阅读(...) 评论(...) 编辑 收藏