参考地址:

https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn170416(v=pandp.10)

总览

Unity是一个轻量级的,可扩展的依赖项注入容器,支持构造函数,属性和方法调用注入。

  • 简化的对象创建,尤其是对于分层对象结构和依赖关系。
  • 需求抽象;这使开发人员可以在运行时或配置中指定依赖关系,并简化横切关注点的管理。
  • 通过将组件配置推迟到容器来提高灵活性。
  • 服务定位功能,允许客户端存储或缓存容器。
  • 实例和类型拦截(Unity for Windows Store应用程序不支持)。

Unity是一个通用容器,可用于任何类型的基于Microsoft.NET Framework的应用程序。它提供了依赖注入机制中常见的所有功能,包括注册类型映射和对象实例,解析对象,管理对象生存期以及将依赖对象注入到构造函数和方法的参数中以及作为对象属性值的方法。

Unity是可扩展的。您可以编写更改容器行为或添加新功能的容器扩展。例如,Unity提供的拦截功能可作为容器扩展来实现,您可以使用该功能来捕获对对象的调用并向目标对象添加其他功能和策略。

目标:松散耦合系统。

为什么要使用

优势

可维护性

可测试性:测试驱动开发(TDD)等方法要求您在编写任何代码以实现新功能之前编写单元测试,而这种设计技术的目标是提高应用程序的质量。

灵活性和可扩展性

后期绑定

并行开发

横切关注点(Crosscutting Concerns):验证,异常处理和日志处理

松耦合

例子

未使用Unit:Tenant租赁

TenantStore:一个存储库,该存储库处理对基础数据存储(如关系数据库)的访问。

ManagementController:一个MVC控制器类,它从存储库中请求数据。

ManagementController类必须实例化TenantStore对象或从其他位置获取对TenantStore对象的引用,然后才能调用GetTenantGetTenantNames方法。该ManagementController类依赖于特定的,具体TenantStore类。

public class TenantStore
{
... public Tenant GetTenant(string tenant)
{
...
} public IEnumerable<string> GetTenantNames()
{
...
}
} public class ManagementController
{
private readonly TenantStore tenantStore; public ManagementController()
{
tenantStore = new TenantStore(...);
} public ActionResult Index()
{
var model = new TenantPageViewData<IEnumerable<string>>
(this.tenantStore.GetTenantNames())
{
Title = "Subscribers"
};
return this.View(model);
} public ActionResult Detail(string tenant)
{
var contentModel = this.tenantStore.GetTenant(tenant);
var model = new TenantPageViewData<Tenant>(contentModel)
{
Title = string.Format("{0} details", contentModel.Name)
};
return this.View(model);
} ...
}

可能出现的问题:

  • 如果TenantStore变化,实现多态,这边的依赖已经被写死,不能更改
  • 在单元测试中ManagementController需要实例化TenantStore对象,测试变得复杂
  • 不能通过后期绑定的方式,在编译的过程中直接被编译成了TenantStore类。

针对接口编程,对类修改封闭,扩展开放,

改造:

public interface ITenantStore
{
void Initialize();
Tenant GetTenant(string tenant);
IEnumerable<string> GetTenantNames();
void SaveTenant(Tenant tenant);
void UploadLogo(string tenant, byte[] logo);
} public class TenantStore : ITenantStore
{
... public TenantStore()
{
...
} ...
} public class ManagementController : Controller
{
private readonly ITenantStore tenantStore; public ManagementController(ITenantStore tenantStore)
{
this.tenantStore = tenantStore;
} public ActionResult Index()
{
...
} public ActionResult Detail(string tenant)
{
...
} ...
}

好处:

  • 易于维护,当TenantStore类发生变化时,不需要修改控制器,
  • 实现后期绑定。
  • 两个团队可以在控制器和TenantStore上并行工作。

怎么用

什么时候使用?

什么位置使用?

常见依赖模式

工厂方法模式

public class ManagementController : Controller
{
protected ITenantStore tenantStore; public ManagementController()
{
this.tenantStore = CreateTenantStore();
} protected virtual ITenantStore CreateTenantStore()
{
var storageAccount = AppConfiguration
.GetStorageAccount("DataConnectionString");
var tenantBlobContainer = new EntitiesContainer<Tenant>
(storageAccount, "Tenants");
var logosBlobContainer = new FilesContainer
(storageAccount, "Logos",
"image/jpeg");
return new TenantStore(tenantBlobContainer,
logosBlobContainer);
} public ActionResult Index()
{
var model = new TenantPageViewData<IEnumerable<string>>
(this.tenantStore.GetTenantNames())
{
Title = "Subscribers"
};
return this.View(model);
} ...
}
public class SQLManagementController : ManagementController
{
protected override ITenantStore CreateTenantStore()
{
var storageAccount = ApplicationConfiguration
.GetStorageAccount("DataConnectionString");
var tenantSQLTable = ...
var logosSQLTable = ....
return new SQLTenantStore(tenantSQLTable, logosSQLTable);
} ...
}

