NET CORE business layer service automatic registration – DI

<strong>App background
   In the development of the three-layer structure of net Core, many people still use the manual registration method, which is to write code to register. Generally, there is a class in which all the implementation classes corresponding to the XX interface are manually registered, such as code. I'm lazy and don't like to write boring code all the time, so I encapsulated an automatically registered class library. The following is the source code of the class library.

core code
The service discovery and automatic registration in the AutoInjectRepository class depends on whether there is</strong><em>[AutoInject(typeof(IIndustryService), InjectType.Scope)]<strong>tag on the service implementation class. If you have this tag, you will be registered</strong></em>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;

namespace CommonHelper.AutoInject.Service
{
    public static class AutoInjectRepository
    {
        public static IServiceCollection AddAutoDi(this IServiceCollection serviceCollection)
        {
            foreach (Assembly item in Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll").Select(Assembly.LoadFrom).ToList())
            {
                List<Type> list = (from a in item.GetTypes()
                                   where a.GetCustomAttribute<AutoInjectAttribute>() != null
                                   select a).ToList();
                if (list.Count <= 0)
                {
                    continue;
                }

                foreach (Type item2 in list)
                {
                    AutoInjectAttribute customAttribute = item2.GetCustomAttribute<AutoInjectAttribute>();
                    if (!(customAttribute?.Type == null))
                    {
                        switch(customAttribute.InjectType)
                        {
                            case InjectType.Scope:
                                serviceCollection.AddScoped(customAttribute.Type, item2);
                                break;
                            case InjectType.Single:
                                serviceCollection.AddSingleton(customAttribute.Type, item2);
                                break;
                            case InjectType.Transient:
                                serviceCollection.AddTransient(customAttribute.Type, item2);
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }
                }
            }

            return serviceCollection;
        }

        public static IServiceCollection AddAutoDiService(this IServiceCollection serviceCollection, string namespaceName)
        {
            foreach (Type item in getTypesInNamespace(namespaceName))
            {
                AutoInjectAttribute customAttribute = item.GetCustomAttribute<AutoInjectAttribute>();
                if (!(customAttribute?.Type == null))
                {
                    switch(customAttribute.InjectType)
                    {
                        case InjectType.Scope:
                            serviceCollection.AddScoped(customAttribute.Type, item);
                            break;
                        case InjectType.Single:
                            serviceCollection.AddSingleton(customAttribute.Type, item);
                            break;
                        case InjectType.Transient:
                            serviceCollection.AddTransient(customAttribute.Type, item);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }

            return serviceCollection;
        }

        private static List<Type> getTypesInNamespace(string namespaceName)
        {
            return Assembly.Load(namespaceName).GetTypes().ToList();
        }
    }
}

The service automatic registration function of Startup.cs file,

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;
using LD.Admin.Models;
using Microsoft.OpenApi.Models;
using System.Reflection;
using System.IO;
using Swashbuckle.AspNetCore.SwaggerUI;
using CommonHelper.AutoInject.Service;
using LD.Admin.Service.RegisterService;
using LD.Admin.Repository.Factory;
using LD.Admin.Api.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using LD.Admin.Common;
using Microsoft.AspNetCore.SignalR;
using CommonHelper.AutoInject.Repository;

namespace LD.Admin.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            //Register the global Configuration object
            ConfigurationManager.Configure(Configuration);
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "XX background management service interface", Version = "v1" });
                // Get xml file name
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                // Get xml file path
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                //Add controller layer annotation, true means display controller annotation
                c.IncludeXmlComments(xmlPath, true);
                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
                c.DocumentFilter<HiddenApiFilter>();
            });
            services.Configure<TokenManagementModel>(Configuration.GetSection("JwtTokenConfig"));
            var token = Configuration.GetSection("JwtTokenConfig").Get<TokenManagementModel>();
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
                    ValidIssuer = token.Issuer,
                    ValidAudience = token.Audience,
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });

            services.AddScoped<IAuthenticateService, TokenAuthenticationService>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            //services.AddSingleton<NotificationHub>();
            //services.AddSingleton<ChatHub>();
            //"LD.Admin.Repository"
            //RepositoryFactory.AutoRegisterService(string.Empty, "LD.Admin.Repository");
            //RegisterServiceIoc.Register(services);
            //services.AddAutoDi();
            //Automatic injection of business layer services
            services.AddAutoDiService("LD.Admin.Service");
            //services.AddAutoDiService("LD.Admin.Repository");
            //AutoFacFactory.AutoRegisterService("LD.Admin.Repository");
            //Call the AutoRegisterService method to realize automatic registration of data access layer services
            RepositoryIocFactory.AutoRegisterService("LD.Admin.Repository");
            //Add support for AutoMapper and find classes that inherit Profile in all assemblies
            //Configure AutoMapper
            services.AddAutoMapper(typeof(AutoMapperConfigs));
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
            services.AddControllers();
            services.AddSignalR();

            //Cross domain
            var corsstring = Configuration.GetSection("Cors").Value;
            string[] corsarray = corsstring.Split(',');

            services.AddCors(options => options.AddPolicy("CorsPolicy",
            builder =>
            {
                builder.AllowAnyMethod().AllowAnyHeader()
                       .WithOrigins(corsarray)
                       .AllowCredentials();
            }));

            //var assbembly = AppDomain.CurrentDomain.GetAssemblies().ToList();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //Inject request.
            ServiceLocator.SetServices(app.ApplicationServices);
            //Add Swagger related middleware
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "AdminAPI v1");
                c.RoutePrefix = string.Empty;
                c.DocExpansion(DocExpansion.None);
            });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            
            app.UseHttpsRedirection();

            app.UseRouting();
            app.UseCors("CorsPolicy");
            //Enable authentication
            app.UseAuthentication();
            //Enable authorization
            app.UseAuthorization();
            //var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
            var apiUrlConfig = Configuration.GetSection("ApiUrl").Value;
            app.UseEndpoints(endpoints =>
            {
                //endpoints.MapHub<ChatHub>("/chatHub");
                //endpoints.MapHub<ChatHub>("/chatHub").RequireCors(t => t.WithOrigins(new string[] { "http://localhost:8080" }).AllowAnyMethod().AllowAnyHeader ().AllowCredentials());
                endpoints.MapHub<ChatHub>("/chatHub").RequireCors(t => t.WithOrigins(new string[] { apiUrlConfig }).AllowAnyMethod().AllowAnyHeader().AllowCredentials());
                //endpoints.MapHub<ChatHub>("/notifyHub").RequireCors(t => t.WithOrigins(new string[] { apiUrlConfig }).AllowAnyMethod().AllowAnyHeader().AllowCredentials());
                endpoints.MapControllers();
            });
        }
    }
}

