威尼斯人线上娱乐

借助注入,如何编写3个简约的信赖注入容器

19 4月 , 2019  

趁着大面积的品类越多,大多类型都引进了依靠注入框架,在那之中最盛行的有Castle
温泽, Autofac和Unity Container。
微软在风行版的Asp.Net
Core中自带了依靠注入的作用,有意思味能够翻看那里。
至于怎么样是凭借注入容器网上已经有成都百货上千的篇章介绍,那里作者将第2讲述怎么着兑现3个和好的器皿,能够支持你明白依赖注入的法则。

乘势周围的品种更扩张,很多类别都引进了正视注入框架,当中最风靡的有Castle
温莎, Autofac和Unity Container。
微软在最新版的Asp.Net
Core中自带了依赖注入的功力,有乐趣可以查看那里。
关于什么是信赖注入容器网上早就有诸多的稿子介绍,这里作者将重大讲述怎么样促成二个投机的器皿,能够扶持您领会信赖注入的原理。

在前方的章节(Middleware章节)中,大家提到了依赖注入成效(Dependency
Injection),ASP.NET
五正式将凭借注入进行了全职能的落到实处,以便开荒人士能够开采更具弹性的机件程序,MVC6也采纳了借助注入的意义重新对Controller和View的劳动注入功效拓展了再一次设计;以往的借助注入功效还大概提供越来越多的API,全部即便还未曾起来接触依赖注入的话,就得好好学一下了。

在后边的章节(Middleware章节)中,我们关系了借助注入功用(Dependency
Injection),ASP.NET
5正式将依靠注入进行了全职能的贯彻,以便开荒职员能够开辟更具弹性的零部件程序,MVC陆也使用了依靠注入的效能重新对Controller和View的服务注入作用进行了重新规划;今后的信赖注入成效还可能提供越来越多的API,全数假诺还并未有从头接触正视注入的话,就得好好学一下了。

容器的构想

在编写制定容器从前,应该先想好这几个容器怎么样选拔。
容器允许注册服务和兑现项目,允许从服务类型得出服务的实例,它的采纳代码应该像

var container = new Container();

container.Register<MyLogger, ILogger>();

var logger = container.Resolve<ILogger>();

容器的构想

在编写容器此前,应该先想好这些容器怎么样使用。
容器允许注册服务和促成项目,允许从服务类型得出服务的实例,它的运用代码应该像

var container = new Container();

container.Register<MyLogger, ILogger>();

var logger = container.Resolve<ILogger>();

在前头版本的依靠注入功效里,正视注入的输入有MVC中的IControllerFactory和Web
API中的IHttpControllerActivator中,在新版ASP.NET第55中学,注重注入形成了最底部的基本功支撑,MVC、Routing、SignalEnclave、Entity
Framrwork等都凭借于依靠注入的IServiceProvider接口,针对该接口微软提交了暗中同意的落到实处ServiceProvider,以及Ninject和AutoFac版本的卷入,当然你也能够运用别的第一方的依靠注入容器,如Castle
温泽等;一旦采纳了第3方容器,全数的正视解析都会被路由到该第一方容器上。

在事先版本的注重注入效能里,注重注入的输入有MVC中的IControllerFactory和Web
API中的IHttpControllerActivator中,在新版ASP.NET第55中学,重视注入变成了最尾巴部分的底子支撑,MVC、Routing、SignalRubicon、Entity
Framrwork等都依赖于依靠注入的IServiceProvider接口,针对该接口微软交付了默许的兑现ServiceProvider,以及Ninject和AutoFac版本的卷入,当然你也能够选取任何第二方的依赖注入容器,如Castle
温莎等;①旦选取了第二方容器,全体的信赖性解析都会被路由到该第一方容器上。

最基础的容器

在上头的构想中,Container类有三个函数,三个是Register,一个是Resolve
容器须求在Register时关联ILogger接口到MyLogger兑现,并且需求在Resolve时明白应该为ILogger生成MyLogger的实例。
以下是兑现这三个函数最基础的代码

public class Container
{
    // service => implementation
    private IDictionary<Type, Type> TypeMapping { get; set; }

    public Container()
    {
        TypeMapping = new Dictionary<Type, Type>();
    }

    public void Register<TImplementation, TService>()
        where TImplementation : TService
    {
        TypeMapping[typeof(TService)] = typeof(TImplementation);
    }

    public TService Resolve<TService>()
    {
        var implementationType = TypeMapping[typeof(TService)];
        return (TService)Activator.CreateInstance(implementationType);
    }
}

Container在里边创建了二个服务类型(接口类型)到完毕项目的目录,Resolve时行使索引找到完毕项目并成立实例。
其壹落成很简短,可是有众多题目,例如

  • 1个服务类型不能够对应八个落成项目
  • 从未对实例进行生命周期管理
  • 并未兑现构造函数注入

最基础的器皿

在地点的构想中,Container类有多个函数,三个是Register,一个是Resolve
容器须要在Register时关联ILogger接口到MyLogger得以落成,并且要求在Resolve时知道应该为ILogger生成MyLogger的实例。
以下是落成那七个函数最基础的代码

public class Container
{
    // service => implementation
    private IDictionary<Type, Type> TypeMapping { get; set; }

    public Container()
    {
        TypeMapping = new Dictionary<Type, Type>();
    }

    public void Register<TImplementation, TService>()
        where TImplementation : TService
    {
        TypeMapping[typeof(TService)] = typeof(TImplementation);
    }

    public TService Resolve<TService>()
    {
        var implementationType = TypeMapping[typeof(TService)];
        return (TService)Activator.CreateInstance(implementationType);
    }
}

Container在其间创立了1个服务类型(接口类型)到完结项目的目录,Resolve时使用索引找到完毕项目并创立实例。
本条完结很简短,不过有广大标题,例如

  • 借助注入,如何编写3个简约的信赖注入容器。一个服务类型不能够对应几个落到实处项目
  • 从未有过对实例进行生命周期管理
  • 并未有落到实处构造函数注入

针对通用的重视性类型的解析与创制,微软默断定义了四连串型的生命周期,分别如下:

本着通用的正视性类型的分析与成立,微软默确定义了四种档次的生命周期,分别如下:

咬文嚼字容器的构想 – 类型索引类型

要让2个服务类型对应两个完毕项目,能够把TypeMapping改为

IDictionary<Type, IList<Type>> TypeMapping { get; set; }

假定其它提供叁个保存实例的变量,也能得以达成生命周期管理,但显得有点复杂了。
此处能够转移一下思路,把{服务类型=>完成项目}改为{服务类型=>工厂函数},让生命周期的管住在工厂函数中得以达成。

