.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 應用程序

 


免責聲明!

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



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