参考地址:

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. pod install/update速度慢或失败的解决方案实践

    本文基于 https://www.cnblogs.com/dabaomo/p/9634727.html 声明 坚决拥护党的领导,本文章所用技术乃出于工作需要,敬请谅解. 正文 可以先过去快速浏览一遍再 ...

  2. Python正则表达式,看完这篇文章就够了...#华为云&#183;寻找黑马程序员#【华为云技术分享】

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

  3. UIContainerView纯代码实现及原理介绍

    UIContainerView纯代码实现及原理介绍 1.1-在StoryBoard中使用UIContainerView 1.2-纯代码使用UIContainerView 1.3-UIContainer ...

  4. IOS UISwitch 控件

    转自:http://my.oschina.net/wolx/blog/396680 一 UISwitch 简介 二 UISwitch 创建 三设置选中状态 四 添加监听 五 测试代码 5.1 代码 5 ...

  5. jQuery中的属性选择器

    先看代码,后面详细解释: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  6. JavaScript2 基础

    运算符 赋值运算符  用于给变量赋值. y=5;/z=2; 算术运算符  即算数符号,是基本算数运算.+ 加 / - 减/ * 乘/ / 除/ % 取余数/ ++ 自增(y++先赋值再自增/++y先自 ...

  7. 模电&数电知识整理(不定期更新)

    模电总复习之爱课堂题目概念整理 Chapter 1 1) 设室温情况下某二极管的反偏电压绝对值为1V,则当其反偏电压值减少100mV时,反向电流的变化是基本不发生变化. 2) 二极管发生击穿后,在击穿 ...

  8. 适用于带fifo接口的存储器和显示器测试模块封装 挑战cb

    cb说完美是没有极限的,对此我表示赞同,自从用了cb的板子,玩开cmos,fpga,sdram,vga等. 不断涌现的是,双端口sdram,四端口sdram,各式各样的封装,但是大同小异,但总是有些不 ...

  9. ajax来获取JWT的token

    AJAX方式获取token需要用

  10. nmap扫描、信息收集(网安全实训第一天)

    本期内容:网站信息收集.nmap扫描 1. 信息收集 2. nmap扫描1.信息收集 (1)确定目标 首先,我们确定攻击目标,在这里,我们随便找一个网站做测试,我以码云为例. (2)nslookup查 ...