Code of service layer implementation class

/// <summary>
    /// Industry table business logic layer interface implementation class
    /// </summary>
    [AutoInject(typeof(IIndustryService), InjectType.Scope)]
    public class IndustryService : BaseService<IIndustryRepository,int, IndustrySearchModel, IndustryModel>, IIndustryService
    {
        protected override IIndustryRepository Service
        {
            //get { return RepositoryFactory.Industry; }
            get { return RepositoryIocFactory.GetRegisterImp<IIndustryRepository>(); }
        }

        protected override string ClassName
        {
            get { return "IndustryService"; }
        }
    }

Use of controller layer

/// <summary>
    /// Industry interface
    /// </summary>
    public class IndustryController : BaseApiController
    {
        private IIndustryService _service;

        /// <summary>
        ///
        /// </summary>
        /// <param name="service"></param>
        public IndustryController(IIndustryService service)
        {
            _service = service;
        }

        /// <summary>
        /// Get the paginated list
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost, Route("getlistbypage")]
        public AjaxResultModel GetListByPage(IndustrySearchModel model)
        {
            var rspModel = new AjaxResultPageModel();
            try
            {
                model.ValidatePageSize();
                var pageModel = _service.GetListByPage(model, t => t.CreatedOn, OrderModeEnum.Desc);
                if (pageModel != null & amp; & amp; pageModel.Models.Count > 0)
                {
                    rspModel.data = pageModel.Models;
                    rspModel.PageCount = pageModel.PageCount;
                    rspModel.RecordCount = pageModel.RecordCount;
                    rspModel.PageIndex = pageModel.PageIndex;
                }
                else
                {
                    rspModel.message = "No relevant data found";
                }
                rspModel.success = true;
            }
            catch (System.Exception ex)
            {
                rspModel.Error(ex.Message);
            }
            return rspModel;
        }

        /// <summary>
        /// (Details) Query Model based on primary key
        /// </summary>
        /// <param name="id">Primary key</param>
        /// <returns></returns>
        [HttpGet, Route("getmodelbyid")]
        public AjaxResultModel GetModelById(int id)
        {
            var rspModel = new AjaxResultModel();
            try
            {
                if (id > 0)
                {
                    var model = _service.GetModelById(id);
                    if (model != null)
                    {
                        rspModel.Success(model);
                    }
                    else
                    {
                        rspModel.Success(model, "ID does not exist");
                    }
                }
                else
                {
                    rspModel.Warning("ID parameter must be passed");
                }
            }
            catch (Exception ex)
            {
                //AddLogError(_className, "GetModelById", ex.Message, ex, id, id);
                rspModel.Error(ex.Message);
            }
            return rspModel;
        }
    }