web-dev-qa-db-ja.com

Startup.csの外部に依存性注入を実装する

ASP.NET CORE 1依存性注入を実装したい。 .Net CoreのDIについては、すべてがわかっています。例えば

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

しかし、20を超えるエンティティとサービスを含む大規模なプロジェクトの場合、これらのコード行をすべてConfigureServices内に書き込むことは非常に困難で読み取り不可能です。知りたいこれは、Startup.csの外部に依存性注入を実装してから、サービスに追加できるかどうかです。

回答ありがとうございます。

19
Elvin Mammadov

iServiceCollectionの拡張メソッドを記述して、多数のサービス登録をStartup.csの1行のコードにカプセル化できます。

たとえば、これは私のプロジェクトからのものです:

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で私は1行のコードでそのメソッドを呼び出します

services.AddCloudscribeCore(Configuration);
18
Joe Audette

いくつかの方法がありますが、クラス間でコードを移動するだけのものもあります。以下の2番目のオプションとして説明するように、Assembly Scanningを検討することをお勧めします。

1。 'MOVE THE PROBLEM':拡張メソッド

最初のオプションは、サービスの構成にextension methodsを使用することです。

複数のサービス登録を1つの拡張メソッドにラップする1つの例を次に示します。

    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。 「問題を解決する」:アセンブリスキャン

「ベストプラクティス」オプションは Assembly Scanning であり、Implemented Interfaces;に基づいてコンポーネントを自動的に検索して登録するために使用されます。以下はAutofacの例です。

var Assembly= Assembly.GetExecutingAssembly();

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

登録の有効期間(またはスコープ)を処理する1つのトリックは、マーカーインターフェイス(空のインターフェイス)、たとえばIScopedServiceを使用し、適切な有効期間でサービスをスキャンして登録することです。これは、複数のサービスを登録するための最も簡単な方法であり、自動化されているため、「メンテナンス不要」です。

:組み込みのASP.Net Core DI実装はAssembly Scanningをサポートしていません(現在の2016年リリースの場合)。ただし、Github(およびNuget)の Scrutor プロジェクトはこの機能を追加し、サービスとタイプの登録を次のように凝縮します。

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 ScanningExtension Methods(該当する場合)と組み合わせて使用​​すると、かなりの量のメンテナンスを節約でき、アプリケーションの起動時に1回実行され、その後キャッシュされます。これにより、サービス登録を手動でコーディングする必要がなくなります。

8
dmcquiggin

一括登録用の拡張メソッドを作成できます。

    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);
3
adem caglin

私は最近、アセンブリスキャンアプローチを実装しました(成功しました)が、結局、cluster_registrations_in_a_few_extension_methodsのアプローチは、自分自身とそれに取り組んでいる他のプログラマにとって非常にわかりやすくなっています。登録されたクラスが定義されている場所の近くに登録のクラスタリングを維持する場合、メンテナンスは、登録されたクラス自体に関連するメンテナンスよりも常にはるかに少ない作業です。

0
GreatBittern