Unity容器<1>
参考地址:
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对象的引用,然后才能调用GetTenant和GetTenantNames方法。该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>的更多相关文章
- 【中英对照】【EntLib6】【Unity】实验1:使用一个Unity容器
Lab 1: Using a Unity Container 实验1:使用一个Unity容器 Estimated time to complete this lab: 15 minutes 估计完成时 ...
- c#中的Unity容器
DIP是依赖倒置原则:一种软件架构设计的原则(抽象概念).依赖于抽象不依赖于细节 IOC即为控制反转(Inversion of Control):传统开发,上端依赖(调用/指定)下端对象,会有依赖,把 ...
- Unity容器中AOP应用示例程序
转发请注明出处:https://www.cnblogs.com/zhiyong-ITNote/p/9127001.html 实在没有找到Unity容器的AOP应用程序示例的说明,在微软官网找到了教程( ...
- Unity容器的简单AOP与DI的应用Demo(基于asp.net mvc框架)
转发请注明出处:https://home.cnblogs.com/u/zhiyong-ITNote/ 整个Demo是基于Controller-Service-Repository架构设计的,每一层之间 ...
- Unity容器在asp.net mvc中的IOC应用及AOP应用
<asp.net-mvc框架揭秘>一书中,有个示例,是使用unity容器来注入自定义的控制器工厂.代码示例可以自己去下载源码,在这里我就不说了.IOC容器的本质是解耦的实例化接口类,而如何 ...
- 【转载】C#中可使用Unity容器实现属性注入
简介 Unity :Unity是微软团队开发的一个轻量级,可扩展的依赖注入容器,为松散耦合应用程序提供了很好的解决方案,支持构造器注入,属性注入,方法注入. 控制反转:(Inversion of Co ...
- Unity容器实现自动注册
如何创建Unity容器? 首先NuGet搜索Unity, 该示例中使用的版本为4.0.1 新建控制台程序 示例中使用常规操作, 创建一个IPay接口, 分别有两个实现类: ApplePay.Huawe ...
- 2、手写Unity容器--第一层依赖注入
这个场景跟<手写Unity容器--极致简陋版Unity容器>不同,这里构造AndroidPhone的时候,AndroidPhone依赖于1个IPad 1.IPhone接口 namespac ...
- 1、手写Unity容器--极致简陋版Unity容器
模拟Unity容器实例化AndroidPhone 思路: 1.注册类型:把类型完整名称作为key添加到数据字典中,类型添加到数据字典的value中 2.获取实例:根据完整类型名称也就是key取出val ...
- Unity容器实现AOP面向切面编程
为什么要有AOP 需求总是变化的,比如经常会对一些方法后期增加日志.异常处理.权限.缓存.事务的处理,遇到这种情况我们往往只能修改类. 为了应对变化,我们常常使用设计模式解决,但是也有其局限性:设计模 ...
随机推荐
- 漫谈边缘计算(三):5G的好拍档
边缘计算的热度迅速攀升,还有一个不得不提的因素,就是5G的发展. [5G推动云计算从集中化向分布式演进] 在第一篇文章(<漫谈边缘计算(一):边缘计算是大势所趋>)中我提到,传统的云计算技 ...
- deepin系统安装pip
Deepin系统安装pip Deepin系统通常自带了两个版本的python,一个python2,一个python3.可以在命令行输入这两个命令测试下是不是有两个版本,都是有两个版本都存在的情况下,安 ...
- ubuntu下仅仅获取网卡一的ip地址 && shell中字符串拼接
问题描述: ubuntu下仅仅获取网卡一的ip地址 问题背景: eth0,eth1,eth2……代表网卡一,网卡二,网卡三…… lo代表127.0.0.1,即localhost | 问题描述: 已知字 ...
- milvus安装及其使用教程
milvus 简介 milvus是干什么的?通俗的讲,milvus可以让你在海量向量库中快速检索到和目标向量最相似的若干个向量,这里相似度量标准可以是内积或者欧式距离等.借用官方的话说就是: Milv ...
- DOM中操作结点的属性_操作元素结点的样式
有俩种方式操作结点的属性. 首先我们需要先获取所要操作的结点元素: var uname=document.getElementById("uname"); var gan=unam ...
- 第五章 初始jQuery
jQuery与JavaScript: jQuery的用途: 访问和操作DOM元素: 控制页面样式: 对页面事件的处理: 方便地使用jQuery插件: 与Ajax技术的完美结合: jQuery的优势: ...
- [TimLinux] Linux shell获取进程pid
调用脚本时,获取进程PID: (/this/is/a/script/file.sh > /out/to/log.txt & echo $!) & 脚本内部,获取进程PID: ec ...
- JS-常考算法题解析
常考算法题解析 这一章节依托于上一章节的内容,毕竟了解了数据结构我们才能写出更好的算法. 对于大部分公司的面试来说,排序的内容已经足以应付了,由此为了更好的符合大众需求,排序的内容是最多的.当然如果你 ...
- python之with语句结合上下文管理器
所谓上下文管理器即在一个类中重写了__enter__方法和__exit__方法的类就可以成为上下文管理器类. 我们可以通过with语句结合上下文管理器简化一些操作. 使用with语句结合自定义上下文管 ...
- 【Java Web开发学习】Spring构造器和属性注入
测试类 public class Construct { private String address; private long phone; public Construct(String nam ...