65.9K
CodeProject 正在变化。 阅读更多。
Home

Munq IOC 容器 for ASP.NET 简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (6投票s)

2009 年 10 月 26 日

CPOL

8分钟阅读

viewsIcon

104559

这是关于使用 Munq DI IOC 容器和 IOC 容器的系列文章的第一篇。

引言

这是关于使用 Munq DI IOC 容器和 IOC 容器的系列文章的第一篇。

控制反转(IOC)是一种模式,它将接口的使用与该接口的具体实现分离开来。通过消除这种耦合,

  • 代码可以通过使用接口的特殊测试实现来进行测试。
  • 在多层部署的每一层需要部署的 DLL 会更少。
  • 由于项目依赖项减少,项目编译速度可能会加快。
  • 无需更改主应用程序代码,即可配置使用接口的备用实现。这意味着只需更改一个配置文件,就可以从 MSSQL 切换到 Oracle。

IOC 容器是一种模式,它允许应用程序在不知道将使用的具体类的情况下获得接口的实例。

Munq DI IOC 容器是我所知的最小、最快的 IOC 容器。它比任何主流容器(Unity、Windsor Castle、Ninject、StructureMap 等)都快。请参见下方的图表。此外,它还专为 Web 开发人员设计,其生命周期管理包括 Request、Session 和 Cached 生命周期管理器。

本文将为您提供 Munq IOC 容器的详细了解,并附带

  • 一张引人注目的性能图
  • API 文档

其他文章将演示 Munq DI IOC 容器在实际应用中的用法,包括 Munq 与 ASP.NET MVC 的集成、Munq 代码导览以及使用 IOC 容器时需要注意的事项。

背景

今年早些时候,我观看了 Daniel Cazzulino(Kzu)创作的非常有趣的 Funq 屏幕录制系列。这个分为九部分的系列详细介绍了开发一个名为 Funq 的小型、快速 IOC 容器的 TDD 过程,该容器后来发展成为 ContainerModel,用于 Patterns & Practices: Mobile Application Blocks

下载代码后,我发现了一些改进,这些改进既简化了代码,又显著提高了性能。经过几次讨论,Kzu 让我成为了贡献者,我的一些想法和代码被整合到了代码库中。

不幸的是,Kzu 的 Funq 目标是移动开发领域,而我更专注于 ASP.NET Webform 和 MVC 应用程序开发。我不需要 Funq 的一些功能,例如容器层次结构和初始化器,但我需要生命周期管理功能,包括面向 Web 的样式,例如 Request、Session 和 Cached 生命周期管理。因此,Munq 应运而生。

此次开发的目标是:

  1. 速度。高流量网站需要最大限度地减少每个页面请求执行的工作量。即使是 1/10 秒的差异,也会影响用户对网站的感知。
  2. 简洁。Munq 只做一件事,但做得很好。它通过执行容器中先前注册的工厂来解析类型实例的请求。
  3. 提供 Web 生命周期管理。Windows 应用程序可以通过每次请求都创建一个新实例或重复使用同一实例的容器来运行。在 Web 应用程序的世界中,对象可以只存在于当前请求(Request)、用户会话(Session)中,或者被缓存(Cached)以减少数据库负载和访问延迟。

Munq 的最新版本可在 CodePlex 上找到,地址为 http://munq.codeplex.com

性能

在开发 Funq 的过程中,Kzu 和我创建了一个小型性能测量应用程序,以比较从各种 IOC 容器创建新实例的相对开销。

此测试使用 IOC 容器构建一个实现了 IWebService 接口的“WebApp”,该接口具有以下图所示的依赖项。

Sample App Dependencies

所有容器都配置为返回所有接口的新实例,除了 ILogger,它是一个共享实例。每个用例运行 10000 次迭代后,结果如下。

容器 每个迭代的 Ticks 数
5.1746
Munq 68.3385
Funq 76.2779
Unity 613.0442
Autofac 877.035
StructureMap 280.9433
Ninject 4122.1138
Ninject2 5059.9001
温莎 4206.1035

