使用DI框架进行横切关注点(如缓存、日志等)的应用程序设计






4.50/5 (6投票s)
能够通过依赖注入框架插件化应用程序的横切关注点。
引言
在当今世界,应用程序设计应考虑到插入横切关注点(如缓存、日志和异常处理等)的能力,以便能够扩展应用程序以满足未来的需求,而不会产生额外的维护成本。
背景
我最近在做一个客户项目,任务是查看应用程序的性能调整并提供扩展应用程序的建议。 作为过程的一部分,我们观察到的一件事是,横切关注点的特定实现(这里以缓存为例)与业务层紧密集成(使用HttpContext.Current.Cache
),从而阻止(如果不是阻止,使其更难,需要大量的代码更改、测试工作、延迟的时间表,最后但并非最不重要的是重新部署)扩展应用程序。
开发人员和设计人员使用了ASP.NET缓存(不是说ASP.NET缓存不好,而是它不是全局/分布式缓存),因此导致缓存对象在服务器场中的每个Web服务器上重复。 在这种情况下,将数据填充到缓存中的成本很高,因为需要运行的查询很复杂(实际上非常复杂),并且需要几秒钟才能执行。 这促使我写一篇关于如何创建可插拔设计的文章。
我要感谢Anil Sistla先生(Virtusa的架构师)提供的架构思路以及对解决方案实施的持续支持。
此解决方案提出了IoC(控制反转容器)如何帮助您解决这些依赖关系,并展示了您如何能够通过最小的配置更改来切换缓存数据提供程序层来轻松扩展。
在这个示例中,我使用的IoC容器是StructureMap
。 还有其他IoC框架,请根据您的需求进行评估和使用。 本主题不是关于特定的Ioc,而是关于如何使用IoC来提供可插拔的设计,它不仅限于解决横切关注点,而是可以将该概念应用于应用程序的任何部分。
此示例使用 Memcached 服务器,MemcachedClient
和ASP.NET缓存。
Using the Code
以下是缓存示例的代码结构

我们有一个名为CacheProviders.Interfaces.ICache
的接口。 出于演示目的,此接口提供了三个基本操作SetItem
,GetItem
和RemoveItem
。
我们有两个ICache
接口的实现,一个名为CacheDataProviders.Memcached
,另一个名为CacheDataProviders.AspNet
。
AspNetCacheProvider
接口的Icache
实现
public class AspNetCacheProvider : Providers.ICache {
public AspNetCacheProvider() {
}
bool Providers.ICache.SetItem(string key, object value) {
HttpRuntime.Cache.Insert(
key,
value,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default,
null);
return true;
}
object Providers.ICache.GetItem(string key) {
object _item = HttpRuntime.Cache.Get(key);
return _item;
}
bool Providers.ICache.RemoveItem(string key) {
HttpRuntime.Cache.Remove(key);
return true;
}
}
MemCacheProvider
接口的Icache
实现
public class MemCacheProvider : Providers.ICache {
private readonly MemcachedClient _cacheProviderClient;
// As of this writing, MemCacheClient configuration is done in the app.config
// of the host / consuming application. Please read the config file as it is
// self explanatory.
public MemCacheProvider() {
_cacheProviderClient = new MemcachedClient();
}
bool Providers.ICache.SetItem(string key, object value) {
bool _stored = _cacheProviderClient.Store(StoreMode.Set, key, value);
return _stored;
}
object Providers.ICache.GetItem(string key) {
object _value = _cacheProviderClient.Get(key);
return _value;
}
bool Providers.ICache.RemoveItem(string key) {
bool _removed = _cacheProviderClient.Remove(key);
return _removed;
}
}
CacheDataProviders.MemCached
使用eniym.memcached
客户端将数据存储到memcached中。
CacheDataProviders.AspNet
使用ASP.NET缓存来存储数据。
CachingSample
应用程序是消耗缓存层的程序,而不知道CacheDataProvider
的具体细节。
因此,对于开发目的,您可能希望使用AspNet
缓存,而对于暂存/生产部署,您可能希望使用分布式缓存,如Memcache
,或者您自己的缓存提供程序(当然,请确保已测试相应的CacheDataProvider
)。
我们使用DI框架根据环境(暂存或生产)创建必要的缓存提供程序,本文使用StructureMap
DI框架,因为它轻量级且易于演示(市场上还有其他用于.NET的DI框架,如Spring.Net,Microsoft的unity等。 每个都有其自身的优点和缺点,由开发人员/设计人员根据其需求选择合适的DI框架)。
以下是App.config文件中StructureMap
的配置部分,当前设置为MemCacheProvider
。 我们可以轻松地在配置文件中将插件从MemCached
翻转到ASP.NET,并且应用程序将开始采用新的缓存提供程序,在本例中为ASP.NET缓存。 如果当前缓存提供程序有任何限制,那么我们只需更改配置中的一个步骤就可以轻松地将应用程序扩展到不同的缓存提供程序。
<StructureMap>
<PluginFamily Type="CacheProviders.Interfaces.ICache"
Assembly="CacheProviders.Interfaces"
DefaultKey="DefaultCache">
<!--
TODO: Instead of MemCachedProvider, write an abstract base class
and provide the instance as default cache.
-->
<Plugin Assembly="CacheProviders.MemCached"
Type="CacheProviders.Memcached.MemCacheProvider"
Scope="Singleton"
ConcreteKey="DefaultCache" />
<!--<Plugin Assembly="CacheProviders.AspNet"
Type="CacheProviders.AspNet.AspNetCacheProvider"
Scope="Singleton"
ConcreteKey="DefaultCache" />-->
</PluginFamily>
</StructureMap>
免责声明
本文提供的示例代码仅用于演示目的,尚未为生产/部署做好准备。 读者有责任在将代码与其应用程序集成之前评估风险。