利用订阅模式实现缓存更新

来源:互联网 发布:空间域名绑定建站教程 编辑:程序博客网 时间:2024/06/07 17:52

1. 引言

很多Web项目,都需要和数据库打交道,典型的就是CRUD(读,写,更新,删除)操作。无论是哪种数据库,Asp.Net MVC 作为后端框架的项目,都有很多操作数据库的类库。最近在一个Web项目中就用到了EntityFramework来存取Sql Server。相信很多人都懂得,如何利用EntityFramework存取数据,所以这方面不做详细的介绍。 今天给大家介绍一种如何利用订阅模式来实现缓存更新。

实现过程主要参照NopCommerce,它是一个开源的电商平台,里面有不少精妙的设计,值得每一个.Net程序员一看。

 

2. 实现

先来看看未采用Cache的设计,定义一个Service,这个Service主要是利用EntityFramework存取数据。

   //  定义简单的数据模型    public class TargetSegment    {        public int Id { get; set; }        public string Segment { get; set; }    }

Service接口和默认实现

复制代码
    public interface ITargetSegmentService    {        TargetSegment GetById(object id);        void Insert(TargetSegment item);        void Update(TargetSegment item);        void Delete(TargetSegment item);        IList<TargetSegment> GetAll();    }
复制代码
复制代码
    public class TargetSegmentService : ITargetSegmentService    {        private IRepository<TargetSegment> _repository;        public TargetSegmentService(IRepository<TargetSegment> repository)        {            this._repository = repository;        }        public TargetSegment GetById(object id)        {            return this._repository.GetById(id);        }        public void Insert(TargetSegment item)        {            if (item == null)                throw new ArgumentNullException("item");            this._repository.Insert(item);        }        public void Update(TargetSegment item)        {            if (item == null)                throw new ArgumentNullException("item");            this._repository.Update(item);        }        public void Delete(TargetSegment item)        {            if (item == null)                throw new ArgumentNullException("item");            this._repository.Delete(item);        }        public IList<TargetSegment> GetAll()        {            return this._repository.Table.ToList();        }    }
复制代码

这个实现能够满足基本的需求,但是缺乏优化使得每次当Service调用GetAll 函数的时候,都会从数据库读取所有该类条目。如果没有Update,Create或者Delete每次GetAll返回的数据都是一样的。

因此可以在GetAll这里添加缓存,在Update,Create,Delete更新缓存。

首先我们利用.Net自带的System.Runtime.Caching.ObjectCache 类 定义缓存接口以及简单实现:

复制代码
    public interface ICache : IDisposable    {        T Get<T>(string key);        void Set(string key, object data, int cacheMinutes);        bool IsSet(string key);        void Remove(string key);        void RemoveByPattern(string pattern);        void Clear();    }    public static class CacheExtension    {        public static T GetOrAdd<T>(this ICache cache, string key, int cacheMinutes, Func<T> factory)        {            if (cache.IsSet(key))            {                return cache.Get<T>(key);            }            else            {                var data = factory();                cache.Set(key, data, cacheMinutes);                return data;            }        }        public static void RemoveByPattern(this ICache cache, string pattern, IEnumerable<string> keys)        {            Regex regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);            foreach (var target in keys.Where(k=>regex.IsMatch(k)))            {                cache.Remove(target);            }        }    }
复制代码
复制代码
    public partial class MemoryCache : ICache    {        protected System.Runtime.Caching.ObjectCache Cache        {            get { return System.Runtime.Caching.MemoryCache.Default; }        }        public MemoryCache()        {        }        public T Get<T>(string key)        {            return (T) this.Cache[key];        }        public void Set(string key, object data, int cacheMinutes)        {            if (data == null)                return;            this.Cache.Add(new CacheItem(key, data),                new CacheItemPolicy() {AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheMinutes)});        }        public bool IsSet(string key)        {            return this.Cache.Contains(key);        }        public void Remove(string key)        {            this.Cache.Remove(key);        }        public void RemoveByPattern(string pattern)        {            this.RemoveByPattern(pattern, Cache.Select(item => item.Key));        }        public void Clear()        {            foreach (var item  in Cache)            {                this.Remove(item.Key);            }        }        public void Dispose()        {            this.Clear();        }    }
复制代码

到此为止,已经可以实现缓存跟新了,只要在Insert,Update,Delete 函数里面添加 缓存更新即可。我们进一步利用订阅模式。

订阅模式 一般有三个对象,一个是Subject代表发送给所有订阅者的主题信息。一个是Consumer代表订阅者接受订阅的Subject, 另一个是Publisher代表发送Subject的实现。

这里我们定义几个Subject

复制代码
   // 代表数据删除的Subject   public class EntityDeleted<T> where T: class     {        public T Entity { get; private set; }        public EntityDeleted(T entity)        {            this.Entity = entity;        }    }