IDictionary<Type, IList<Func<object>>> Factories { get; set; }

突发性大家会想让用户在铺排文件中切换达成项目,那时要是把键类型改成服务类型+字符串,完毕起来会轻松诸多。
Resolve能够如此用:
Resolve<Service>(serviceKey: Configuration["ImplementationName"])

IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

革新容器的构想 – 类型索引类型

要让1个服务类型对应四个实现项目,能够把TypeMapping改为

IDictionary<Type, IList<Type>> TypeMapping { get; set; }

只要其它提供3个保留实例的变量,也能落成生命周期管理,但突显略微复杂了。
那里能够转变一下思路,把{服务类型=>完毕项目}改为{服务类型=>工厂函数},让生命周期的管制在工厂函数中贯彻。

IDictionary<Type, IList<Func<object>>> Factories { get; set; }

有时大家会想让用户在布置文件中切换落成项目,那时倘使把键类型改成服务类型+字符串,达成起来会轻巧很多。
Resolve能够如此用:
Resolve<Service>(serviceKey: Configuration["ImplementationName"])

IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }
类型 描述
Instance 任何时间都只能使用特定的实例对象,开发人员需要负责该对象的初始化工作。
Transient 每次都重新创建一个实例。
Singleton 创建一个单例,以后每次调用的时候都返回该单例对象。
Scoped 在当前作用域内,不管调用多少次,都是一个实例,换了作用域就会再次创建实例,类似于特定作用内的单例。
类型 描述
Instance 任何时间都只能使用特定的实例对象,开发人员需要负责该对象的初始化工作。
Transient 每次都重新创建一个实例。
Singleton 创建一个单例,以后每次调用的时候都返回该单例对象。
Scoped 在当前作用域内,不管调用多少次,都是一个实例,换了作用域就会再次创建实例,类似于特定作用内的单例。

立异容器的构想 – Register和Resolve的拍卖

在规定了索引类型后,RegisterResolve的拍卖都应有跟着变动。
Register挂号时应有率先根据贯彻项目改换工厂函数,再把工厂函数加到服务类型对应的列表中。
Resolve缓解时应当根据服务类型找到工厂函数,然后实施工厂函数重回实例。

寻行数墨容器的构想 – Register和Resolve的拍卖

在规定了索引类型后,RegisterResolve的拍卖都应该跟着退换。
Register挂号时应该率先依照贯彻项目更改工厂函数,再把工厂函数加到服务类型对应的列表中。
Resolve缓解时应当依照服务类型找到工厂函数,然后施行工厂函数再次来到实例。

品种注册与示范

重视注入类型的登记壹般是在先后运转的输入中,如Startup.cs中的ConfigureServices中,该类的第2目的就是挂号正视注入的项目。由于注重注入的机要体现是接口编制程序,所以本例中,笔者以接口和实现类的办法来比喻。

先是注明一个接口ITodoRepository和达成类TodoRepository一,代码如下:

public interface ITodoRepository
{
    IEnumerable<TodoItem> AllItems { get; }
    void Add(TodoItem item);
    TodoItem GetById(int id);
    bool TryDelete(int id);
}

public class TodoItem
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class TodoRepository : ITodoRepository
{
    readonly List<TodoItem> _items = new List<TodoItem>();

    public IEnumerable<TodoItem> AllItems
    {
        get { return _items; }
    }

    public TodoItem GetById(int id)
    {
        return _items.FirstOrDefault(x => x.Id == id);
    }

    public void Add(TodoItem item)
    {
        item.Id = 1 + _items.Max(x => (int?)x.Id) ?? 0;
        _items.Add(item);
    }

    public bool TryDelete(int id)
    {
        var item = GetById(id);

        if (item == null) { return false; }

        _items.Remove(item);

        return true;
    }
}

为了演示不一致的扬言周期类型,提出多达成多少个类,比如TodoRepository二、TodoRepository三、TodoRepository四等,以便进行现身说法。

下一场在ConfigureServices方法内注册接口ITodoRepository类型和相应的贯彻类,本例中遵照分化的生命周期注册了差异的落成类,具体示例如下:

//注册单例模式,整个应用程序周期内ITodoRepository接口的示例都是TodoRepository1的一个单例实例
services.AddSingleton<ITodoRepository, TodoRepository1>();
services.AddSingleton(typeof(ITodoRepository), typeof(TodoRepository1));  // 等价形式

//注册特定实例模型,整个应用程序周期内ITodoRepository接口的示例都是固定初始化好的一个单例实例

TodoRepository2
services.AddInstance<ITodoRepository>(new TodoRepository2());
services.AddInstance(typeof(ITodoRepository), new TodoRepository2());  // 等价形式

//注册作用域型的类型,在特定作用域内ITodoRepository的示例是TodoRepository3
services.AddScoped<ITodoRepository, TodoRepository3>();
services.AddScoped(typeof(ITodoRepository), typeof(TodoRepository3));// 等价形式

//获取该ITodoRepository实例时,每次都要实例化一次TodoRepository4类
services.AddTransient<ITodoRepository, TodoRepository4>();
services.AddTransient(typeof(ITodoRepository), typeof(TodoRepository));// 等价形式

//如果要注入的类没有接口,那你可以直接注入自身类型,比如:
services.AddTransient<LoggingHelper>();

依靠注入的在MVC中的使用办法当下有二种,分别是Controller的构造函数、属性以及View中的Inject形式。个中构造函数注入和在此之前的MVC中的是千篇壹律的,示例代码如下:

public class TodoController : Controller
{
    private readonly ITodoRepository _repository;

    /// 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该构造函数
    public TodoController(ITodoRepository repository)
    {
        _repository = repository;
    }

    [HttpGet]
    public IEnumerable<TodoItem> GetAll()
    {
        return _repository.AllItems;  //这里就可以使用该对象了
    }
}

属性注入,则是透过在性质上加八个[FromServices]属性就能够落成机关获取实例。

public class TodoController : Controller
{
    // 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该属性
    [FromServices]
    public ITodoRepository Repository { get; set; }

    [HttpGet]
    public IEnumerable<TodoItem> GetAll()
    {
        return Repository.AllItems;
    }
}

小心:那种办法,近来只适用于Controller以及子类,不适用于普通类
还要:通过那种方法,你可以取获得越多的系统实例对象,如ActionContextHttpContextHttpRequestHttpResponse
ViewDataDictionary、以及ActionBindingContext

在视图中,则足以因而@inject重要字来兑现注入类型的实例提取,示例如下:

