Внедрение зависимостей вне Startup.cs

Я хочу реализовать инъекцию зависимостей в ASP.NET CORE 1. Я знаю, что все об DI в.Net Core. Например

   public void ConfigureServices(IServiceCollection services)
   {
      // Add application services.
     services.AddTransient<IDateTime, SystemDateTime>();
   }

Но для крупных проектов с более чем 20 объектами и службами это так сложно и нечитаемо, записывая все эти строки кода внутри ConfigureServices. Я хочу знать. Возможно ли это реализовать инъекцию зависимостей внедрения за пределами Startup.cs, а затем добавить ее в службы.

Спасибо за ответы.

Ответ 1

вы можете написать методы расширения IServiceCollection, чтобы инкапсулировать множество регистраций услуг в 1 строку кода в Startup.cs

например, вот один из моего проекта:

using cloudscribe.Core.Models;
using cloudscribe.Core.Models.Setup;
using cloudscribe.Core.Web;
using cloudscribe.Core.Web.Components;
using cloudscribe.Core.Web.Components.Editor;
using cloudscribe.Core.Web.Components.Messaging;
using cloudscribe.Core.Web.Navigation;
using cloudscribe.Web.Common.Razor;
using cloudscribe.Web.Navigation;
using cloudscribe.Web.Navigation.Caching;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class StartupExtensions
    {
        public static IServiceCollection AddCloudscribeCore(this IServiceCollection services, IConfigurationRoot configuration)
        {
            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.Configure<MultiTenantOptions>(configuration.GetSection("MultiTenantOptions"));
            services.Configure<SiteConfigOptions>(configuration.GetSection("SiteConfigOptions"));
            services.Configure<UIOptions>(configuration.GetSection("UIOptions"));
            services.Configure<CkeditorOptions>(configuration.GetSection("CkeditorOptions"));
            services.Configure<CachingSiteResolverOptions>(configuration.GetSection("CachingSiteResolverOptions"));
            services.AddMultitenancy<SiteContext, CachingSiteResolver>();
            services.AddScoped<CacheHelper, CacheHelper>();
            services.AddScoped<SiteManager, SiteManager>();
            services.AddScoped<GeoDataManager, GeoDataManager>();
            services.AddScoped<SystemInfoManager, SystemInfoManager>();
            services.AddScoped<IpAddressTracker, IpAddressTracker>();
            services.AddScoped<SiteDataProtector>();
            services.AddCloudscribeCommmon();
            services.AddScoped<ITimeZoneIdResolver, RequestTimeZoneIdResolver>();
            services.AddCloudscribePagination();
            services.AddScoped<IVersionProviderFactory, VersionProviderFactory>();
            services.AddScoped<IVersionProvider, CloudscribeCoreVersionProvider>();
            services.AddTransient<ISiteMessageEmailSender, SiteEmailMessageSender>();
            services.AddTransient<ISmsSender, SiteSmsSender>();
            services.AddSingleton<IThemeListBuilder, SiteThemeListBuilder>();
            services.TryAddScoped<ViewRenderer, ViewRenderer>();
            services.AddSingleton<IOptions<NavigationOptions>, SiteNavigationOptionsResolver>();
            services.AddScoped<ITreeCacheKeyResolver, SiteNavigationCacheKeyResolver>();
            services.AddScoped<INodeUrlPrefixProvider, FolderTenantNodeUrlPrefixProvider>();
            services.AddCloudscribeNavigation(configuration);

            services.AddCloudscribeIdentity();

            return services;
        }


    }
}

и в Startup.cs Я вызываю этот метод с одной строкой кода

services.AddCloudscribeCore(Configuration);

Ответ 2

Существует несколько подходов, которые могут быть предприняты, но некоторые из них просто перемещают код между классами; Я предлагаю вам рассмотреть Assembly Scanning как я описываю в качестве второго варианта ниже:

1. "ПЕРЕМЕЩЕНИЕ ПРОБЛЕМЫ": МЕТОДЫ РАСШИРЕНИЯ

Первоначальный вариант - использовать extension methods для настройки Сервисов.