复制代码
复制代码
    public class EntityInserted<T> where T : class    {        public T Entity { get; private set; }        public EntityInserted(T entity)        {            this.Entity = entity;        }    }
复制代码
复制代码
    public class EntityUpdated<T> where T : class     {        public T Entity { get; private set; }        public EntityUpdated(T entity)        {            this.Entity = entity;        }    }
复制代码

定义Consumer接口和Cache相关的Consumer实现

    public interface IConsumer<T>    {        void HandleEvent(T eventMessage);    }
复制代码
    public class CacheEventConsumer :         IConsumer<EntityInserted<TargetSegment>>,        IConsumer<EntityUpdated<TargetSegment>>,         IConsumer<EntityDeleted<TargetSegment>>    {        private readonly ICache _cache;        public CacheEventConsumer()        {            this._cache = DependencyResolver.Current.GetService(typeof(ICache)) as ICache;        }        public const string AllTargetSegmentPattern = "TargetSegment.All";        public void HandleEvent(EntityInserted<TargetSegment> eventMessage)        {            this._cache.RemoveByPattern(AllTargetSegmentPattern);        }        public void HandleEvent(EntityUpdated<TargetSegment> eventMessage)        {            this._cache.RemoveByPattern(AllTargetSegmentPattern);        }        public void HandleEvent(EntityDeleted<TargetSegment> eventMessage)        {            this._cache.RemoveByPattern(AllTargetSegmentPattern);        }    }
复制代码

定义publish接口和实现

复制代码
    public interface IEventPublisher    {        void Publish<T>(T eventMessage);    }    public static class EventPublisherExtension    {        public static void EntityInserted<T>(this IEventPublisher eventPublisher, T entity) where T : class        {            eventPublisher.Publish(new EntityInserted<T>(entity));        }        public static void EntityUpdated<T>(this IEventPublisher eventPublisher, T entity) where T : class        {            eventPublisher.Publish(new EntityUpdated<T>(entity));        }        public static void EntityDeleted<T>(this IEventPublisher eventPublisher, T entity) where T : class        {            eventPublisher.Publish(new EntityDeleted<T>(entity));        }    }
复制代码
复制代码
    public class EventPublisher : IEventPublisher    {        public EventPublisher()        {        }        public void Publish<T>(T eventMessage)        {            var consumers = DependencyResolver.Current.GetServices<IConsumer<T>>();            foreach (var consumer in consumers)            {                this.PublishToConsumer(consumer, eventMessage);            }        }        protected virtual void PublishToConsumer<T>(IConsumer<T> consumer, T eventMessage)        {            try            {                consumer.HandleEvent(eventMessage);            }            catch (Exception exception)            {                   throw;            }        }    }
复制代码

最终的Service多了ICache 和 IEventPublish 两个对象:

复制代码
    public class TargetSegmentService : ITargetSegmentService    {        private IRepository<TargetSegment> _repository;        private ICache _cache;        private IEventPublisher _eventPublisher;        public TargetSegmentService(IRepository<TargetSegment> repository, ICache cache, IEventPublisher eventPublisher)        {            this._repository = repository;            this._cache = cache;            this._eventPublisher = eventPublisher;        }        public TargetSegment GetById(object id)        {            return this._repository.GetById(id);        }        public void Insert(TargetSegment item)        {            if (item == null)                throw new ArgumentNullException("item");            this._repository.Insert(item);            this._eventPublisher.EntityInserted(item);        }        public void Update(TargetSegment item)        {            if (item == null)                throw new ArgumentNullException("item");            this._repository.Update(item);            this._eventPublisher.EntityUpdated(item);        }        public void Delete(TargetSegment item)        {            if (item == null)                throw new ArgumentNullException("item");            this._repository.Delete(item);            this._eventPublisher.EntityDeleted(item);        }        public IList<TargetSegment> GetAll()        {            return this._cache.GetOrAdd(SnappsCacheEventConsumer.AllTargetSegmentPattern, 60, () =>            {                return this._repository.Table.ToList();            });        }    }
复制代码

最后通过Unity实现依赖注入

            Container.RegisterInstance<ICache>(new MemoryCache(), new ContainerControlledLifetimeManager());            Container.RegisterInstance<IEventPublisher>(new EventPublisher(), new ContainerControlledLifetimeManager());            Container.RegisterType(typeof(IConsumer<>), typeof(CacheEventConsumer), new ContainerControlledLifetimeManager());

 

3. 总结

这是订阅模式的一种运用,在NopCommerce里面有很多设计模式都运用的非常巧妙,对于EntityFramework的优化远不止这些,以后再给大家分享。

转载:http://www.cnblogs.com/VectorZhang/p/5605901.html

0 0
原创粉丝点击