该方法实现了对修改封闭对扩展开放,但还是很难测试ManangementController类,

简单工厂模式

public class ManagementController : Controller
{
private readonly ITenantStore tenantStore; public ManagementController()
{
var tenantStoreFactory = new TenantStoreFactory();
this.tenantStore = tenantStoreFactory.CreateTenantStore();
} public ActionResult Index()
{
var model = new TenantPageViewData<IEnumerable<string>>
(this.tenantStore.GetTenantNames())
{
Title = "Subscribers"
};
return this.View(model);
} ...
}

由一个全新的工厂TenantStore来负责创建,该模式消除了ManangementController对特定TenantStore的依赖。

大型应用程序中使用简单工厂可能引起问题是难以保持一致性,所以工厂要进行多态,可能需要各种工厂BlobStoreFactory和SqlStoreFactory,就是抽象工厂模式

服务定位器模式

服务定位模式可以视为一个注册表,应用程序中的一个类在该服务定位器中创建并注册对象或服务的实例。与工厂模式不同的是,服务定位器负责管理对象的生命周期,并将简单的应用返回给客户端。

具体可以查看https://martinfowler.com/articles/injection.html的使用服务定位器一节。

常见依赖的优缺点

常见依赖注入的共同特征是:高级客户端对象仍然对请求特定实例的对象存在依赖。而且其都采用不同复杂程度的拉模型将职责分配给工厂。拉模型还使得客户端对工厂也存在依赖,并隐藏在类的内部。

依赖注入则是通过推模型替代拉模型。

依赖注入时,另一个类负责在运行时将依赖项注入到客户端中,也就是ManagementController类,

public class ManagementController : Controller
{
private readonly ITenantStore tenantStore; public ManagementController(ITenantStore tenantStore)
{
this.tenantStore = tenantStore;
} public ActionResult Index()
{
var model = new TenantPageViewData<IEnumerable<string>>
(this.tenantStore.GetTenantNames())
{
Title = "Subscribers"
};
return this.View(model);
} ...
}

这样做的好处是客户端不了解负责实例化ITenantStore对象的类或组件。

依赖注入管理对象

怎么创建

DependencyInjectionContainer类可以管理多个类似工厂类的依赖,并提供一些附加功能,如生命周期的管理,拦截和按约定的注册。

如果使用依赖注入,需要将一些类或组件的依赖项伴随着构造函数传递进去,同时也意味着由一些类或组件必须负责这些依赖项的实例化并将它们传入到正确的构造函数、方法和属性。这些类和组件通常是:在控制台应用程序中是Main方法,Web应用程序中是Global.asax,Windows Azure的Role's Onstart方法。

生命周期

哪个对象负责管理状态,对象是否共享以及对象将生存多长时间。创建对象总是要花费有限的时间,该时间取决于对象的大小和复杂性,一旦创建了对象,它就会占用系统的某些内存。

可能希望每个客户端类都具有自己的ITenantStore对象,或者希望所有客户端类共享相同的ITenantStore实例,或者对于不同组的客户端类,每个客户端类都具有自己的ITenantStore实例。

构造函数注入

属性注入

public class AzureTable<T> : ...
{
public AzureTable(StorageAccount account)
: this(account, typeof(T).Name)
{
} ... public IAzureTableRWStrategy ReadWriteStrategy
{ get; set; } ...
}

使用属性注入时相比于构造函数注入有可能以往,但在依赖项可选的时候,应该使用属性注入器。

方法注入

public class MessageQueue<T> : ...
{
... public MessageQueue(StorageAccount account)
: this(account, typeof(T).Name.ToLowerInvariant())
{
} public MessageQueue(StorageAccount account,
string queueName)
{
...
} public void Initialize(TimeSpan visibilityTimeout,
IRetryPolicyFactory retryPolicyFactory)
{
...
} ...
}

方法注入:在此示例中提供了Initialize方法中注入,当提供一些正在使用的对象上下文时不能使用构造函数注入,可以使用方法注入的方法。

不应使用依赖注入

  • 依赖注入在小型应用程序中显得过大,从而导致额外的复杂性和不适当无用要求
  • 在大型应用程序中,依赖注入会使得代码和正在发生的事情变得难以理解
  • 类型注册和解析会导致性能损失:解析几乎可以忽略不计,注册只进行一次。
  • 依赖注入在功能上的重要性要小得多,当可测试性,故障恢复和并行性是关键要求时,函数式编程正变得越来越普遍。