@using WebApplication1
@inject ITodoRepository repository
<div>
    @repository.AllItems.Count()
</div>

而最相似的使用方法,则是赢得IServiceProvider的实例,获取该IServiceProvider实例的不二等秘书诀当下有如下二种(但范围区别):

var provider1 = this.Request.HttpContext.ApplicationServices; 当前应用程序里注册的Service
var provider2 = Context.RequestServices;  // Controller中,当前请求作用域内注册的Service
var provider3 = Resolver; //Controller中

下一场经过GetService和GetRequiredService方法来获取内定项目标实例,示例如下:

var _repository1 = provider1.GetService(typeof(ITodoRepository));
var _repository2 = provider1.GetService<LoggingHelper>();//等价形式
//上述2个对象可能为空

var _repository3 = provider1.GetRequiredService(typeof(ITodoRepository));
var _repository4 = provider1.GetRequiredService<LoggingHelper>();//等价形式
//上述2个对象肯定不为空,因为如果为空的话,会自动抛异常出来

体系注册与示范

借助注入类型的注册1般是在程序运转的入口中,如Startup.cs中的ConfigureServices中,该类的关键目标正是注册重视注入的项目。由于依赖注入的最主要反映是接口编制程序,所以本例中,小编以接口和落到实处类的秘籍来比喻。

率先声美素佳儿个接口ITodoRepository和得以达成类TodoRepository一,代码如下:

public interface ITodoRepository{    IEnumerable<TodoItem> AllItems { get; }    void Add(TodoItem item);    TodoItem GetById;    bool TryDelete;}public class TodoItem{    public int Id { get; set; }    public string Name { get; set; }}public class TodoRepository : ITodoRepository{    readonly List<TodoItem> _items = new List<TodoItem>();    public IEnumerable<TodoItem> AllItems    {        get { return _items; }    }    public TodoItem GetById    {        return _items.FirstOrDefault(x => x.Id == id);    }    public void Add(TodoItem item)    {        item.Id = 1 + _items.Max(x => x.Id) ?? 0;        _items.Add;    }    public bool TryDelete    {        var item = GetById;        if (item == null) { return false; }        _items.Remove;        return true;    }}

为了演示分裂的注脚周期类型,建议多达成多少个类,比如TodoRepository二、TodoRepository3、TodoRepository肆等,以便实行出现说法。

接下来在Configure瑟维斯s方法内注册接口ITodoRepository类型和对应的兑现类,本例中依据分化的生命周期注册了分化的贯彻类,具体示例如下:

//注册单例模式,整个应用程序周期内ITodoRepository接口的示例都是TodoRepository1的一个单例实例services.AddSingleton<ITodoRepository, TodoRepository1>();services.AddSingleton(typeof(ITodoRepository), typeof(TodoRepository1));  // 等价形式//注册特定实例模型,整个应用程序周期内ITodoRepository接口的示例都是固定初始化好的一个单例实例TodoRepository2services.AddInstance<ITodoRepository>(new TodoRepository2;services.AddInstance(typeof(ITodoRepository), new TodoRepository2;  // 等价形式//注册作用域型的类型,在特定作用域内ITodoRepository的示例是TodoRepository3services.AddScoped<ITodoRepository, TodoRepository3>();services.AddScoped(typeof(ITodoRepository), typeof(TodoRepository3));// 等价形式//获取该ITodoRepository实例时,每次都要实例化一次TodoRepository4类services.AddTransient<ITodoRepository, TodoRepository4>();services.AddTransient(typeof(ITodoRepository), typeof(TodoRepository));// 等价形式//如果要注入的类没有接口,那你可以直接注入自身类型,比如:services.AddTransient<LoggingHelper>();

借助注入的在MVC中的使用办法当下有二种,分别是Controller的构造函数、属性以及View中的Inject情势。个中构造函数注入和事先的MVC中的是同等的,示例代码如下:

public class TodoController : Controller{    private readonly ITodoRepository _repository;    /// 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该构造函数    public TodoController(ITodoRepository repository)    {        _repository = repository;    }    [HttpGet]    public IEnumerable<TodoItem> GetAll()    {        return _repository.AllItems;  //这里就可以使用该对象了    }}

质量注入,则是由此在品质上加四个[FromServices]品质就能够实现活动获得实例。

public class TodoController : Controller{    // 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该属性    [FromServices]    public ITodoRepository Repository { get; set; }    [HttpGet]    public IEnumerable<TodoItem> GetAll()    {        return Repository.AllItems;    }}

瞩目:那种方法,近年来只适用于Controller以及子类,不适用于普通类
而且:通过那种方式,你可以获得到越多的系统实例对象,如ActionContextHttpContextHttpRequestHttpResponse
ViewDataDictionary、以及ActionBindingContext

在视图中,则足以经过@inject根本字来兑现注入类型的实例提取,示例如下:

@using WebApplication1@inject ITodoRepository repository<div>    @repository.AllItems.Count()</div>

而最相似的选用办法,则是获得IServiceProvider的实例,获取该IServiceProvider实例的点子当下有如下两种:

var provider1 = this.Request.HttpContext.ApplicationServices; 当前应用程序里注册的Servicevar provider2 = Context.RequestServices;  // Controller中,当前请求作用域内注册的Servicevar provider3 = Resolver; //Controller中

下一场经过GetService和GetRequiredService方法来赢得钦定项目标实例,示例如下:

var _repository1 = provider1.GetService(typeof(ITodoRepository));var _repository2 = provider1.GetService<LoggingHelper>();//等价形式//上述2个对象可能为空var _repository3 = provider1.GetRequiredService(typeof(ITodoRepository));var _repository4 = provider1.GetRequiredService<LoggingHelper>();//等价形式//上述2个对象肯定不为空,因为如果为空的话,会自动抛异常出来

改进后的容器

以此容器新增加了贰个ResolveMany函数,用于缓解多少个实例。
除此以外还用了Expression.Lambda编写翻译工厂函数,生作用率会比Activator.CreateInstance快数10倍。

public class Container
{
    private IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

    public Container()
    {
        Factories = new Dictionary<Tuple<Type, string>, IList<Func<object>>>();
    }

    public void Register<TImplementation, TService>(string serviceKey = null)
        where TImplementation : TService
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            factories = new List<Func<object>>();
            Factories[key] = factories;
        }
        var factory = Expression.Lambda<Func<object>>(Expression.New(typeof(TImplementation))).Compile();
        factories.Add(factory);
    }

