Munq IOC 容器 – 创建线程本地存储生命周期管理器





5.00/5 (2投票s)
Munq IOC 容器 – 创建线程本地存储生命周期管理器
目录
“您有邮件”
在我开始撰写关于在 ASP.NET MVC2 应用程序中使用 Munq IocContainer V2 的第二篇文章时,我收到了以下电子邮件。
Miro Bartanus 在“[文章]:Munq IOC 容器在 ASP.NET 中的介绍”上发表了新评论:
嗨 Matthew,Munq 非常好,我只是想知道它是否支持 TLS 作为生命周期,我今天晚些时候会查看源代码,但你可能知道,或者实现起来应该不难……
我心想,“这真是个好主意。” 我已经计划了一篇关于创建生命周期管理器的文章,但还不确定类型。ThreadLocalStorageLifetimeManager
(线程本地存储生命周期管理器)相对容易实现,而且它是我现在需要解决的一些编程问题,因为我已经开始使用 .NET 4 中的并行编程库了。
什么是生命周期管理器?
LifetimeManager
(生命周期管理器)控制 IOC 容器在被要求解析类型时实例的重用。通过调用 UsesDefaultLifetimeManagerOf
方法,可以在 Container
上将解析时使用的 LifetimeManager
指定为默认值。以下示例将默认 LifetimeManager
设置为 RequestLifetimeManger
,从而使实例在每个 HTTP 请求的持续时间内重用。
// create the container. Only done once in Application_Start
IIocContainer iocContainer = new Container();
// create a lifetime manager to use as default
ILifetimeManager lifetimeManager = new LifetimeManagers.RequestLifetime();
// set the default lifetime manager
iocContainer.UsesDefaultLifetimeManagerOf(lifetimeManager);
或者,您可以在 RegisterXXX
调用返回的 IRegistration
实例上调用 WithLifetimeManager
方法。以下示例注册了两个服务,并导致容器始终返回相同的实例,从而有效地使其成为单例。
// create the container. Only done once in Application_Start
IIocContainer iocContainer = new Container();
// create a Container lifetime manager to use for 'singleton' services
// only one instance will be created and reused for each resolve request.
ILifetimeManager containerLifetimeManager = new LifetimeManagers.ContainerLifetime();
iocContainer.Register<IMembershipService>( ioc =>
new AccountMembershipService(Membership.Provider))
.WithLifetimeManager(containerLifetimeManager);
iocContainer.Register<IFormsAuthenticationService>
(ioc => new FormsAuthenticationService())
.WithLifetimeManager(containerLifetimeManager);
有哪些可用的生命周期管理器?
Munq 在 2.0 版本中包含许多 LifetimeManager
(生命周期管理器)。下面将对它们进行描述。我将在未来的小版本中添加 ThreadLocalStorageLifetimeManger
。
警告:如果您使用了 RegisterInstance
方法,那么无论使用哪种生命周期管理器,都会返回相同的实例。
- AlwaysNewLifetime
- 此生命周期管理器的行为是,通过执行工厂方法,在调用
Resolve
方法时始终返回一个新实例。这是默认行为。 - ContainerLifetime
- 此生命周期管理器的行为是,通过执行工厂方法,在调用
Resolve
方法时始终返回同一个实例。该实例被缓存到容器本身。 - SessionLifetime
- 此生命周期管理器的行为是,在调用
Resolve
方法时,始终尝试从Session
中检索实例。如果实例不存在于Session
中,则通过执行工厂方法创建新实例,并将其存储在Session
中。 - RequestLifetime
- 此生命周期管理器的行为是,在调用
Resolve
方法时,始终尝试从Request.Items
中检索实例。如果实例不存在于Request.Items
中,则通过执行工厂方法创建新实例,并将其存储在Request.Items
中。 - CachedLifetime
- 此生命周期管理器的行为是,在调用
Resolve
方法时,始终尝试从Cache
中检索实例。如果实例不存在于Cache
中,则通过执行工厂方法创建新实例,并将其存储在Cache
中。CachedLifetimeManager
可以应用CacheDependencies
和滑动或绝对超时。
检查现有的生命周期管理器
生命周期管理器实现 ILifetimeManager
接口及其两个方法。第一个方法 GetInstance
从生命周期管理器的缓存中获取请求的实例,或者在没有缓存实例时创建一个新实例。第二个方法 InvalidateInstanceCache
移除任何先前创建和缓存的实例,强制在下一个解析请求时创建新实例。
public interface ILifetimeManager
{
object GetInstance(IInstanceCreator creator);
void InvalidateInstanceCache(IRegistration registration);
}
下面是 RequestLifetimeManager
的代码。在 GetInstance
方法中,代码使用传入的 IInstanceCreator
(创建者)的 Key
属性尝试从 HttpContext.Request.Items
集合中检索实例。如果实例不存在,则调用 IInstanceCreator CreateInstance
方法,指定不将实例缓存到容器中。这会返回所需类型的新实例,并将其存储在 HttpContext.Request.Items
集合中以备将来重用。
InvalidateInstanceCache
方法只是移除任何存储的实例,强制在下一个解析请求时创建新实例。
其他代码用于支持测试,可以忽略。
using System.Web;
namespace Munq.LifetimeManagers
{
public class RequestLifetime : ILifetimeManager
{
private HttpContextBase testContext;
/// <summary>
/// Return the HttpContext if running in a web application, the test
/// context otherwise.
/// </summary>
private HttpContextBase Context
{
get
{
HttpContextBase context = (HttpContext.Current != null)
? new HttpContextWrapper(HttpContext.Current)
: testContext;
return context;
}
}
#region ILifetimeManage Members
/// <summary>
/// Gets the instance from the Request Items, if available,
/// otherwise creates a new
/// instance and stores in the Request Items.
/// </summary>
/// <param name="creator">The creator (registration) to create a new instance.
/// </param>
/// <returns>The instance.</returns>
public object GetInstance(IInstanceCreator creator)
{
object instance = Context.Items[creator.Key];
if (instance == null)
{
instance = creator.CreateInstance
(ContainerCaching.InstanceNotCachedInContainer);
Context.Items[creator.Key] = instance;
}
return instance;
}
/// <summary>
/// Invalidates the cached value.
/// </summary>
/// <param name="registration">The Registration
/// which is having its value invalidated</param>
public void InvalidateInstanceCache(IRegistration registration)
{
Context.Items.Remove(registration.Key);
}
#endregion
// only used for testing. Has no effect when in web application
public void SetContext(HttpContextBase context)
{
testContext = context;
}
}
}
创建新的生命周期管理器
现在您已经具备了创建新 LifetimeManager
所需的知识。基本步骤是:
- 创建一个派生自
ILifetimeManger
的类 - 实现这两个方法
我将在 Munq.IocContainer
的源代码中创建这些,因为我认为这是一个很好的补充,但您也可以创建一个新项目并引用 Munq.Interfaces
。然后,如果您需要自定义 LifetimeManager
,您将引用您的 DLL。
该类需要一个线程本地字典或哈希表来存储实例。否则,代码与 RequestLifetimeManager
的代码基本相同。完整代码如下:
>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Munq.LifetimeManagers
{
/// <summary>
/// A LifetimeManager that uses Thread Local Storage to cache instances.
/// </summary>
public class ThreadLocalStorageLifetime : ILifetimeManager
{
// The thread local storage. The ThreadStatic attribute makes this easy.
[ThreadStatic]
static Dictionary<string, object> localStorage;
/// <summary>
/// Gets an instance from the thread local storage,
/// or creates a new instance if not found.
/// </summary>
/// <param name="creator">The IInstanceCreate to use to get the Key
/// and create new if required.</param>
/// <returns>The instance.</returns>
public object GetInstance(IInstanceCreator creator)
{
object instance = null;
// if it is a new thread then the localStorage needs to be initialized;
if (localStorage == null)
localStorage = new Dictionary<string,object>();
if (!localStorage.TryGetValue(creator.Key, out instance))
{
instance = creator.CreateInstance
(ContainerCaching.InstanceNotCachedInContainer);
localStorage[creator.Key] = instance;
}
return instance;
}
/// <summary>
/// Removes the instance for the registration from the local storage cache.
/// </summary>
/// <param name="registration">The IRegistration returned
/// when the type was registered in the IOC container.</param>
public void InvalidateInstanceCache(IRegistration registration)
{
// nothing stored yet
if (localStorage == null)
return;
localStorage.Remove(registration.Key);
}
}
}
此代码的单元测试是:
>using Munq.LifetimeManagers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Munq;
using System.Web;
using MvcFakes;
using Munq.FluentTest;
using System.Threading.Tasks;
namespace Munq.Test
{
/// <summary>
///This is a test class for ThreadLocalStorageLifetimeTest and is intended
///to contain all ThreadLocalLifetimeTest Unit Tests
///</summary>
[TestClass()]
public class ThreadLocalStorageLifetimeTest
{
private TestContext testContextInstance;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use ClassInitialize to run code before running the first test in the class
//[ClassInitialize()]
//public static void MyClassInitialize(TestContext testContext)
//{
//}
IIocContainer iocContainer;
// Use TestInitialize to run code before running each test
[TestInitialize()]
public void MyTestInitialize()
{
iocContainer = new Munq.Container();
}
// Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup()
{
// remove the registrations, and cache values
var regs = iocContainer.GetRegistrations<IFoo>();
regs.ForEach(reg => iocContainer.Remove(reg));
iocContainer.Dispose();
}
#endregion
/// <summary>
/// Verify that Can Set the DefaultLifetimeManager To ThreadLocalStorageLifetime
///</summary>
[TestMethod()]
public void CanSetDefaultLifetimeManagerToThreadLocalStorageLifetime()
{
var lifetime = new ThreadLocalStorageLifetime();
iocContainer.UsesDefaultLifetimeManagerOf(lifetime);
Verify.That(iocContainer.LifeTimeManager).IsTheSameObjectAs(lifetime);
}
/// <summary>
/// verify Request Lifetime returns same instance for same request,
/// different for different request
/// </summary>
[TestMethod]
public void ThreadLocalStorageLifetimeManagerReturnsSameObjectForSameRequest()
{
var requestltm = new ThreadLocalStorageLifetime();
var container = new Container();
container.Register<IFoo>(c => new Foo1())
.WithLifetimeManager(requestltm);
IFoo result1 = container.Resolve<IFoo>();
IFoo result2 = container.Resolve<IFoo>();
IFoo result3=null;
IFoo result4=null;
// get values on a different thread
var t = Task.Factory.StartNew(() =>
{
result3 = container.Resolve<IFoo>();
result4 = container.Resolve<IFoo>();
});
t.Wait();
// check the results
Verify.That(result3).IsNotNull();
Verify.That(result4).IsNotNull()
.IsTheSameObjectAs(result3);
Verify.That(result2).IsNotNull();
Verify.That(result1).IsNotNull()
.IsTheSameObjectAs(result2)
.IsNotTheSameObjectAs(result3);
}
}
}
结论
添加自己的自定义生命周期管理器非常简单,它允许您支持您已经编写或使用的任何自定义数据存储、缓存、会话等。如何实现一个 AppFabricLifetimeManager
呢?
请关注后续文章。