专栏首页DotNet程序园ASP.NET Core 3.0 原生DI拓展实现IocManager

ASP.NET Core 3.0 原生DI拓展实现IocManager

昨天.NET Core 3.0正式发布,创建一个项目运行后发现:原来使用的Autofac在ConfigureServices返回IServiceProvider的这种写法已经不再支持。

当然Autofac官方也给出了示例。.NET Core 本身内置DI,我决定不再使用Autofac,就使用原生DI,拓展IServiceCollection实现一个IocManager,

实现批量注入,静态获取实例能。末尾处含有Autofac IocManager实现方式。

一、Autofac官方文档

Program Class

Hosting changed in ASP.NET Core 3.0 and requires a slightly different integration. This is for ASP.NET Core 3+ and the .NET Core 3+ generic hosting support:

public class Program
{
  public static void Main(string[] args)
  {
    // ASP.NET Core 3.0+:
    // The UseServiceProviderFactory call attaches the
    // Autofac provider to the generic hosting mechanism.
    var host = Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureWebHostDefaults(webHostBuilder => {
          webHostBuilder
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>();
        })
        .Build();

    host.Run();
  }
}

Startup Class

In your Startup class (which is basically the same across all the versions of ASP.NET Core) you then use ConfigureContainer to access the Autofac container builder and register things directly with Autofac.

public class Startup
{
  public Startup(IHostingEnvironment env)
  {
    // In ASP.NET Core 3.0 env will be an IWebHostingEnvironment, not IHostingEnvironment.
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();
    this.Configuration = builder.Build();
  }

  public IConfigurationRoot Configuration { get; private set; }

  public ILifetimeScope AutofacContainer { get; private set; }

  // ConfigureServices is where you register dependencies. This gets
  // called by the runtime before the ConfigureContainer method, below.
  public void ConfigureServices(IServiceCollection services)
  {
    // Add services to the collection. Don't build or return
    // any IServiceProvider or the ConfigureContainer method
    // won't get called.
    services.AddOptions();
  }

  // ConfigureContainer is where you can register things directly
  // with Autofac. This runs after ConfigureServices so the things
  // here will override registrations made in ConfigureServices.
  // Don't build the container; that gets done for you. If you
  // need a reference to the container, you need to use the
  // "Without ConfigureContainer" mechanism shown later.
  public void ConfigureContainer(ContainerBuilder builder)
  {
      builder.RegisterModule(new AutofacModule());
  }

  // Configure is where you add middleware. This is called after
  // ConfigureContainer. You can use IApplicationBuilder.ApplicationServices
  // here if you need to resolve things from the container.
  public void Configure(
    IApplicationBuilder app,
    ILoggerFactory loggerFactory)
  {
    // If, for some reason, you need a reference to the built container, you
    // can use the convenience extension method GetAutofacRoot.
    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

    loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
    app.UseMvc();
  }
}

二、IocManager实现

1、创建IocManager

IIocManager接口

public interface IIocManager
{
    IServiceProvider ServiceProvider { get; set; }
}

IocManager实现

public class IocManager : IIocManager
{
    static IocManager()
    {
        Instance = new IocManager();
    }
    public static IocManager Instance { get; private set; }
    public IServiceProvider ServiceProvider { get; set; }
}

2、创建生命周期接口

/// <summary>
/// 标记依赖项生命周期的接口
/// <see cref="ILifetimeScopeDependency" />,
/// <see cref="ITransientDependency" />,
/// <see cref="ISingletonDependency" />
/// </summary>
public interface ILifetime { }
/// <summary>
/// 确定接口或类的生存期
/// 作用域模式,服务在每次请求时被创建,整个请求过程中都贯穿使用这个创建的服务。
/// </summary>
public interface ILifetimeScopeDependency : ILifetime { }
/// <summary>
/// 确定接口或类的生存期
/// 作用域模式,服务在每次请求时被创建,整个请求过程中都贯穿使用这个创建的服务。
/// </summary>
public interface ILifetimeScopeDependency : ILifetime { }
/// <summary>
/// 确定接口或类的生存期
/// 瞬态模式,每次请求时都会创建。
/// </summary>
public interface ITransientDependency : ILifetime { }