如下面的图表所示,数值越小越好。

IOC Container Performance

注册工厂的代码如下:

ILifetimeManager lifetime = new ContainerLifetime();

	Container container = new Container();

	container.Register<IWebservice>(
		c => new WebService(
			c.Resolve<IAuthenticator>(),
			c.Resolve<IStockquote>()));

	container.Register<IAuthenticator>(
		c => new Authenticator(
			c.Resolve<ILogger>(),
			c.Resolve<IErrorhandler>(),
			c.Resolve<IDatabase>()));

	container.Register<IStockquote>(
		c => new StockQuote(
			c.Resolve<ILogger>(),
			c.Resolve<IErrorhandler>(),
			c.Resolve<IDatabase>()));

	container.Register<IDatabase>(
		c => new Database(
			c.Resolve<ILogger>(),
			c.Resolve<IErorhandler>()));

	container.Register<IErrorhandler>(
		c => new ErrorHandler(c.Resolve<ILogger>()));

	container.RegisterInstance<ILlogger>(new Logger())
		.WithLifetimeManager(lifetime);

解析实例

	var webApp = container.Resolve<IWebservice>();
	webApp.Execute();

Using the Code

使用容器相对简单。基本步骤如下:

  1. 创建容器。
  2. 注册接口和/或类的工厂委托。
  3. 通过调用容器的 Resolve 方法来解析实例。

创建 IOC 容器

容器通常在应用程序首次启动时创建。在 Web 应用程序中,容器通常会在 Application_Start 中创建,并存储在派生的 Application 类的字段或 static 变量中。

Container container = new Container();

注册类型工厂

注册类型工厂会将要解析的类型与返回该类型实例的函数关联起来。当容器被要求返回类型的实例时,就会调用此方法。

Munq 有四种注册类型工厂函数的方式。这些函数可以是任何具有正确签名的内容,不限于以下形式的委托:

c=> new MyType()

但也可以是:

c => CreateAndInitialzeMyType(c.Resolve<IOne>(), c.Resolve<ITwo>)

第一种是使用类型安全的泛型 Register 方法。有命名和非命名注册的版本。两者都将一个委托作为参数,该委托接受一个 Container 作为其唯一参数,并返回该类型的实例。

public IRegistration Register<TType>(Func<Container, TType> func)
public IRegistration Register<TType>(string name, Func<Container, TType>func)

第二种是一组方法,允许通过传递要解析的类型以及一个接受 Container 作为其唯一参数并返回 Object 的委托来注册工厂方法。有命名和非命名注册的版本。如果注册信息是从外部存储(如数据库、XML 文件或 web.config 文件)读取的,通常会使用这些方法。

public IRegistration Register(Type type, Func<Container, object> func)
public IRegistration Register(string name, Type type, Func<Container, object> func)

第三种方法是使用类型安全的泛型 RegisterInstance 方法。有命名和非命名注册的版本。两者都将该类型的实例作为参数。

public IRegistration RegisterInstance<TType>(TType instance)
public IRegistration RegisterInstance<TType>(string name, TType instance)

第四种是一组方法,允许通过传递要解析的类型以及在解析类型时要返回的对象来注册工厂方法。有命名和非命名注册的版本。如果注册信息是从外部存储(如数据库、XML 文件或 web.config 文件)读取的,通常会使用这些方法。

public IRegistration RegisterInstance(Type type, object instance)
public IRegistration RegisterInstance(string name, Type type, object instance)

从容器中获取实例

一旦接口和函数在容器中注册,应用程序就可以通过请求容器将接口解析为已注册的具体实现来检索实例。Munq 有两种形式的 Resolve 方法:一种是使用泛型的类型安全版本,另一种是接受 Type 作为参数并返回 Object 的版本。这两个版本都有命名和非命名重载。

