参考地址:

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. Thinkphp5——数据库表名的大小写问题

    ThinkPHP5中数据库的表名如果是驼峰命名法,会被转换成小写加下划线,解决方法如下: 1.表名全部小写,因为数据库的表名区分大小写的. 2.使用Db::table("表名"), ...

  2. git log详细使用参数

    1. 可以看到fileName相关的commit记录 git log filename 2. 可以显示每次提交的diff git log -p filename 3. 只看某次提交中的某个文件变化,可 ...

  3. EditText 无法失焦与失焦后键盘不收缩解决方案

    背景 有一个需求,比方说下图: 点击了上图的Image 区域才可以编辑. 那么我首先想到的就是: android:focusable="false" 不让它获取到焦点不就ok吗? ...

  4. OA项目之Mybatis多表链接查询

    xml文件: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC & ...

  5. variable fonts - 更小更灵活的字体

    原文链接 variable fonts(下文中vf为缩写)是数字时代制作的字体技术,用更小的文件大小在web上提供更丰富的排版,但是一项新的技术往往伴随着新的挑战和复杂未知的情况.不过,我们要拥抱技术 ...

  6. Python基础第二课

    字符串(引号):四种表达方式 n1 = "我是" n1 = '我是' n1 = """我是""" n1 = '" ...

  7. Javascript 垃圾回收方法

    Javascript 垃圾回收方法 标记清除(mark and sweep) 这是 JavaScript 最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为& ...

  8. shell 100

    1.编写hello world脚本 #!/bin/bash# 编写hello world脚本 echo "Hello World!"2.通过位置变量创建 Linux 系统账户及密码 ...

  9. java 反射的意义

    具体的关于反射的介绍可以参考我的另外一篇博文<深入解析java反射>. 反射的意义是什么,其实就是为了代码简洁,提高代码的复用率,外部调用方便,源代码,反编译都能看到. 某些情况下解耦用反 ...

  10. Centos7 Openresty 开发环境

    首先要安装编译环境 yum group install "Development Tools" yum install pcre-devel openssl-devel gcc c ...