    public TService Resolve<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        var factory = Factories[key].Single();
        return (TService)factory();
    }

    public IEnumerable<TService> ResolveMany<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            yield break;
        }
        foreach (var factory in factories)
        {
            yield return (TService)factory();
        }
    }
}

改进后的器皿如故有以下的标题

  • 尚未对实例进行生命周期管理
  • 尚无完毕构造函数注入

勘误后的器皿

本条容器新添了二个ResolveMany函数,用于缓解八个实例。
其余还用了Expression.Lambda编写翻译工厂函数,生功用率会比Activator.CreateInstance快数拾倍。

public class Container
{
    private IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

    public Container()
    {
        Factories = new Dictionary<Tuple<Type, string>, IList<Func<object>>>();
    }

    public void Register<TImplementation, TService>(string serviceKey = null)
        where TImplementation : TService
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            factories = new List<Func<object>>();
            Factories[key] = factories;
        }
        var factory = Expression.Lambda<Func<object>>(Expression.New(typeof(TImplementation))).Compile();
        factories.Add(factory);
    }

    public TService Resolve<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        var factory = Factories[key].Single();
        return (TService)factory();
    }

    public IEnumerable<TService> ResolveMany<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            yield break;
        }
        foreach (var factory in factories)
        {
            yield return (TService)factory();
        }
    }
}

考订后的器皿依旧有以下的题材

  • 从不对实例举行生命周期管理
  • 从未有过兑现构造函数注入

普通类的依赖性注入

在新版的ASP.NET5中,不仅援助方面大家所说的接口类的重视性注入,还辅助一般的品种的依靠注入,比如大家生命3个普通类,示例如下:

public class AppSettings
{
    public string SiteTitle { get; set; }
}

上述普通类要保管有无参数构造函数,那么注册的用法,就应当像如下那样:

services.Configure<AppSettings>(app =>
{
    app.SiteTitle = "111";
});

运用的时候,则须要获得IOptions<AppSettings>类型的实例,然后其Options属性就是AppSettings的实例,代码如下:

var appSettings = app.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Options;

理所当然,大家也足以在视图中,使用@inject语法来赢得实例,示例代码如下:

@inject IOptions<AppSettings> AppSettings

<title>@AppSettings.Options.SiteTitle</title>

普通类的借助注入

在新版的ASP.NET5中,不仅协助地点大家所说的接口类的依赖注入,还援助壹般的花色的借助注入,比如大家生命二个普通类,示例如下:

public class AppSettings{    public string SiteTitle { get; set; }}

上述普通类要保管有无参数构造函数,那么注册的用法,就活该像如下那样:

services.Configure<AppSettings>(app =>{    app.SiteTitle = "111";});

使用的时候,则供给得到IOptions<AppSettings>种类的实例,然后其Options属性正是AppSettings的实例,代码如下:

var appSettings = app.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Options;

自然,大家也得以在视图中,使用@inject语法来取得实例,示例代码如下:

@inject IOptions<AppSettings> AppSettings<title>@AppSettings.Options.SiteTitle</title>

贯彻实例的单例

以下边代码为例

var logger_a = container.Resolve<ILogger>();
var logger_b = container.Resolve<ILogger>();

动用方面包车型大巴器皿试行那段代码时,logger_alogger_b是多少个差别的对象,借使想要每一次Resolve都回到同样的靶子啊?
大家得以对工厂函数举行包装,借助闭包(Closure)的才具能够格外简单的落到实处。

private Func<object> WrapFactory(Func<object> originalFactory, bool singleton)
{
    if (!singleton)
        return originalFactory;
    object value = null;
    return () =>
    {
        if (value == null)
            value = originalFactory();
        return value;
    };
}

加多那个函数后在Register中调用factory = WrapFactory(factory, singleton);即可。
完整代码就要前面放出,接下去再看哪样贯彻构造函数注入。

完结实例的单例

以上面代码为例

var logger_a = container.Resolve<ILogger>();
var logger_b = container.Resolve<ILogger>();

行使方面包车型地铁容器实践那段代码时,logger_alogger_b是四个例外的靶子,要是想要每一遍Resolve都回去同样的靶子呢?
大家得以对工厂函数举行打包,借助闭包(Closure)的才具能够相当简单的完结。

private Func<object> WrapFactory(Func<object> originalFactory, bool singleton)
{
    if (!singleton)
        return originalFactory;
    object value = null;
    return () =>
    {
        if (value == null)
            value = originalFactory();
        return value;
    };
}

增加那一个函数后在Register中调用factory = WrapFactory(factory, singleton);即可。
全体代码将要背后放出,接下去再看怎么着促成构造函数注入。

依照Scope生命周期的正视注入

传闻Scope生命周期的借助注入

达成构造函数注入

以上边代码为例

public class MyLogWriter : ILogWriter
{
    public void Write(string str)
    {
        Console.WriteLine(str);
    }
}

public class MyLogger : ILogger
{
    ILogWriter _writer;

    public MyLogger(ILogWriter writer)
    {
        _writer = writer;
    }

    public void Log(string message)
    {
        _writer.Write("[ Log ] " + message);
    }
}

static void Main(string[] args)
{
    var container = new Container();
    container.Register<MyLogWriter, ILogWriter>();
    container.Register<MyLogger, ILogger>();

    var logger = container.Resolve<ILogger>();
    logger.Log("Example Message");
}

在那段代码中,MyLogger协会时索要3个ILogWriter的实例,可是这些实例我们不能够直接传给它。
诸如此类将须要容器能够自动生成ILogWriter的实例,再传给MyLogger以生成MyLogger的实例。
要得以落成那么些职能须求使用c#中的反射机制。

把上边代码中的

var factory = Expression.Lambda<Func<object>>(Expression.New(typeof(TImplementation))).Compile();

换成

private Func<object> BuildFactory(Type type)
{
    // 获取类型的构造函数
    var constructor = type.GetConstructors().FirstOrDefault();
    // 生成构造函数中的每个参数的表达式
    var argumentExpressions = new List<Expression>();
    foreach (var parameter in constructor.GetParameters())
    {
        var parameterType = parameter.ParameterType;
        if (parameterType.IsGenericType &&
            parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            // 等于调用this.ResolveMany<TParameter>();
            argumentExpressions.Add(Expression.Call(
                Expression.Constant(this), "ResolveMany",
                parameterType.GetGenericArguments(),
                Expression.Constant(null, typeof(string))));
        }
        else
        {
            // 等于调用this.Resolve<TParameter>();
            argumentExpressions.Add(Expression.Call(
                Expression.Constant(this), "Resolve",
                new [] { parameterType },
                Expression.Constant(null, typeof(string))));
        }
    }
    // 构建new表达式并编译到委托
    var newExpression = Expression.New(constructor, argumentExpressions);
    return Expression.Lambda<Func<object>>(newExpression).Compile();
}