public TType Resolve<TType>()
public TType Resolve<TType>(string name)

public object Resolve(Type type)
public object Resolve(string name, Type type)

延迟解析

在某些情况下,您可能不希望立即创建实例。这可能是因为实例使用了稀缺资源,或者由于某些逻辑可能不需要实例化。对于这些情况,您可以使用 LazyResolve 方法,它返回一个函数,该函数在执行时进行解析并返回实例。

Resolve 方法一样,LazyResolve 有两种形式:一种是使用泛型的类型安全版本,另一种是接受 Type 作为参数的版本。这两个版本都有命名和非命名重载。

Func LazyResolve<ttype>() 
Func<ttype> LazyResolve<ttype<(string name)
 
Func<Object> LazyResolve(Type type) 
Func<Object> LazyResolve(string name, Type type)

什么是 IRegistration Thing?

您可能已经注意到,Registration 方法都返回实现 IRegistration 接口的对象。此接口允许用户检索注册的内部生成 ID,并指定实例在解析时使用的 LifetimeManager

例如,如果需要一个 IShoppingCart 对象存储在用户的会话中,您将告诉注册使用 SessionLifetime 管理器。

    ILifetimeManager lifetime = new SessionLifetime();
    container.Register<IShoppingCart>(c => new MyShoppingCart())
             .WithLifetimeManager(lifetime);

请注意,这已以流畅的接口方式实现,以允许更改方法调用。虽然此接口目前只有一个方法,但任何将来的方法都将遵循此模式。

 public interface IRegistration
{
    string Id { get; }
    IRegistration WithLifetimeManager(ILifetimeManager manager);
}

指定默认生命周期管理器

Container 首次创建时,默认行为是对每次 Resolve 方法调用返回一个新实例。如上所示,这可以在每个注册的基础上修改。此外,您可以指定任何注册要使用的生命周期管理器。例如,要始终为每次调用 Resolve 返回相同的实例,请使用 ContainerLifetime 管理器,如下所示:

Container container = new Container();
ILifetimeManager lifetime = new ContainerLifetime();

container.UsesDefaultLifetimeManagerOf(lifetime);

方法定义是:

public Container UsesDefaultLifetimeManagerOf(ILifetimeManager lifetimeManager)

可用的生命周期管理器

生命周期管理器允许您修改容器解析实例的行为以及实例的生命周期。Munq 拥有一组专为 Web 应用程序设计的生命周期管理器。下面将进行介绍。

警告:如果您使用了 RegisterInstance 方法,那么无论使用哪种生命周期管理器,都将返回相同的实例。

AlwaysNewLifetime

此生命周期管理器的行为是,在调用 Resolve 方法时,通过执行工厂方法始终返回一个新实例。这是默认行为。

ContainerLifetime

此生命周期管理器的行为是,在调用 Resolve 方法时,通过执行工厂方法始终返回同一个实例。

SessionLifetime

此生命周期管理器的行为是,在调用 Resolve 方法时,始终尝试从 Session 检索实例。如果实例在 Session 中不存在,则通过执行工厂方法创建新实例,并将其存储在 Session 中。

RequestLifetime

此生命周期管理器的行为是,在调用 Resolve 方法时,始终尝试从 Request.Items 检索实例。如果实例在 Request.Items 中不存在,则通过执行工厂方法创建新实例,并将其存储在 Request.Items 中。

CachedLifetime

此生命周期管理器的行为是,在调用 Resolve 方法时,始终尝试从 Cache 检索实例。如果实例在 Cache 中不存在,则通过执行工厂方法创建新实例,并将其存储在 Cache 中。

结论

我简要介绍了 Munq DI IOC 及其 API。之后,我将发布更多文章,介绍 Munq 的用法并剖析 Munq 的代码。

Munq 的完整源代码可在 CodePlex 上找到,地址为 http://munq.codeplex.com

del.icio.us 标签: , , , , ,

© . All rights reserved.