首页 文章

在Startup.cs之外实现依赖注入

提问于
浏览
14

我想在 ASP.NET CORE 1 中实现依赖注入 . 我知道一切都与.Net Core中的DI有关 . 例如

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

但对于拥有20多个实体和服务的Big项目,在ConfigureServices中编写所有这些代码行是如此困难和难以理解 . 我想知道这是否可能在Startup.cs之外实现依赖注入,然后将其添加到服务中 .

谢谢你的回答 .

4 回答

  • 0

    您可以编写IServiceCollection的扩展方法,将大量服务注册封装到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);
    
  • 6

    有几种方法可以采用,但有些方法只是在类之间移动代码;我建议你考虑 Assembly Scanning ,我将其描述为下面的第二个选项:

    1. 'MOVE THE PROBLEM': EXTENSION METHODS

    最初的选项是使用 extension methods 来配置服务 .

    以下是将多个服务重新创建包装到一个扩展方法中的一个示例:

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

    Note: 这对于特定配置(例如,当服务需要向其传递多个选项时)作为'builder pattern'是有用的,但是,不能解决必须通过手动编码注册多个服务的问题;它与编写相同的代码但在不同的类文件中基本没什么不同,它仍然需要手动维护 .

    2. 'SOLVE THE PROBLEM': ASSEMBLY SCANNING

    'best practice'选项是Assembly Scanning,用于根据 Implemented Interfaces 自动查找和注册组件 . 下面是一个Autofac示例:

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

    处理注册生命周期(或范围)的一个技巧是使用标记接口(空接口),例如 IScopedService ,并使用它来扫描和注册具有适当生命周期的服务 . 这是注册多个服务的最低摩擦方法,这是自动的,因此'zero maintenance' .

    Note :内置的ASP.Net Core DI实现不支持 Assembly Scanning (如pf current,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());
    

    SUMMARY

    Assembly Scanning ,与 Extension Methods (适用时)相结合将为您节省大量维护费用,并在应用程序启动时执行一次,然后进行缓存 . 它不需要手动编码服务注册 .

  • 15

    您可以编写批量注册的扩展方法:

    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

    我最近实现了Assembly扫描方法(成功),但最终发现cluster_registrations_in_a_few_extension_methods方法更加清晰,可供我自己和其他程序员阅读 . 如果将注册聚类保持在已定义注册类的位置附近,则维护工作总是比注册类本身所涉及的维护工作少得多 .

相关问题