1. 程式人生 > 其它 >反射反射程式猿的快樂,使用反射實現NetCore3.1依賴注入

反射反射程式猿的快樂,使用反射實現NetCore3.1依賴注入

反射反射,程式猿的快樂,用於NetCore專案依賴注入

十年河東,十年河西,莫欺少年窮

學無止境,精益求精

開局一張圖,內容全靠編

如下圖:

上圖是我簡單構造的一個NetCore的分層專案,解釋如下:

appModel:實體層

appDataInterface:資料訪問介面層

appDataService:資料訪問介面層的實現層

appLogicInterface:業務邏輯介面層

appLogicService:業務邏輯層的實現層

appWeb:webApi站點

很簡單的一個基礎的專案構造就這樣悄無聲息的構建出來了,層次之間的引用,我就不多贅述了。哈哈

然後,我們搞一個介面,如下:

appDataInterface 的 ILoginRepository

using appModel;
using System;
using System.Collections.Generic;
using System.Text;

namespace appDataInterface
{
    public interface ILoginRepository
    {
        LoginModel UserLogin(string userAccount, string userPassword);
    }
}

appLogicInterface 的 ILoginService

using appModel;
using System;
using System.Collections.Generic; using System.Text; namespace appLogicInterface { public interface ILoginService { LoginModel UserLogin(string userAccount, string userPassword); } }

再然後,我們搞下介面的實現,如下:

appDataService 的 LoginRepository

using appDataInterface;
using appModel;
using
System; namespace appDataService { public class LoginRepository: ILoginRepository { public LoginModel UserLogin(string userAccount, string userPassword) { return new LoginModel() { userName = "陳大六", userRole = "超級管理員" }; } } }

appLogicService 的LoginService

using appDataInterface;
using appLogicInterface;
using appModel;
using System;

namespace appLogicService
{
    public class LoginService: ILoginService
    {
        private readonly ILoginRepository repository;
        public LoginService(ILoginRepository repository)
        {
            this.repository = repository;
        }
        /// <summary>
        /// 使用者登入介面
        /// </summary>
        /// <param name="userAccount"></param>
        /// <param name="userPassword"></param>
        /// <returns></returns>
        public LoginModel UserLogin(string userAccount, string userPassword)
        {
            return repository.UserLogin(userAccount, userPassword);
        }
    }
}

注意:業務邏輯層需要依賴注入資料訪問層,因此她的構造方法為:

        private readonly ILoginRepository repository;
        public LoginService(ILoginRepository repository)
        {
            this.repository = repository;
        }

最後,我們來寫個簡單的控制器,如下:

using appLogicInterface;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace appWeb.Controllers
{
    [Route("api/Login")]
    [ApiController]
    public class LoginController : ControllerBase
    {
        private readonly ILoginService service;
        public LoginController(ILoginService service)
        {
            this.service = service;
        }

        [HttpGet("UserLogin")]
        public IActionResult UserLogin(string userName,string userPassword)
        {
            return Ok(service.UserLogin(userName, userPassword));
        }
    }
}
View Code

截止到這兒,我們還差一步、、、那就是在StartUp.cs中注入介面及實現類,如下:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddScoped<ILoginRepository, LoginRepository>();
            services.AddScoped<ILoginService, LoginService>();
        }

專案構造完成後,我們來看看返回結果:

那麼,問題來了,如果我們的專案足夠大,介面及實現類足夠多,我們就需要在StartUp.cs中寫很多很多依賴注入,這樣給人一種臃腫的感覺

有沒有什麼好的方法來避免寫很多的依賴注入呢?

答案是肯定的,反射反射,程式猿的快樂,通過反射我們來解決此問題。

1、在該專案中增加一個專案,命名為:appInit

多餘的話,我也不想多說,直接上程式碼吧,程式碼有一定的註釋:

Constants.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace app.Init
{
    public class Constants
    {
        /// <summary>
        /// 業務邏輯層DLL檔案:appLogicService.dll
        /// </summary>
        public const string ServiceDllFullName = "appLogicService.dll";
        /// <summary>
        /// 業務邏輯層實現類的命名字尾,也就是說,我們在介面層定義一個介面為:IABCService 那麼,它的實現類就必須為:ABCService
        /// </summary>
        public const string ServiceSuffix = "Service";
        /// <summary>
        /// 資料訪問層DLL檔案:appDataService.dll
        /// </summary>
        public const string RepositoryDllFullName = "appDataService.dll";
        /// <summary>
        /// 業務邏輯層實現類的命名字尾,也就是說,我們在介面層定義一個介面為:IBugRepository 那麼,它的實現類就必須為:BugRepository
        /// </summary>
        public const string RepositorySuffix = "Repository";

    }
}
View Code

DIExtension.cs

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;

namespace app
{
    /// <summary>
    /// 反射注入
    /// </summary>
    public static class DIExtension
    {
        //public static void RegisterAssemblyEndsWith(this IServiceCollection services, Assembly assembly, params string[] endsWith)
        //{
        //    string @namespace = typeof(DIExtension).Namespace;
        //    foreach (var type in assembly.GetTypes())
        //    {
        //        if ((!type.IsInterface) && (!type.IsAbstract) && CheckEndsWith(type.FullName, endsWith))
        //        {
        //            var typeInterface = type.GetInterfaces().Where(p => p.FullName.StartsWith(@namespace)).FirstOrDefault(i => CheckEndsWith(i.FullName, endsWith));
        //            if (typeInterface != null)
        //            {
        //                services.AddScoped(typeInterface, type);
        //            }
        //            else
        //            {
        //                services.AddScoped(type, type);
        //            }
        //        }
        //    }
        //}