这段代码通过反射获取了构造函数中的全体参数,并对种种参数使用ResolveResolveMany解决。
值得注意的是参数的缓解是延迟的,唯有在创设MyLogger的时候才会营造MyLogWriter,这样做的裨益是流入的实例不自然需如若单例。
用表明式构建的厂子函数化解的时候的属性会非常高。

落成构造函数注入

以上面代码为例

public class MyLogWriter : ILogWriter
{
    public void Write(string str)
    {
        Console.WriteLine(str);
    }
}

public class MyLogger : ILogger
{
    ILogWriter _writer;

    public MyLogger(ILogWriter writer)
    {
        _writer = writer;
    }

    public void Log(string message)
    {
        _writer.Write("[ Log ] " + message);
    }
}

static void Main(string[] args)
{
    var container = new Container();
    container.Register<MyLogWriter, ILogWriter>();
    container.Register<MyLogger, ILogger>();

    var logger = container.Resolve<ILogger>();
    logger.Log("Example Message");
}

在那段代码中,MyLogger结构时索要3个ILogWriter的实例,不过那一个实例大家不可能直接传给它。
如此将须要容器能够自动生成ILogWriter的实例,再传给MyLogger以生成MyLogger的实例。
要贯彻这几个功用要求使用c#中的反射机制。

把地点代码中的

var factory = Expression.Lambda<Func<object>>(Expression.New(typeof(TImplementation))).Compile();

换成

private Func<object> BuildFactory(Type type)
{
    // 获取类型的构造函数
    var constructor = type.GetConstructors().FirstOrDefault();
    // 生成构造函数中的每个参数的表达式
    var argumentExpressions = new List<Expression>();
    foreach (var parameter in constructor.GetParameters())
    {
        var parameterType = parameter.ParameterType;
        if (parameterType.IsGenericType &&
            parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            // 等于调用this.ResolveMany<TParameter>();
            argumentExpressions.Add(Expression.Call(
                Expression.Constant(this), "ResolveMany",
                parameterType.GetGenericArguments(),
                Expression.Constant(null, typeof(string))));
        }
        else
        {
            // 等于调用this.Resolve<TParameter>();
            argumentExpressions.Add(Expression.Call(
                Expression.Constant(this), "Resolve",
                new [] { parameterType },
                Expression.Constant(null, typeof(string))));
        }
    }
    // 构建new表达式并编译到委托
    var newExpression = Expression.New(constructor, argumentExpressions);
    return Expression.Lambda<Func<object>>(newExpression).Compile();
}

这段代码通过反射获取了构造函数中的全数参数,并对种种参数使用ResolveResolveMany解决。
值得注意的是参数的消除是延迟的,只有在创设MyLogger的时候才会营造MyLogWriter,那样做的裨益是流入的实例不自然需假若单例。
用表明式创设的工厂函数解决的时候的属性会异常高。

普通的Scope正视注入

据说Scope功用域的实例在开立的时候须要先成立成效域,然后在该成效域内再获得一定的实例,大家看看一个示范并对其展开认证。首先,注册正视注入类型,代码如下:

services.AddScoped<ITodoRepository, TodoRepository>();

下一场创制功效域,并在该效用域内取得实例:

var serviceProvider = Resolver;

var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>(); //获取Scope工厂类
using (var scope = scopeFactory.CreateScope())  // 创建一个Scope作用域
{
    var containerScopedService = serviceProvider.GetService<ITodoRepository>();  //获取普通的实例
    var scopedService1 = scope.ServiceProvider.GetService<ITodoRepository>(); //获取当前Scope的实例
    Thread.Sleep(200);
    var scopedService2 = scope.ServiceProvider.GetService<ITodoRepository>(); //获取当前Scope的实例

    Console.WriteLine(containerScopedService == scopedService1); // 输出:False
    Console.WriteLine(scopedService1 == scopedService2); //输出:True
}

其它,Scope也得以拓展嵌套,嵌套的上下功能域所获得的实例也是不一致的,实例代码如下:

var serviceProvider = Resolver;

var outerScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
using (var outerScope = outerScopeFactory.CreateScope()) //外部Scope作用域
{
    var innerScopeFactory = outerScope.ServiceProvider.GetService<IServiceScopeFactory>();
    using (var innerScope = innerScopeFactory.CreateScope()) //内部Scope作用域
    {
        var outerScopedService = outerScope.ServiceProvider.GetService<ITodoRepository>();
        var innerScopedService = innerScope.ServiceProvider.GetService<ITodoRepository>();

        Console.WriteLine(outerScopedService == innerScopedService); // 输出:False
    }
}

平常的Scope正视注入

基于Scope功效域的实例在开立的时候须要先成立功能域,然后在该效能域内再取得特定的实例,大家看看1个演示并对其开始展览求证。首先,注册依赖注入类型,代码如下:

services.AddScoped<ITodoRepository, TodoRepository>();

下一场创立成效域,并在该效能域内获得实例:

var serviceProvider = Resolver;var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>(); //获取Scope工厂类using (var scope = scopeFactory.CreateScope  // 创建一个Scope作用域{    var containerScopedService = serviceProvider.GetService<ITodoRepository>();  //获取普通的实例    var scopedService1 = scope.ServiceProvider.GetService<ITodoRepository>(); //获取当前Scope的实例    Thread.Sleep;    var scopedService2 = scope.ServiceProvider.GetService<ITodoRepository>(); //获取当前Scope的实例    Console.WriteLine(containerScopedService == scopedService1); // 输出:False    Console.WriteLine(scopedService1 == scopedService2); //输出:True}

此外,Scope也得以开始展览嵌套,嵌套的上下作用域所得到的实例也是区别的,实例代码如下:

var serviceProvider = Resolver;var outerScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();using (var outerScope = outerScopeFactory.CreateScope //外部Scope作用域{    var innerScopeFactory = outerScope.ServiceProvider.GetService<IServiceScopeFactory>();    using (var innerScope = innerScopeFactory.CreateScope //内部Scope作用域    {        var outerScopedService = outerScope.ServiceProvider.GetService<ITodoRepository>();        var innerScopedService = innerScope.ServiceProvider.GetService<ITodoRepository>();        Console.WriteLine(outerScopedService == innerScopedService); // 输出:False    }}

完整代码

容器和演示的壹体化代码如下

public interface ILogWriter
{
    void Write(string text);
}

public class MyLogWriter : ILogWriter
{
    public void Write(string str)
    {
        Console.WriteLine(str);
    }
}

public interface ILogger
{
    void Log(string message);
}

public class MyLogger : ILogger
{
    ILogWriter _writer;

    public MyLogger(ILogWriter writer)
    {
        _writer = writer;
    }

    public void Log(string message)
    {
        _writer.Write("[ Log ] " + message);
    }
}

static void Main(string[] args)
{
    var container = new Container();
    container.Register<MyLogWriter, ILogWriter>();
    container.Register<MyLogger, ILogger>();
    var logger = container.Resolve<ILogger>();
    logger.Log("asdasdas");
}

public class Container
{
    private IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

    public Container()
    {
        Factories = new Dictionary<Tuple<Type, string>, IList<Func<object>>>();
    }

    private Func<object> WrapFactory(Func<object> originalFactory, bool singleton)
    {
        if (!singleton)
            return originalFactory;
        object value = null;
        return () =>
        {
            if (value == null)
                value = originalFactory();
            return value;
        };
    }

    private Func<object> BuildFactory(Type type)
    {
        // 获取类型的构造函数
        var constructor = type.GetConstructors().FirstOrDefault();
        // 生成构造函数中的每个参数的表达式
        var argumentExpressions = new List<Expression>();
        foreach (var parameter in constructor.GetParameters())
        {
            var parameterType = parameter.ParameterType;
            if (parameterType.IsGenericType &&
                parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                // 等于调用this.ResolveMany<TParameter>();
                argumentExpressions.Add(Expression.Call(
                    Expression.Constant(this), "ResolveMany",
                    parameterType.GetGenericArguments(),
                    Expression.Constant(null, typeof(string))));
            }
            else
            {
                // 等于调用this.Resolve<TParameter>();
                argumentExpressions.Add(Expression.Call(
                    Expression.Constant(this), "Resolve",
                    new [] { parameterType },
                    Expression.Constant(null, typeof(string))));
            }
        }
        // 构建new表达式并编译到委托
        var newExpression = Expression.New(constructor, argumentExpressions);
        return Expression.Lambda<Func<object>>(newExpression).Compile();
    }

    public void Register<TImplementation, TService>(string serviceKey = null, bool singleton = false)
        where TImplementation : TService
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            factories = new List<Func<object>>();
            Factories[key] = factories;
        }
        var factory = BuildFactory(typeof(TImplementation));
        WrapFactory(factory, singleton);
        factories.Add(factory);
    }

    public TService Resolve<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        var factory = Factories[key].Single();
        return (TService)factory();
    }

    public IEnumerable<TService> ResolveMany<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            yield break;
        }
        foreach (var factory in factories)
        {
            yield return (TService)factory();
        }
    }
}

一体化代码

容器和演示的全部代码如下

public interface ILogWriter
{
    void Write(string text);
}

public class MyLogWriter : ILogWriter
{
    public void Write(string str)
    {
        Console.WriteLine(str);
    }
}

public interface ILogger
{
    void Log(string message);
}

public class MyLogger : ILogger
{
    ILogWriter _writer;

    public MyLogger(ILogWriter writer)
    {
        _writer = writer;
    }

    public void Log(string message)
    {
        _writer.Write("[ Log ] " + message);
    }
}

static void Main(string[] args)
{
    var container = new Container();
    container.Register<MyLogWriter, ILogWriter>();
    container.Register<MyLogger, ILogger>();
    var logger = container.Resolve<ILogger>();
    logger.Log("asdasdas");
}

public class Container
{
    private IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

    public Container()
    {
        Factories = new Dictionary<Tuple<Type, string>, IList<Func<object>>>();
    }

    private Func<object> WrapFactory(Func<object> originalFactory, bool singleton)
    {
        if (!singleton)
            return originalFactory;
        object value = null;
        return () =>
        {
            if (value == null)
                value = originalFactory();
            return value;
        };
    }

    private Func<object> BuildFactory(Type type)
    {
        // 获取类型的构造函数
        var constructor = type.GetConstructors().FirstOrDefault();
        // 生成构造函数中的每个参数的表达式
        var argumentExpressions = new List<Expression>();
        foreach (var parameter in constructor.GetParameters())
        {
            var parameterType = parameter.ParameterType;
            if (parameterType.IsGenericType &&
                parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                // 等于调用this.ResolveMany<TParameter>();
                argumentExpressions.Add(Expression.Call(
                    Expression.Constant(this), "ResolveMany",
                    parameterType.GetGenericArguments(),
                    Expression.Constant(null, typeof(string))));
            }
            else
            {
                // 等于调用this.Resolve<TParameter>();
                argumentExpressions.Add(Expression.Call(
                    Expression.Constant(this), "Resolve",
                    new [] { parameterType },
                    Expression.Constant(null, typeof(string))));
            }
        }
        // 构建new表达式并编译到委托
        var newExpression = Expression.New(constructor, argumentExpressions);
        return Expression.Lambda<Func<object>>(newExpression).Compile();
    }

    public void Register<TImplementation, TService>(string serviceKey = null, bool singleton = false)
        where TImplementation : TService
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            factories = new List<Func<object>>();
            Factories[key] = factories;
        }
        var factory = BuildFactory(typeof(TImplementation));
        WrapFactory(factory, singleton);
        factories.Add(factory);
    }

    public TService Resolve<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        var factory = Factories[key].Single();
        return (TService)factory();
    }

    public IEnumerable<TService> ResolveMany<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            yield break;
        }
        foreach (var factory in factories)
        {
            yield return (TService)factory();
        }
    }
}

遵照HTTP请求的Scope依赖注入

在前边很多风行的DI容器中,针对种种请求,在该请求成效域内保留一个单实例对象是很盛行的,也正是在每回请求期间四个门类的对象实例只会创建贰遍,那样能够大大提升质量。

在ASP.NET5中,基于HTTP请求的Scope正视注入是透过三个ContainerMiddleware来完结的,调用该Middleware时,会成立3个范围作用域的DI容器,用于替换当前伏乞中已部分私下认可DI容器。在该管线中,全体继续的Middleware都会选用那么些新的DI容器,在伸手走完全体Pipeline管线今后,该ContainerMiddleware的机能就得了了,此时功效域会被灭绝,并且在该功效域内创造的实例对象也都会销毁释放。

ContainerMiddleware的时序图如下所示:

威尼斯人线上娱乐 1

实际的运用办法如下:

app.Use(new Func<RequestDelegate, RequestDelegate>(nextApp => new ContainerMiddleware(nextApp, app.ApplicationServices).Invoke));