Unity容器<1>的更多相关文章

  1. 【中英对照】【EntLib6】【Unity】实验1:使用一个Unity容器

    Lab 1: Using a Unity Container 实验1:使用一个Unity容器 Estimated time to complete this lab: 15 minutes 估计完成时 ...

  2. c#中的Unity容器

    DIP是依赖倒置原则:一种软件架构设计的原则(抽象概念).依赖于抽象不依赖于细节 IOC即为控制反转(Inversion of Control):传统开发,上端依赖(调用/指定)下端对象,会有依赖,把 ...

  3. Unity容器中AOP应用示例程序

    转发请注明出处:https://www.cnblogs.com/zhiyong-ITNote/p/9127001.html 实在没有找到Unity容器的AOP应用程序示例的说明,在微软官网找到了教程( ...

  4. Unity容器的简单AOP与DI的应用Demo(基于asp.net mvc框架)

    转发请注明出处:https://home.cnblogs.com/u/zhiyong-ITNote/ 整个Demo是基于Controller-Service-Repository架构设计的,每一层之间 ...

  5. Unity容器在asp.net mvc中的IOC应用及AOP应用

    <asp.net-mvc框架揭秘>一书中,有个示例,是使用unity容器来注入自定义的控制器工厂.代码示例可以自己去下载源码,在这里我就不说了.IOC容器的本质是解耦的实例化接口类,而如何 ...

  6. 【转载】C#中可使用Unity容器实现属性注入

    简介 Unity :Unity是微软团队开发的一个轻量级,可扩展的依赖注入容器,为松散耦合应用程序提供了很好的解决方案,支持构造器注入,属性注入,方法注入. 控制反转:(Inversion of Co ...

  7. Unity容器实现自动注册

    如何创建Unity容器? 首先NuGet搜索Unity, 该示例中使用的版本为4.0.1 新建控制台程序 示例中使用常规操作, 创建一个IPay接口, 分别有两个实现类: ApplePay.Huawe ...

  8. 2、手写Unity容器--第一层依赖注入

    这个场景跟<手写Unity容器--极致简陋版Unity容器>不同,这里构造AndroidPhone的时候,AndroidPhone依赖于1个IPad 1.IPhone接口 namespac ...

  9. 1、手写Unity容器--极致简陋版Unity容器

    模拟Unity容器实例化AndroidPhone 思路: 1.注册类型:把类型完整名称作为key添加到数据字典中,类型添加到数据字典的value中 2.获取实例:根据完整类型名称也就是key取出val ...

  10. Unity容器实现AOP面向切面编程

    为什么要有AOP 需求总是变化的,比如经常会对一些方法后期增加日志.异常处理.权限.缓存.事务的处理,遇到这种情况我们往往只能修改类. 为了应对变化,我们常常使用设计模式解决,但是也有其局限性:设计模 ...

随机推荐

  1. Python一秒搭建ftp服务器,帮助你在局域网共享文件【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  2. python 拷贝文件夹下的文件 到 另一个文件夹

    import os,shutil def copy_search_file(srcDir, desDir): ls = os.listdir(srcDir) for line in ls: fileP ...

  3. 蓝牙5.0芯片NRF52810和NRF52832可进行mesh组网

    提供智能化mesh照明解决方案,在现有传统灯具的基础上,插入NRF52832/52810的照明Mesh模块,可以迅速升级现有的传统灯具,配合手机APP和服务器系统,使每一盏灯成为物联网的一个智能节点, ...

  4. 揭秘 iOS App Extension 开发 —— Today 篇

    转自:http://www.cocoachina.com/ios/20160619/16760.html 本文授权转载,作者:Cyandev(简书) 从 iOS 8 开始,苹果引入了全新的 App E ...

  5. java 反射借助 asm 获取参数名称最优雅简单的方式

    背景说明 最近写反射相关的代码,想获取对应的参数名称,却发现没有特别好的方式. jdk7 及其以前,是无法通过反射获取参数名称的. jdk8 可以获取,但是要求指定 -parameter 启动参数,限 ...

  6. NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结

    NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结 经典NIM游戏: 一共有N堆石子,编号1..n,第i堆中有个a[i]个石子. 每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子 ...

  7. java之单例设计模式

    什么是设计模式? 设计模式是在大量的实践中总结和理论化之后优选的代码结构.编程风格.以及解决问题的思考方式.设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和探索. 所谓单例 ...

  8. Day 05 文本处理和爬虫基础1

    目录 什么是文件 什么是文本 如何通过文本编辑器控制.txt文件 打开文件的三种模式 t和b模式 高级应用 文本处理 + 词云分析 效果如下 爬虫原理 requests模块 re模块 爬取图片 爬取视 ...

  9. 【JS】382- JavaScript 模块化方案总结

    本文包含两部分,第一部分通过简明的描述介绍什么是 CommonJS.AMD.CMD.UMD.ES Module 以及它们的常见用法,第二部分则根据实际问题指出在正常的 webpack 构建过程中该如何 ...

  10. 【CSS】309- 复习 CSS盒模型

    点击上方"前端自习课"关注,学习起来~ 一.概念 CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:外边距(margin).边框(border).内边距(padding ...