.net5 - 创建Web.Api项目(八)IOC依赖注入

 方式一:自定义【在调试是加载时间过长】

NuGet包:

Microsoft.Extensions.DependencyModel
Microsoft.Extensions.Options

XXX.Common项目下新建文件夹【DependencyInjection】

 

新建类:RuntimeHelper、ServiceExtension

using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace NetFive.Common.DependencyInjection
{
    public class RuntimeHelper
    {
        /// <summary>
        /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包
        /// </summary>
        /// <returns></returns>
        public static IList<Assembly> GetAllAssemblies()
        {
            var list = new List<Assembly>();
            var deps = DependencyContext.Default;
            var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包
            foreach (var lib in libs)
            {
                try
                {
                    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                    list.Add(assembly);
                }
                catch (Exception)
                {
                }
            }
            return list;
        }

        public static Assembly GetAssembly(string assemblyName)
        {
            return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName));
        }

        public static IList<Type> GetAllTypes()
        {
            var list = new List<Type>();
            foreach (var assembly in GetAllAssemblies())
            {
                var typeInfos = assembly.DefinedTypes;
                foreach (var typeInfo in typeInfos)
                {
                    list.Add(typeInfo.AsType());
                }
            }
            return list;
        }

        public static IList<Type> GetTypesByAssembly(string assemblyName)
        {
            var list = new List<Type>();
            var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName));
            var typeInfos = assembly.DefinedTypes;
            foreach (var typeInfo in typeInfos)
            {
                list.Add(typeInfo.AsType());
            }
            return list;
        }

        public static Type GetImplementType(string typeName, Type baseInterfaceType)
        {
            return GetAllTypes().FirstOrDefault(t =>
            {
                if (t.Name == typeName &&
                    t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name))
                {
                    var typeInfo = t.GetTypeInfo();
                    return typeInfo.IsClass && !typeInfo.IsAbstract;
                }
                return false;
            });
        }
    }
}
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;

namespace NetFive.Common.DependencyInjection
{
    /// <summary>
    /// IServiceCollection扩展
    /// </summary>
    public static class ServiceExtension
    {
        /// <summary>
        /// 注册服务
        /// </summary>
        /// <param name="services"></param>
        /// <param name="interfaceAssemblyName">定义业务接口的程序集名称</param>
        /// <param name="implementAssemblyName">实现业务接口的程序集名称(默认 interfaceAssemblyName)</param>
        public static void RegisterService(this IServiceCollection service, string interfaceAssemblyName, string implementAssemblyName)
        {
            if (string.IsNullOrEmpty(implementAssemblyName))
            {
                RegisterAssembly(service, interfaceAssemblyName);
            }
            else
            {
                RegisterAssembly(service, interfaceAssemblyName, implementAssemblyName);
            }
        }

        /// <summary>
        /// 批量注入接口程序集中对应的实现类。
        /// <para>
        /// 需要注意的是,这里有如下约定:
        /// IUserService --> UserService, IUserRepository --> UserRepository.
        /// </para>
        /// </summary>
        /// <param name="service"></param>
        /// <param name="interfaceAssemblyName">接口程序集的名称(不包含文件扩展名)</param>
        /// <returns></returns>
        internal static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName)
        {
            if (service == null)
            { 
                throw new ArgumentNullException(nameof(service));
            }
            if (string.IsNullOrEmpty(interfaceAssemblyName))
            {
                throw new ArgumentNullException(nameof(interfaceAssemblyName));
            }
            var assembly = RuntimeHelper.GetAssembly(interfaceAssemblyName);
            if (assembly == null)
            {
                throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found");
            }
            //过滤掉非接口及泛型接口
            var types = assembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface);
            foreach (var type in types)
            {
                var implementTypeName = type.Name.Substring(1);
                var implementType = RuntimeHelper.GetImplementType(implementTypeName, type);
                if (implementType != null)
                    service.AddScoped(type, implementType);
            }
            return service;
        }

        /// <summary>
        /// 用DI批量注入接口程序集中对应的实现类。
        /// </summary>
        /// <param name="service"></param>
        /// <param name="interfaceAssemblyName">接口程序集的名称(不包含文件扩展名)</param>
        /// <param name="implementAssemblyName">实现程序集的名称(不包含文件扩展名)</param>
        /// <returns></returns>
        internal static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName, string implementAssemblyName)
        {
            if (service == null)
                throw new ArgumentNullException(nameof(service));
            if (string.IsNullOrEmpty(interfaceAssemblyName))
                throw new ArgumentNullException(nameof(interfaceAssemblyName));
            if (string.IsNullOrEmpty(implementAssemblyName))
                throw new ArgumentNullException(nameof(implementAssemblyName));
            var interfaceAssembly = RuntimeHelper.GetAssembly(interfaceAssemblyName);
            if (interfaceAssembly == null)
            {
                throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found");
            }
            var implementAssembly = RuntimeHelper.GetAssembly(implementAssemblyName);
            if (implementAssembly == null)
            {
                throw new DllNotFoundException($"the dll \"{implementAssemblyName}\" not be found");
            }
            //过滤掉非接口及泛型接口
            var types = interfaceAssembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface);
            foreach (var type in types)
            {
                //过滤掉抽象类、泛型类以及非class
                var implementType = implementAssembly.DefinedTypes
                    .FirstOrDefault(t => t.IsClass && !t.IsAbstract &&
                                         t.GetInterfaces().Any(b => b.Name == type.Name));
                if (implementType != null)
                {
                    service.AddScoped(type, implementType.AsType());
                }
            }
            return service;
        }
    }
}

 Startup注册:

            #region IOC依赖注入自定义
            services.RegisterService("NetFive.Repository", string.Empty);
            services.RegisterService("NetFive.Command", string.Empty);
            services.RegisterService("NetFive.Query", string.Empty);
            services.RegisterService("NetFive.Service", string.Empty);
            #endregion

  

 方式二:Autofac简单

 NuGet包:

Autofac
Autofac.Extensions.DependencyInjection

 Program.cs

.UseServiceProviderFactory(new AutofacServiceProviderFactory())//添加Autofac服务

 Startup类添加 ConfigureContainer() 方法,必须添加在Startup类里面没有细研究其他方法

/// <summary>
/// 这段代码必须放在Startup类里面
/// </summary>
/// <param name="builder"></param>
public void ConfigureContainer(ContainerBuilder builder)
{
    
}

 构造函数注入:

//构造函数注入
builder.RegisterType<EmployeeService>().As<IEmployeeService>();

 构造函数使用:

 属性注入:

builder.RegisterType<EmployeeService>().As<IEmployeeService>().PropertiesAutowired();//只能在当前的EmployeeService类,使用属性注入

 属性注入使用:

方法注入:

 

//方法注入
builder.RegisterType<EmployeeQry>().As<IEmployeeQry>();
builder.RegisterType<EmployeeService>().OnActivated(e => e.Instance.Qry(e.Context.Resolve<IEmployeeQry>())).As<IEmployeeService>();

 方法注入使用:

方式2:Autofac批量

需要 using 命名空间 System.Reflection

修改 Straup.cs 文件中的 ConfigureContainer() 方法

约定接口(Interface)和实现(class)都是以 Service 【或者其他】结尾的。

        /// <summary>
        /// 这段代码必须放在Startup类里面
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //批量注入//加载程序集
            //var urpIService = Assembly.Load("NetFive.Service"); //接口层
            //var urpService = Assembly.Load("NetFive.Service");  //实现层
            //根据名称约定(服务层的接口和实现均以Service结尾),实现服务接口和服务实现的依赖
            //builder.RegisterAssemblyTypes(urpIService, urpService).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().PropertiesAutowired();

            //泛型注册
            builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));

            //批量注入
            var repository = Assembly.Load("NetFive.Repository");
            builder.RegisterAssemblyTypes(repository).Where(t => t.Name.EndsWith("UnitOfWorks") || t.Name.EndsWith("DbContext")).AsImplementedInterfaces();

            var qry = Assembly.Load("NetFive.Query");
            builder.RegisterAssemblyTypes(qry).Where(t => t.Name.EndsWith("Qry")).AsImplementedInterfaces();

            var cmd = Assembly.Load("NetFive.Command");
            builder.RegisterAssemblyTypes(cmd).Where(t => t.Name.EndsWith("Cmd")).AsImplementedInterfaces();

            //批量注入//加载程序集
            var urpIService = Assembly.Load("NetFive.Service");
            //因为接口层和实现层都在一起,所以只用写一个
            builder.RegisterAssemblyTypes(urpIService).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().PropertiesAutowired();
        }

 使用:

 方式2:Autofac控制器注入

#region 指定控制器的实例由容器来创建
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
#endregion

            #region 注册所有控制器的关系及控制器实例化所需要的组件

            var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
                .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();

            builder.RegisterTypes(controllersTypesInAssembly)
                .PropertiesAutowired();

            #endregion 

 使用:

 扩展:自己控制哪些属性需要做依赖注入(默认是让控制器中的属性都依赖注入)

 

using System;

namespace NetFive.WebApi.Custom.Autofacs
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class AutowaredAttribute : Attribute { }
}
using Autofac.Core;
using System.Linq;
using System.Reflection;

namespace NetFive.WebApi.Custom.Autofacs
{
    public class PropertySelector : IPropertySelector
    {
        public bool InjectProperty(PropertyInfo propertyInfo, object instance)
        {
            return propertyInfo.CustomAttributes.Any(ca => ca.AttributeType == typeof(AutowaredAttribute));
        }
    }
}

 

添加特性才能使用:

 

 

 方式2:Autofac泛型注入

 

//方式1.以泛型方式注册
builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>)).SingleInstance();
builder.RegisterGeneric(typeof(DatabaseFactory<>)).As(typeof(IDatabaseFactory<>)).SingleInstance();
//方式2.以普通的方式注册
//builder.RegisterType<UnitOfWork<AutofacDBEntities>>().As<IUnitOfWork<AutofacDBEntities>>().SingleInstance();
//builder.RegisterType<DatabaseFactory<AutofacDBEntities>>().As<IDatabaseFactory<AutofacDBEntities>>().SingleInstance();

 

 方式2:Autofac生命周期

 1、InstancePerDependency :默认模式,每次调用,都会重新实例化对象;每次请求都创建一个新的对象;

builder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerDependency();

 2、SingleInstance :单例模式,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;

builder.RegisterType<TestServiceA>().As<ITestServiceA>().SingleInstance();

 3、InstancePerLifetimeScope : 同一个生命周期域中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;且每个不同的生命周期域中的实例是唯一的,不共享的。

builder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerLifetimeScope();

 4、InstancePerMatchingLifetimeScope : 同一个匹配的生命周期域中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;且每个不匹配的生命周期域中的实例是唯一的,不共享的。

builder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerMatchingLifetimeScope();

 5、InstancePerOwned : 在一个所拥有的实例创建的生命周期中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;(较少使用)

 6、InstancePerHttpRequest : 同一次Http请求上下文中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;仅适用于 ASP.NET (CORE) MVC 或 WebForm 应用程序

 

posted @ 2021-03-06 16:57  gygtech  Views(783)  Comments(0Edit  收藏  举报