65.9K
CodeProject is changing. Read more.
Home

Configuring Dependency Injections in .NET Core without any Code using Service Installer

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3 votes)

Apr 10, 2020

CPOL
viewsIcon

7652

How to use an interface called IServiceInstaller that you can use to configure Dependency Injections and then automatically read it with a simple line of reflection code

Introduction

This is how we configure IServiceInstaller interface:

public interface IServiceInstaller
{
   void InstallServices(IServiceCollection services, 
                        AppSettings appSettings, Assembly startupProjectAssembly);
}

Description

The “appSettings” parameter is a class that contains all of “appsettings.json” file configuration values. You can use IConfiguration indeed and pass the object to StartUp class. The “startupProjectAssembly” parameter is used when you want to implement these files in another layer except API Layer.

Then this is how we use reflection to implement an extension method for ConfigureServices method:

public static class ServiceInstallerExtensions
    {
        public static void InstallServicesInAssemblies
               (this IServiceCollection services, AppSettings appSettings)
        {
            var startupProjectAssembly = Assembly.GetCallingAssembly();
            var assemblies = new[] { startupProjectAssembly, Assembly.GetExecutingAssembly() };
            var installers = assemblies.SelectMany(a => a.GetExportedTypes())
                .Where(c => c.IsClass && !c.IsAbstract 
                && c.IsPublic && typeof(IServiceInstaller).IsAssignableFrom(c))
                .Select(Activator.CreateInstance).Cast<IServiceInstaller>().ToList();
            installers.ForEach(i => i.InstallServices
                              (services, appSettings, startupProjectAssembly));
        }
    }

Eventually, after adding all services, we implement ConfigureServices method like this:

public void ConfigureServices(IServiceCollection services)
        {
            //* HttpContextAccessor
            // services.AddHttpContextAccessor();
            //* Controllers
            services.AddControllers(options => { options.Filters.Add(new AuthorizeFilter()); })
                .AddNewtonsoftJson();

            //* Installers
            services.InstallServicesInAssemblies(_appSettings);
        }

Finished. Now all the services with implementing the IServiceInstaller will be registered automatically in ConfigureServices.

We use AutoRegisterDi Package to automatically register DI Services. Now we build a class called:

public class RegisterServicesUsingAutoRegisterDiInstaller : IServiceInstaller
    {
        public void InstallServices
        (IServiceCollection services, AppSettings appSettings, Assembly startupProjectAssembly)
        {
            var dataAssembly = typeof(SomeRepository).Assembly;
            var serviceAssembly = typeof(SomeService).Assembly;
            var webFrameworkAssembly = Assembly.GetExecutingAssembly();
            var startupAssembly = startupProjectAssembly;
            var assembliesToScan = new[] 
            { dataAssembly, serviceAssembly, webFrameworkAssembly, startupAssembly };

            #region Generic Type Dependencies
            services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
            #endregion

            #region Scoped Dependency Interface
            services.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
                .Where(c => c.GetInterfaces().Contains(typeof(IScopedDependency)))
                .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);
            #endregion

            #region Singleton Dependency Interface
            services.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
                .Where(c => c.GetInterfaces().Contains(typeof(ISingletonDependency)))
                .AsPublicImplementedInterfaces(ServiceLifetime.Singleton);
            #endregion

            #region Transient Dependency Interface
            services.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
                .Where(c => c.GetInterfaces().Contains(typeof(ITransientDependency)))
                .AsPublicImplementedInterfaces(); // Default is Transient
            #endregion

            #region Register DIs By Name
            services.RegisterAssemblyPublicNonGenericClasses(dataAssembly)
                .Where(c => c.Name.EndsWith("Repository")
                            && !c.GetInterfaces().Contains(typeof(ITransientDependency))
                            && !c.GetInterfaces().Contains(typeof(IScopedDependency))
                            && !c.GetInterfaces().Contains(typeof(ISingletonDependency)))
                .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);

            services.RegisterAssemblyPublicNonGenericClasses(serviceAssembly)
                .Where(c => c.Name.EndsWith("Service")
                            && !c.GetInterfaces().Contains(typeof(ITransientDependency))
                            && !c.GetInterfaces().Contains(typeof(IScopedDependency))
                            && !c.GetInterfaces().Contains(typeof(ISingletonDependency)))
                .AsPublicImplementedInterfaces();
            #endregion
        }
    }

History

  • 10th April, 2020: Initial version