参考地址:

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. 一条数据的HBase之旅,简明HBase入门教程1:开篇

    [摘要] 这是HBase入门系列的第1篇文章,主要介绍HBase当前的项目活跃度以及搜索引擎热度信息,以及一些概况信息,内容基于HBase 2.0 beta2版本.本系列文章既适用于HBase新手,也 ...

  2. luogu P2812 校园网络【[USACO]Network of Schools加强版】|Tarjan

    题目背景 浙江省的几所OI强校的神犇发明了一种人工智能,可以AC任何题目,所以他们决定建立一个网络来共享这个软件.但是由于他们脑力劳动过多导致全身无力身体被♂掏♂空,他们来找你帮助他们. 题目描述 共 ...

  3. 《手把手教你》系列进阶篇之3-python+ selenium自动化测试 - python几种骚操作你都知道吗?(详细教程)

    1. 简介 这篇文章主要是给小伙伴或者童鞋们介绍和分享 python几种骚操:读取配置文件.获取根目录的相对路径.获取系统时间和格式化时间显示.字符串切割等等操作.为后边的自动化框架打下一个结实的基础 ...

  4. vue 各种打包坑

    1,报错 Refused to load the image 'http://localhost:8080/favicon.ico' because it violates the following ...

  5. Java修炼——四种方式解析XML_DOM

    四种方式解析XML:DOM      JDOM   DOM4J    SAX 先写一个XML栗子: <?xml version="1.0" encoding="UT ...

  6. MVC参数传递

    MVC参数传递 请求参数自动类型转换 JSP页面 form class="loginForm" action="/user/getUser" method=&q ...

  7. CoderForces 163E e-Government(AC自动机+树状数组维护fail树的dfs序)

    E. e-Government time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...

  8. 笔记||Python3之模块与包

    模块的概念:一个.py文件就称之为一个模块. 包的概念:把许多个模块按照功能放到不同的目录中来组织模块,这些组织存放模块文件的目录,我们称之为包. 模块与包的优势:1- 方便别人调用 2 - 避免同名 ...

  9. 如何成为一名成功的iOS程序员,挑战年薪50万?

    编程是一个仅靠兴趣仍不足以抵达成功彼岸的领域.你必须充满激情,并且持之以恒地不断汲取更多有关编程的知识.只是对编程感兴趣还不足以功成名就——众所周知,我们工作起来像疯子. 编程是一个没有极限的职业,所 ...

  10. Python3 并发编程1

    目录 操作系统发展 穿孔卡片 批处理 多道技术(单核) 并发与并行 进程 程序与进程 进程调度 进程的三个状态 同步和异步 阻塞与非阻塞 僵尸进程与孤儿进程 守护进程 Python中的进程操作 Pro ...