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

Stella981
• 阅读 673

昨天**.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 ISingletonDependency: 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 class 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()
        {
         //通过IocManagerIocManager实例
         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 AutofacContainer { get; set; }
        TService GetInstance<TService>();
}

IocManager实现

    public class IocManager : IIocManager
    {
        static IocManager()
        {
            Instance = new IocManager();
        }
        public static IocManager Instance { get; private set; }
        /// <summary>
        /// Autofac容器
        /// </summary>
        public ILifetimeScope AutofacContainer { get; set; }

        public TService GetInstance<TService>()
        {
            return AutofacContainer.Resolve<TService>();
        }
    }

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

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

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

        /// <summary>
        /// 注册程序集
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="assembly">The assembly.</param>
        private static void RegisterDependenciesByAssembly<TServiceLifetime>(ContainerBuilder builder, 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.IsNull()) continue;
                if (typeof(TServiceLifetime) == typeof(ISingletonDependency))
                {
                    builder.RegisterType(type).As(itype).SingleInstance();
                }
                if (typeof(TServiceLifetime) == typeof(ITransientDependency))
                {
                    builder.RegisterType(type).As(itype).InstancePerDependency();
                }
                if (typeof(TServiceLifetime) == typeof(ILifetimeScopeDependency))
                {
                    builder.RegisterType(type).As(itype).InstancePerLifetimeScope();
                }
            }
        }

        /// <summary>
        /// 注册IocManager
        /// </summary>
        /// <param name="services"></param>
        public static void UseIocManager(this IApplicationBuilder app)
        {
            IocManager.Instance.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
        }
    }

Service

    /// <summary>
    /// Application拓展类
    /// </summary>
    public static class ServiceExtensions
    {
        /// <summary>
        /// 注入Application容器
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static ContainerBuilder RegisterService(this ContainerBuilder builder)
        {
            var assembly = typeof(ApplicationExtensions).Assembly;
            builder.RegisterAssembly(assembly);
            return builder;
        }
    }

Startup

        public void ConfigureContainer(ContainerBuilder builder)
        {
           builder.RegisterService();
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           app.UseIocManager();
        }
Controller
public class HomeController : Controller
{
    private readonly IIocManager _iocManager;
    public HomeController(IIocManager iocManager)
    {
        _iocManager = iocManager;
    }
    public string test1()
    {
     //通过注入获取IocManager实例
     var _userService=_iocManager.GetInstance<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
    
    public string test2()
    {
     //通过IocManager获取IIocManager实例
     var _userService=IocManager.Instance.GetInstance<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
    
    public string test3([FromServices]IUserService _userService)
    {
     //通过注入获取Service实例
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
}

下载完整源码

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
添砖java的啾 添砖java的啾
3年前
distinct效率更高还是group by效率更高?
目录00结论01distinct的使用02groupby的使用03distinct和groupby原理04推荐groupby的原因00结论先说大致的结论(完整结论在文末):在语义相同,有索引的情况下groupby和distinct都能使用索引,效率相同。在语义相同,无索引的情况下:distinct效率高于groupby。原因是di
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这