3、拓展IServiceCollection

/// <summary>
/// .NET Core 依赖注入拓展
/// </summary>
public static class DependencyInjectionExtensions
{
    /// <summary>
    /// 注册程序集组件
    /// </summary>
    /// <param name="services"></param>
    /// <param name="assemblies"></param>
    /// <returns></returns>
    public static IServiceCollection AddAssembly(this IServiceCollection services, params Assembly[] assemblies)
    {
        if (assemblies==null|assemblies.Count()==0)
        {
            throw new Exception("assemblies cannot be empty.");
        }
        foreach (var assembly in assemblies)
        {
            RegisterDependenciesByAssembly<ISingletonDependency>(services, assembly);
            RegisterDependenciesByAssembly<ITransientDependency>(services, assembly);
            RegisterDependenciesByAssembly<ILifetimeScopeDependency>(services, assembly);
        }
        return services;
    }

    public static void RegisterDependenciesByAssembly<TServiceLifetime>(IServiceCollection services, Assembly assembly)
    {            
        var types = assembly.GetTypes().Where(x => typeof(TServiceLifetime).GetTypeInfo().IsAssignableFrom(x) && x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract && !x.GetTypeInfo().IsSealed).ToList();
        foreach (var type in types)
        {
            var itype = type.GetTypeInfo().GetInterfaces().FirstOrDefault(x => x.Name.ToUpper().Contains(type.Name.ToUpper()));
            if (itype!=null)
            {
                var serviceLifetime = FindServiceLifetime(typeof(TServiceLifetime));
                services.Add(new ServiceDescriptor(itype, type, serviceLifetime));
            }
        }
    }

    private static ServiceLifetime FindServiceLifetime(Type type)
    {
        if (type == typeof(ISingletonDependency))
        {
            return ServiceLifetime.Singleton;
        }
        if (type == typeof(ITransientDependency))
        {
            return ServiceLifetime.Singleton;
        }
        if (type == typeof(ILifetimeScopeDependency))
        {
            return ServiceLifetime.Singleton;
        }

        throw new ArgumentOutOfRangeException($"Provided ServiceLifetime type is invalid. Lifetime:{type.Name}");
    }

    /// <summary>
    /// 注册IocManager
    /// 在ConfigureServices方法最后一行使用
    /// </summary>
    /// <param name="services"></param>
    public static void AddIocManager(this IServiceCollection services)
    {
        services.AddSingleton<IIocManager, IocManager>(provide =>
        {
            IocManager.Instance.ServiceProvider = provide;
            return IocManager.Instance;
        });
    }
}

4、IocManager使用实例:

4.1、示例程序集

namespace Service
{
    public interface IUserService
    {
        string GetUserNameById(string Id);
    }
    public interface UserService:IUserService,ISingletonDependency
    {
        public string GetUserNameById(string Id)
        {
         return "刘大大";
        }
    }
}

4.2、为程序集写一个拓展类

public static class ServiceExtensions
{
    public static IServiceCollection UseService(this IServiceCollection services)
    {
        var assembly = typeof(ServiceExtensions).Assembly;
        services.AddAssembly(assembly);
        return services;
    }
}

4.3、Web层使用

Startup class

public void ConfigureServices(IServiceCollection services)
 {
    services.UseService();
    services.AddControllersWithViews();
    services.AddIocManager();
}

Controller

IIocManager实现了单例模式,可以通过构造器注入获取实例,也可以通过通过IocManager.Instance获取实例

public class HomeController : Controller
{
    private readonly IIocManager _iocManager;
    public HomeController(IIocManager iocManager)
    {
        _iocManager = iocManager;
    }
    public string test1()
    {
     //通过注入获取IocManager实例
     var _userService=_iocManager.ServiceProvider.GetService<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }

    public string test2()
    {
     //通过IocManager获取IIocManager实例
     var _userService=IocManager.Instance.ServiceProvider.GetService<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }

    public string test3([FromServices]IUserService _userService)
    {
     //通过注入获取Service实例
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
}

5、Autofac IocManager实现

5.1、安装 Autofac.Extensions.DependencyInjection包

5.2、 IocManager实现

IIocManager接口

public interface IIocManager
{
    ILifetimeScope ServiceProvider { get; set; }
}

IocManager实现

public class IocManager : IIocManager
{
    static IocManager()
    {
        Instance = new IocManager();
    }
    public static IocManager Instance { get; private set; }
    public ILifetimeScope ServiceProvider { get; set; }
}

静态类 DependencyInjectionExtensions 添加UseIocManager方法。使用Autofac时可以在ConfigureContaine中直接注册内容,ConfigureContainer在ConfigureServices之后运行,

所以不能使用在ConfigureServices里注入IocManager,要在Configure方法中引用IocManager。

/// <summary>
/// 注册IocManager
/// 在Configure方法使用
/// </summary>
/// <param name="services"></param>
public static IApplicationBuilder UseIocManager(this IApplicationBuilder app)
{
    services.AddSingleton<IIocManager, IocManager>(provide =>
    {
        IocManager.Instance.ServiceProvider = app.ApplicationServices.GetAutofacRoot();
        return app;
    });
}

使用示例:

Startup class

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterModule(new AutofacModule());
}
public void Configure(IApplicationBuilder app,ILoggerFactory loggerFactory)
{
    app.UseIocManager();
}

Controller

public class HomeController : Controller
{
    private readonly IIocManager _iocManager;
    public HomeController(IIocManager iocManager)
    {
        _iocManager = iocManager;
    }
    public string test1()
    {
     //通过注入获取IocManager实例
     var _userService=_iocManager.ServiceProvider.Resolve<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }

    public string test2()
    {
     //通过IocManager获取IIocManager实例
     var _userService=IocManager.Instance.ServiceProvider.Resolve<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }

    public string test3([FromServices]IUserService _userService)
    {
     //通过注入获取Service实例
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
}

本文分享自微信公众号 - DotNet程序园(dotnetblog),作者:刘大大

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 什么样的代码是好代码?

    关于什么是好代码,软件行业烂大街的名词一大堆,什么高内聚、低耦合、可复用、可扩展、健壮性等等。也有所谓设计6原则—SOLID:

    梁规晓
  • 六大设计原则(C#)

    如果一个模块需要修改,它肯定是有原因的,除此原因之外,如果遇到了其他情况,还需要对此模块做出修改的话,那么就说这个模块就兼具多个职责。举个栗子:

    梁规晓
  • Asp.Net Core 轻松学-正确使用分布式缓存

        本来昨天应该更新的,但是由于各种原因,抱歉,让追这个系列的朋友久等了。上一篇文章 在.Net Core 使用缓存和配置依赖策略 讲的是如何使用本地缓存,...

    梁规晓
  • 图图谈设计模式_建造者设计模式_java

    还是那句话,设计模式是一种思想,编程靠的就是思想。高级编程就是面向接口和抽象类编程。望读者有个概念,框架源码最为体现,公司封装的框架我虽然没接触过,但是我相信也...

    聚沙成塔
  • 2016头条校招笔试题(LRU)算法之JAVA实现

    操作系统中可以使用LRU(Least Recently Used)内存淘汰旧数据的策略,如果内存需要加载新数据但空间不足,则会按照最近访问时间进行排序,并将最老...

    小柒2012
  • 万物可视之智能可视化管理平台

      Tarsier是优锘科技推出的一款可视化+大数据的IT运维管理产品,针对当前业务环境和技术环境下企业IT运维面临的结构复杂、数据碎片、变化常态、机制板结等问...

    要不要吃火锅
  • Retrofit初探和简单使用

    ? Retrofit简单介绍 Retrofit是Square提供的开源产品,为Android平台的应用提供一个类型安全的REST客户端。它是基于注解,提供 J...

    非著名程序员
  • LintCode 寻找丢失的数 II代码

    给一个由 1 - n 的整数随机组成的一个字符串序列,其中丢失了一个整数,请找到它。

    desperate633
  • MVVM + data-binding 快速入门

    用户1127566
  • 我最喜欢的Mybatis 3.5新特性!超实用!

    Mybatis 3.5 发布有段时间了,终于支持了 Optional ,这么实用的特性,竟然还没人安利……于是本文出现了。

    用户1516716

扫码关注云+社区

领取腾讯云代金券