web-dev-qa-db-fra.com

Implémenter l'injection de dépendance en dehors de Startup.cs

Je veux implémenter dépendance dépendance injection dans ASP.NET CORE 1 . Je sais que tout est à propos de DI dans .Net Core. Par exemple

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

Mais pour les grands projets comptant plus de 20 entités et services, il est si difficile et illisible d’écrire toutes ces lignes de code dans ConfigureServices . Je souhaite savoir si cette injection de dépendance d'implémentation est possible en dehors de Startup.cs, puis ajoutée aux services.

Merci pour les réponses.

14
Elvin Mammadov

vous pouvez écrire des méthodes d'extension d'IServiceCollection pour encapsuler de nombreux enregistrements de service dans 1 ligne de code dans Startup.cs

par exemple en voici un de mon projet:

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;
        }


    }
}

et dans Startup.cs, j'appelle cette méthode avec une ligne de code

services.AddCloudscribeCore(Configuration);
13
Joe Audette

Plusieurs approches peuvent être adoptées, mais certaines ne font que déplacer du code entre les classes; Je suggère que vous considériez Assembly Scanning comme je le décris comme la deuxième option ci-dessous:

1. 'DÉPLACER LE PROBLÈME': MÉTHODES D'EXTENSION

L'option initiale consiste à utiliser extension methods pour la configuration des services.

Voici un exemple qui englobe plusieurs réigstrations de service dans une seule méthode d'extension:

    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;
    }

Ceci peut être appelé dans ConfigureServices:

services.AddCustomServices();

Remarque: Ceci est utile en tant que "modèle de générateur", pour des configurations spécifiques (par exemple, lorsqu'un service nécessite la transmission de plusieurs options), mais ne résout pas le problème de la nécessité d'enregistrer plusieurs. services de codage manuel; ce n'est essentiellement pas différent d'écrire le même code mais dans un fichier de classe différent, et cela nécessite toujours une maintenance manuelle.

2. 'RÉSOUDRE LE PROBLÈME': numérisation de l'ensemble

L'option 'meilleure pratique' est Assembly Scanning , qui permet de rechercher et d'enregistrer automatiquement les composants en fonction de leur Implemented Interfaces; Voici un exemple Autofac:

var Assembly= Assembly.GetExecutingAssembly();

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

Une astuce pour gérer la durée de vie (ou la portée) de l'enregistrement consiste à utiliser une interface de marqueur (une interface vide), par exemple IScopedService, et à l'utiliser pour rechercher et enregistrer des services avec la durée de vie appropriée. Il s’agit de la méthode de friction la plus basse pour l’enregistrement de plusieurs services, qui est automatique et, par conséquent, «maintenance zéro».

Remarque : L'implémentation ASP.Net Core DI intégrée ne prend pas en charge Assembly Scanning (version actuelle 2016 de pf); Cependant, le Scrutor project sur Github (et Nuget) ajoute cette fonctionnalité, qui condense l'enregistrement du service et du type à:

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());

R&EACUTE;SUM&EACUTE;:

Assembly Scanning, associé à Extension Methods (le cas échéant), vous évite une maintenance considérable. Il est effectué une fois au démarrage de l'application puis mis en cache. Cela évite d'avoir à gérer les enregistrements de service de code.

6
dmcquiggin

Vous pouvez écrire une méthode d'extension pour l'enregistrement par lots:

    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
            }
        }
    }

Et utilisation:

 services.AddScopedFromAssembly(Assembly);
3
adem caglin

J'ai récemment implémenté l'approche d'analyse (Assembly) (avec succès), mais j'ai finalement trouvé l'approche cluster_registrations_in_a_few_extension_methods beaucoup plus claire à lire pour moi-même et pour les autres programmeurs travaillant dessus. Si vous gardez le regroupement d'enregistrements proche de celui où les classes enregistrées sont définies, la maintenance représente toujours beaucoup moins de travail que la maintenance associée aux classes enregistrées elles-mêmes.

0
GreatBittern