Вот один пример, который переносит несколько регенераций служб в один метод расширения, взятый из шаблона ASP.Net MVC Bolierplate, который можно найти здесь:

    public static IServiceCollection AddCustomServices(this IServiceCollection services)
    {
        services.AddScoped<IBrowserConfigService, BrowserConfigService>();
        services.AddScoped<IManifestService, ManifestService>();
        services.AddScoped<IRobotsService, RobotsService>();
        services.AddScoped<ISitemapService, SitemapService>();
        services.AddScoped<ISitemapPingerService, SitemapPingerService>();

        // Add your own custom services here e.g.

        // Singleton - Only one instance is ever created and returned.
        services.AddSingleton<IExampleService, ExampleService>();

        // Scoped - A new instance is created and returned for each request/response cycle.
        services.AddScoped<IExampleService, ExampleService>();

        // Transient - A new instance is created and returned each time.
        services.AddTransient<IExampleService, ExampleService>();

        return services;
    }

Это можно вызвать в ConfigureServices:

services.AddCustomServices();

Примечание. Это полезно в качестве "шаблона построителя" для определенных конфигураций (например, когда службе требуется несколько параметров, которые должны быть переданы ей), но не решает проблему регистрации нескольких сервисов ручным кодированием; по существу, не отличается от написания одного и того же кода, но в другом файле класса, и он по-прежнему нуждается в ручном обслуживании.

2. "РЕШЕНИЕ ПРОБЛЕМЫ": СКАНИРОВАНИЕ СБОРКИ

Опцией "лучшей практики" является " Сборочное сканирование", которое используется для автоматического поиска и регистрации компонентов на основе их Implemented Interfaces; ниже приведен пример Autofac:

var assembly= Assembly.GetExecutingAssembly();

builder.RegisterAssemblyTypes(assembly)
       .Where(t => t.Name.EndsWith("Repository"))
       .AsImplementedInterfaces();

Один трюк для обработки времени жизни (или области) регистрации - использовать интерфейс маркера (пустой интерфейс), например IScopedService, и использовать его для сканирования и регистрации служб с соответствующим временем жизни. Это самый низкий подход к трению при регистрации нескольких сервисов, который является автоматическим и, следовательно, "нулевым обслуживанием".

Примечание. Встроенная реализация ASP.Net Core DI не поддерживает Assembly Scanning; однако проект Scrutor в Github (и Nuget) добавляет эту функциональность, которая конденсирует регистрацию службы и типа:

var collection = new ServiceCollection();

collection.Scan(scan => scan
    .FromAssemblyOf<ITransientService>()
        .AddClasses(classes => classes.AssignableTo<ITransientService>())
            .AsImplementedInterfaces()
            .WithTransientLifetime()
        .AddClasses(classes => classes.AssignableTo<IScopedService>())
            .As<IScopedService>()
            .WithScopedLifetime());

РЕЗЮМЕ:

Assembly Scanning в сочетании с Extension Methods (там, где это применимо) сэкономит вам значительное количество обслуживания и выполняется один раз при запуске приложения и затем кэшируется. Это избавляет от необходимости передавать регистрацию услуг кодов.

Ответ 3

Вы можете написать метод расширения для пакетной регистрации:

    public static void AddScopedFromAssembly(this IServiceCollection services, Assembly assembly)
    {
        var allServices = assembly.GetTypes().Where(p =>
            p.GetTypeInfo().IsClass &&
            !p.GetTypeInfo().IsAbstract);
        foreach (var type in allServices)
        {
            var allInterfaces = type.GetInterfaces();
            var mainInterfaces = allInterfaces.Except
                    (allInterfaces.SelectMany(t => t.GetInterfaces()));
            foreach (var itype in mainInterfaces)
            {
                services.AddScoped(itype, type); // if you want you can pass lifetime as a parameter
            }
        }
    }

И использование:

 services.AddScopedFromAssembly(assembly);

Ответ 4

Недавно я реализовал подход сканирования сборок (успешно), но в конце нашел, что метод cluster_registrations_in_a_few_extension_methods намного понятнее для чтения и для других программистов, работающих над ним. Если вы храните кластеризацию регистраций близко к тому, где определены зарегистрированные классы, обслуживание всегда намного меньше, чем обслуживание, связанное с самими зарегистрированными классами.