听大人讲HTTP请求的Scope依赖注入

在事先诸多流行的DI容器中,针对各个请求,在该请求效能域内保留1个单实例对象是相当流行的,也正是在历次请求时期一个档次的靶子实例只会创建一次,那样能够大大升高质量。

在ASP.NET5中,基于HTTP请求的Scope正视注入是因而3个ContainerMiddleware来促成的,调用该Middleware时,会创建一个范围功能域的DI容器,用于替换当前乞请中已有些暗中认可DI容器。在该管线中,全体继续的Middleware都会使用那几个新的DI容器,在呼吁走完全部Pipeline管线以往,该ContainerMiddleware的职能就结束了,此时功用域会被销毁,并且在该成效域内创造的实例对象也都会销毁释放。

ContainerMiddleware的时序图如下所示:

威尼斯人线上娱乐 2

切实的行使办法如下:

app.Use(new Func<RequestDelegate, RequestDelegate>(nextApp => new ContainerMiddleware(nextApp, app.ApplicationServices).Invoke));

写在结尾

本条容器实现了三个凭借注入容器应该有个别重大职能,可是依然有点不清欠缺的地点,例如

  • 不扶助线程安全
  • 不支持非泛型的注册和缓解
  • 不援助只用于钦命范围内的单例
  • 不援救成员注入
  • 不援救动态代理达成AOP

我在ZKWeb网页框架中也运用了本身编写的器皿,唯有300多行可是足以知足实际项目的使用。
完全的源代码能够查阅那里和这里。

微软从.Net
Core起首提供了DependencyInjection的肤浅接口,那为借助注入提供了二个标准。
在今后恐怕不会再须要学习Castle 温泽,
Autofac等,而是一贯运用微软提供的标准接口。
即便实际的贯彻格局离大家原来越远,可是领悟一下它们的原理总是有好处的。

写在终极

本条容器达成了五个依靠注入容器应该有的根本成效,不过依旧有无数供不应求的地点,例如

  • 不帮忙线程安全
  • 不帮衬非泛型的挂号和缓解
  • 不扶助只用于钦定范围内的单例
  • 不扶助成员注入
  • 不援救动态代理达成AOP

我在ZKWeb网页框架中也利用了祥和编排的容器,唯有300多行然则能够满足实际项目的施用。
总体的源代码能够查看那里和这里。

微软从.Net
Core开首提供了DependencyInjection的思梅止渴接口,那为借助注入提供了1个正规。
在前些天说不定不会再必要上学Castle 温莎,
Autofac等,而是直接使用微软提供的标准接口。
虽说现实的贯彻形式离大家原来越远,可是理解一下它们的原理总是有裨益的。

普通类的信赖注入处理

日前普通类的依靠注入,只援救构造函数,比如大家定于一个TestService类,代码如下:

public class TestService
{
    private ITodoRepository _repository;
    public TestService(ITodoRepository r)
    {
        _repository = r;
    }

    public void Show()
    {
        Console.WriteLine(_repository.AllItems);
    }
}

透过在构造函数里传开ITodoRepository类的参数来利用该实例,使用的时候必要先将此类注册到DI容器中,代码如下:

services.AddScoped<ITodoRepository, TodoRepository>();
services.AddSingleton<TestService>();

接下来调用如下语句就可以使用:

var service = serviceProvider.GetRequiredService<TestService>();

其它,供给专注,在脚下的景况下,不能利用[FromServices]来行使信赖注入作用,比如,如下代码在收获TestService2实例的进程中会现身错误:

public class TestService2
{
    [FromServices]
    public ITodoRepository Repository { get; set; }
    public void Show()
    {
        Console.WriteLine(Repository.AllItems);
    }
}

普通类的借助注入处理

近期普通类的依靠注入,只帮助构造函数,比如大家定于四个TestService类,代码如下:

public class TestService{    private ITodoRepository _repository;    public TestService(ITodoRepository r)    {        _repository = r;    }    public void Show()    {        Console.WriteLine(_repository.AllItems);    }}

因而在构造函数里传出ITodoRepository类的参数来行使该实例,使用的时候须求先将该类注册到DI容器中,代码如下:

services.AddScoped<ITodoRepository, TodoRepository>();services.AddSingleton<TestService>();

下一场调用如下语句就可以使用:

var service = serviceProvider.GetRequiredService<TestService>();

除此以外,须要留意,在时下的图景下,不可能使用[FromServices]来使用依赖注入功效,比如,如下代码在得到TestService2实例的进度中会出现谬误:

public class TestService2{    [FromServices]    public ITodoRepository Repository { get; set; }    public void Show()    {        Console.WriteLine(Repository.AllItems);    }}

习认为常类中获得HttpContext实例

在MVC陆中,我们无法通过HttpContent.Current来博取上下文对象了,所以在平凡类中运用的时候就会出标题,要想在壹般类中央银行使该上下文对象,须要经过依赖注入来得到HttpContext实例,微软在ASP.NET5中,提供了IHttpContextAccessor接口用于获取该上下文对象。也便是说,我们得以将该类型的参数放在构造函数中,以博得上下文实例,代码如下:

public class TestService3
{
    private IHttpContextAccessor _httpContextAccessor;
    public TestService3(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Show()
    {
        var httpContext = _httpContextAccessor.HttpContext;//获取上下文对象实例
        Console.WriteLine(httpContext.Request.Host.Value);
    }
}

而利用的时候,则一贯通过如下语句就能够了,代码如下:

var service = serviceProvider.GetRequiredService<TestService3>();
service.Show();

提示:普通类的构造函数中,能够流传四个DI容器扶助的多少类似作为参数。

常备类中猎取HttpContext实例

在MVC陆中,我们无法通过HttpContent.Current来博取上下文对象了,所以在经常类中选择的时候就会出标题,要想在平常类中使用该上下文对象,要求经过正视注入来获得HttpContext实例,微软在ASP.NET5中,提供了IHttpContextAccessor接口用于获取该上下文对象。也正是说,我们能够将该类型的参数放在构造函数中,以获得上下文实例,代码如下:

public class TestService3{    private IHttpContextAccessor _httpContextAccessor;    public TestService3(IHttpContextAccessor httpContextAccessor)    {        _httpContextAccessor = httpContextAccessor;    }    public void Show()    {        var httpContext = _httpContextAccessor.HttpContext;//获取上下文对象实例        Console.WriteLine(httpContext.Request.Host.Value);    }}

而使用的时候,则平素通过如下语句就足以了,代码如下:

var service = serviceProvider.GetRequiredService<TestService3>();service.Show();

唤醒:普通类的构造函数中,能够流传多少个DI容器补助的多少类似作为参数。

运用第一方DI容器

当前,.NETCore不帮衬,只可以在全职能版的.NET
framework上中国人民解放军海军工程大学业夫使用,所以利用的时候需求留意一下。第3方DI容器的交替平常是在Startup.cs的Configure方法中展开的,在格局的始发处进行轮换,以便后续的Middleware会使用相关的重视注入功能。

第一要引进第一方的容器,以Autofac为例,引入Microsoft.Framework.DependencyInjection.Autofac,然后插手如下示例中的替换代码就能够:

app.UseServices(services =>
{
    services.AddMvc();// AddMvc要在这里注册
    var builder = new ContainerBuilder();// 构造容器构建类
    builder.Populate(services);//将现有的Services路由到Autofac的管理集合中
    IContainer container = builder.Build();
    return container.Resolve<IServiceProvider>();//返回AutoFac实现的IServiceProvider
});

瞩目,使用上述办法的时候,要把Mvc的挂号代码services.AddMvc();务须要从ConfigureServices威尼斯人线上娱乐 ,中挪到该表达式内,不然会报极度,等待微软消除。

此外,还有一个方式,微软脚下的实例项目中还未有公开,通过分析部分代码,大家得以窥见,在Microsoft.AspNet.Hosting先后中的StartupLoader.cs担当程序入口点的进行,在该文件中,大家知晓首先是调用Startup.cs中的ConfigureServices艺术,然后再调用Configure方法;大家得以见见示例中的ConfigureServices的重临值是void类型的,但在源码分析中发现,在遵照预订解析ConfigureServices办法的时候,其首先剖断有没有再次回到类型是IServiceProvider的,假若有则试行该方法,用利用该重临中回到的新IServiceProvider实例;未有的话,再持续搜寻void类型的ConfigureServices方法。所以,大家得以由此那种方法,来替换第一方的DI容器,实例代码如下:

// 需要先删除void类型的ConfigureServices方法
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    var builder = new ContainerBuilder();  // 构造容器构建类
    builder.Populate(services);  //将现有的Services路由到Autofac的管理集合中
    IContainer container = builder.Build();
    return container.Resolve<IServiceProvider>(); //返回AutoFac实现的IServiceProvider
}

那样,你就足以像在此以前1致,使用Autofac的方法实行依赖类型的田间管理了,示例如下:

public class AutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.Register(c => new Logger())
            .As<ILogger>()
            .InstancePerLifetimeScope();

        builder.Register(c => new ValuesService(c.Resolve<ILogger>()))
            .As<IValuesService>()
            .InstancePerLifetimeScope();
    }
}

地址:
别的一个有关Autofac集成的案例:

行使第三方DI容器

时下,.NETCore不支持,只幸亏全职能版的.NET
framework上本领运用,所以采纳的时候须求小心一下。第一方DI容器的更迭常常是在Startup.cs的Configure方法中打开的,在点子的早先处进行轮换,以便后续的Middleware会使用有关的信赖注入功效。

先是要引进第三方的器皿,以Autofac为例,引进Microsoft.Framework.DependencyInjection.Autofac,然后进入如下示例中的替换代码即可:

app.UseServices(services =>{    services.AddMvc();// AddMvc要在这里注册    var builder = new ContainerBuilder();// 构造容器构建类    builder.Populate;//将现有的Services路由到Autofac的管理集合中    IContainer container = builder.Build();    return container.Resolve<IServiceProvider>();//返回AutoFac实现的IServiceProvider});

瞩目,使用上述办法的时候,要把Mvc的注册代码services.AddMvc();务要求从ConfigureServices中挪到该表明式内,不然会报卓殊,等待微软解决。

其余,还有一个主意,微软近来的实例项目中还未曾公开,通过分析部分代码,咱们可以发现,在Microsoft.AspNet.Hosting次第中的StartupLoader.cs承担程序入口点的实施,在该公文中,我们掌握首先是调用Startup.cs中的ConfigureServices方法,然后再调用Configure主意;大家可以见到示例中的ConfigureServices的重回值是void类型的,但在源码分析中发觉,在依照约定解析ConfigureServices主意的时候,其首先推断有未有再次来到类型是IServiceProvider的,假使有则施行该措施,用利用该再次回到中回到的新IServiceProvider实例;未有的话,再持续查找void类型的ConfigureServices办法。所以,大家能够透过那种情势,来替换第3方的DI容器,实例代码如下:

// 需要先删除void类型的ConfigureServices方法public IServiceProvider ConfigureServices(IServiceCollection services){    var builder = new ContainerBuilder();  // 构造容器构建类    builder.Populate;  //将现有的Services路由到Autofac的管理集合中    IContainer container = builder.Build();    return container.Resolve<IServiceProvider>(); //返回AutoFac实现的IServiceProvider}

这么,你就足以像往常1律,使用Autofac的法子展开信赖类型的治本了,示例如下:

public class AutofacModule : Module{    protected override void Load(ContainerBuilder builder)    {        builder.Register(c => new Logger            .As<ILogger>()            .InstancePerLifetimeScope();        builder.Register(c => new ValuesService(c.Resolve<ILogger>            .As<IValuesService>()            .InstancePerLifetimeScope();    }}

地址:
其余四个有关Autofac集成的案例:

最好实行

在行使正视注入的的时候,我们应该依照如下最好实施。

  1. 做其它业务从前,务必在程序入口点提前注册全体的依靠类型。
  2. 幸免直接运用IServiceProvider接口,相反,在构造函数里显式增多须求注重的连串就能够,让注重注入引擎本身来分析实例,1旦信赖很难管理以来,就应用抽象工厂。
  3. 据他们说接口实行编制程序,而不是遵照实现进行编制程序。

参考1:
参考2:

一流施行

在运用依赖注入的的时候,大家相应坚守如下最棒推行。

  1. 做其余交事务情在此以前,务必在程序入口点提前注册全体的依赖类型。
  2. 幸免直接利用IServiceProvider接口,相反,在构造函数里显式增加须求借助的项目就可以,让依赖注入引擎自身来分析实例,一旦正视很难管理的话,就动用抽象工厂。
  3. 依据接口进行编制程序,而不是基于达成举行编制程序。

参考1:
参考2:

一路与推荐介绍

正文已同步至目录索引:解读ASP.NET 5 &
MVC6系列

共同与推荐

正文已同步至目录索引:解读ASP.NET 5 & MVC陆名目许多


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图