        public static void RegisterAssemblyEndsWith(this IServiceCollection services, Assembly assembly, params string[] endsWith)
        {
            ///app
            string @namespace = typeof(DIExtension).Namespace;//用於只查詢app開頭的名稱空間
            foreach (var type in assembly.GetTypes())
            {
                if ((!type.IsInterface) && (!type.IsAbstract) && CheckEndsWith(type.FullName, endsWith))
                {
                    ///找到對應的介面,驗證介面命名規則 例如 AService 對應的介面應為:IAService 且 名稱空間的開頭帶有"app"|||用於只查詢app開頭的名稱空間
                    var typeInterfaces = type.GetInterfaces().Where(p => p.FullName.StartsWith(@namespace) && CheckEndsWith(p.FullName, endsWith)).ToList();
                    if (typeInterfaces?.Count > 0)
                    {
                        foreach (var typeInterface in typeInterfaces)
                        {
                            services.AddScoped(typeInterface, type);
                        }
                    }
                    else
                    {
                        services.AddScoped(type, type);
                    }
                }
            }
        }
        /// <summary>
        /// 命名規則檢測
        /// </summary>
        /// <param name="source"></param>
        /// <param name="endsWith"></param>
        /// <returns></returns>
        private static bool CheckEndsWith(string source, string[] endsWith)
            => endsWith.Any(p => source.EndsWith(p, StringComparison.OrdinalIgnoreCase));
    }
}
View Code

DIRegister.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace app.Init
{
    public static class DIRegister
    {
        /// <summary>
        ///此處 this 是將RegisterDI方法標記為IServiceCollection 的 擴充套件方法
        /// </summary>
        /// <param name="services"></param>
        public static void RegisterDI(this IServiceCollection services)
        {
            ///D:\525gitProject\netcore\WuAnManager\WuAnChangeApi\bin\Debug\netcoreapp3.1
            var rootPath = Path.GetDirectoryName(typeof(DIExtension).Assembly.Location);
            var rootDir = new DirectoryInfo(rootPath);
            ///D:\525gitProject\netcore\WuAnManager\WuAnChangeApi\bin\Debug\netcoreapp3.1
            var basePath = rootDir.FullName;
            RegisterDll(services, basePath, Constants.ServiceDllFullName, Constants.ServiceSuffix);
            RegisterDll(services, basePath, Constants.RepositoryDllFullName, Constants.RepositorySuffix);
        }

        /// <summary>
        /// 依賴注入
        /// </summary>
        /// <param name="services"></param>
        /// <param name="basePath"></param>
        /// <param name="dllName"></param>
        /// <param name="endsWith"></param>
        private static void RegisterDll(IServiceCollection services, string basePath, string dllName, params string[] endsWith)
        {
            ///D:\525gitProject\netcore\WuAnManager\WuAnChangeApi\bin\Debug\netcoreapp3.1\WuAnService.dll
            string assemblyPath = Path.Combine(basePath, dllName);
            var assembly = Assembly.LoadFrom(assemblyPath);
            services.RegisterAssemblyEndsWith(assembly, endsWith);
        }

        /// <summary>
        /// 用於檢視注入的介面及實現類  此處的This表明DIListPage方法為IApplicationBuilder的一個擴充套件方法
        /// </summary>
        /// <param name="app"></param>
        /// <param name="_services"></param>
        public static void DIListPage(this IApplicationBuilder app, IServiceCollection _services)
        {
            app.Map($"/api/allservices", builder => builder.Run(async context =>
            {
                var sb = new StringBuilder();
                sb.Append("<h1>All Services</h1>");
                sb.Append("<table><thead>");
                sb.Append("<tr><th>Type</th><th>Lifetime</th><th>Instance</th></tr>");
                sb.Append("</thead><tbody>");
                foreach (var svc in _services)
                {
                    sb.Append("<tr>");
                    sb.Append($"<td>{svc.ServiceType.FullName}</td>");
                    sb.Append($"<td>{svc.Lifetime}</td>");
                    sb.Append($"<td>{svc.ImplementationType?.FullName}</td>");
                    sb.Append("</tr>");
                }
                sb.Append("</tbody></table>");
                await context.Response.WriteAsync(sb.ToString());
            }));
        }
    }
}
View Code

最後,我們修改Startup.cs 如下:

using app.Init;
using appDataInterface;
using appDataService;
using appLogicInterface;
using appLogicService;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace appWeb
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        private IServiceCollection _services;
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //services.AddScoped<ILoginRepository, LoginRepository>();
            //services.AddScoped<ILoginService, LoginService>();

            this._services = services;
            services.RegisterDI();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.DIListPage(_services);
        }
    }
}
View Code

startup修改的地方如下:

 private IServiceCollection _services;

註釋掉:

//services.AddScoped<ILoginRepository, LoginRepository>();
//services.AddScoped<ILoginService, LoginService>();

增加【需要引用appinit專案】

  this._services = services;
  services.RegisterDI();

增加【用於檢視注入的介面及實現類】

  app.DIListPage(_services);

這樣,整個無需逐個寫依賴對映的小工程就構造完成了,執行下,看看效果

1、先檢視注入的介面及實現類,路由參考DIRegister.cs類中的DIListPage方法

再看看我們寫的登入方法:

哈哈,就這樣就可以了。

@天才臥龍的部落格

付婷,你還那麼胖嗎?如果胖,就減肥肥吧。