铺垫

通常在使用 EntityFramework 时,我们会封装出 IRepository 和 IUnitOfWork 接口,前者负责 CRUD 操作,后者负责数据提交 Commit。

   public interface IRepository<T>
where T : class
{
IQueryable<T> Query(); void Insert(T entity); void Update(T entity, params Expression<Func<T, object>>[] modifiedPropertyLambdas); void Delete(T entity);
}
   public interface IUnitOfWork
{
void Commit();
}

然后,通过使用 Unity IoC 容器来注册泛型接口与实现类型。

       Func<IUnityContainer> factory = () =>
{
return new UnityContainer()
.RegisterType(typeof(IRepository<>), typeof(Repository<>), new ContainerControlledLifetimeManager())
.RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager())
.RegisterType<DbContext, MyDBContext>(new ContainerControlledLifetimeManager())
.RegisterType<DbContextAdapter>(new ContainerControlledLifetimeManager())
.RegisterType<IObjectSetFactory, DbContextAdapter>(new ContainerControlledLifetimeManager())
.RegisterType<IObjectContext, DbContextAdapter>(new ContainerControlledLifetimeManager());
};

进而使与数据库相关的操作在 Bisuness Logic 中呈现的非常简单。

例如,通过一系列封装,我们可以达到如下效果:

         Customer customer = new Customer()
{
ID = "",
FirstName = "Dennis",
LastName = "Gao",
};
Repository.Customer.Insert(customer);
Repository.Commit();

查询操作也是一句话搞定:

 Customer customer = Repository.Customer.Query().SingleOrDefault(c => c.ID == "");

需求

假设有一个新的需求:要求在应用层面记录对每个 Table 的 CRUD 的次数。

这时,有几种办法:

  1. 应用程序的 Business Logic 中自己记录,比如调用 Update() 操作后记录。
  2. 使用 AOP 模式,在调用 CRUD 方法时注入计数器。
  3. 修改 Repository<T> 实现,在每个方法中嵌入计数器。
  4. 继承 Repository<T> 类,在衍生类中嵌入计数器。
  5. 使用装饰器模式封装 Repository<T>,在新的 RepositoryDecorator<T> 类中嵌入计数器。

考虑到前三种方法均需要改动已有代码,主要是涉及的修改太多,所有没有尝试采用。

方法 4 则要求修改 Repository<T> 的实现,为 CRUD 方法添加 virtual 关键字以便扩展。

方法 5 不需要修改 Repository<T> 的实现,对已有代码的改动不大。

综上所述,我们选择了方法 5。

Repository 装饰器基类实现

为便于以后的扩展,创建一个装饰器的抽象类。

   public abstract class RepositoryDecorator<T> : IRepository<T>
where T : class
{
private readonly IRepository<T> _surrogate; protected RepositoryDecorator(IRepository<T> surrogate)
{
_surrogate = surrogate;
} protected IRepository<T> Surrogate
{
get { return _surrogate; }
} #region IRepository<T> Members public virtual IQueryable<T> Query()
{
return _surrogate.Query();
} public virtual void Insert(T entity)
{
_surrogate.Insert(entity);
} public virtual void Update(T entity, params Expression<Func<T, object>>[] modifiedPropertyLambdas)
{
_surrogate.Update(entity, modifiedPropertyLambdas);
} public virtual void Delete(T entity)
{
_surrogate.Delete(entity);
} #endregion
}

可以看到,RepositoryDecorator<T> 类型仍然实现了 IRepository<T> 接口,对外使用没有任何变化。

实现需求

我们定义一个 CountableRepository<T> 类用于封装 CRUD 计数功能,其继承自 RepositoryDecorator<T> 抽象类。

   public class CountableRepository<T> : RepositoryDecorator<T>
where T : class
{
public CountableRepository(IRepository<T> surrogate)
: base(surrogate)
{
} public override IQueryable<T> Query()
{
PerformanceCounter.CountQuery<T>();
return base.Query();
} public override void Insert(T entity)
{
PerformanceCounter.CountInsert<T>();
base.Insert(entity);
} public override void Update(T entity, params Expression<Func<T, object>>[] modifiedPropertyLambdas)
{
PerformanceCounter.CountUpdate<T>();
base.Update(entity, modifiedPropertyLambdas);
} public override void Delete(T entity)
{
PerformanceCounter.CountDelete<T>();
base.Delete(entity);
}
}

我们在 override 方法中,添加了 CRUD 的计数功能。这里的代码简写为:

 PerformanceCounter.CountQuery<T>();

对原有代码的修改则是需要注册新的 CountableRepository<T> 类型。

       Func<IUnityContainer> factory = () =>
{
return new UnityContainer()
.ReplaceBehaviorExtensionsWithSafeExtension()
.RegisterType(typeof(IRepository<>), typeof(Repository<>), new ContainerControlledLifetimeManager())
.RegisterType(typeof(CountableRepository<>), new ContainerControlledLifetimeManager())
.RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager())
.RegisterType<DbContext, MyDBContext>(new ContainerControlledLifetimeManager())
.RegisterType<DbContextAdapter>(new ContainerControlledLifetimeManager())
.RegisterType<IObjectSetFactory, DbContextAdapter>(new ContainerControlledLifetimeManager())
.RegisterType<IObjectContext, DbContextAdapter>(new ContainerControlledLifetimeManager());
};

扩展应用

既然有了抽象基类 RepositoryDecorator<T> ,我们可以从其设计衍生多个特定场景的 Repository 。

比如,当我们需要为某个 Table 的 Entity 添加缓存功能时,我们可以定制一个 CachableRepository<T> 来完成这一个扩展。

EntityFramework中使用Repository装饰器的更多相关文章

  1. Python中利用函数装饰器实现备忘功能

    Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下   " ...

  2. Angular 个人深究(一)【Angular中的Typescript 装饰器】

    Angular 个人深究[Angular中的Typescript 装饰器] 最近进入一个新的前端项目,为了能够更好地了解Angular框架,想到要研究底层代码. 注:本人前端小白一枚,文章旨在记录自己 ...

  3. python 中多个装饰器的执行顺序

    python 中多个装饰器的执行顺序: def wrapper1(f1): print('in wrapper1') def inner1(*args,**kwargs): print('in inn ...

  4. 第7.17节 Python类中的静态方法装饰器staticmethod 定义的静态方法深入剖析

    第7.17节  Python类中的静态方法装饰器staticmethod 定义的静态方法深入剖析 静态方法也是通过类定义的一种方法,一般将不需要访问类属性但是类需要具有的一些能力可以静态方法提供. 一 ...

  5. 第7.26节 Python中的@property装饰器定义属性访问方法getter、setter、deleter 详解

    第7.26节 Python中的@property装饰器定义属性访问方法getter.setter.deleter 详解 一.    引言 Python中的装饰器在前面接触过,老猿还没有深入展开介绍装饰 ...

  6. Python中的各种装饰器详解

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...

  7. Python中闭包、装饰器的概念

    1.闭包(Closure)的概念: 内部函数中对enclosing作用域的变量进行引用 1 passline = 60 2 def func(val): 3 print('%x' % id(val)) ...

  8. 谈谈Python中的decorator装饰器,如何更优雅的重用代码

    众所周知,Python本身有很多优雅的语法,让你能用一行代码写出其他语言很多行代码才能做的事情,比如: 最常用的迭代(eg: for i in range(1,10)), 列表生成式(eg: [ x* ...

  9. Python 中写一个装饰器实现限制频率访问

    1.思路: 首先要在装饰器中确定访问的方法名, 第一次可以访问成功,之后要在规定的时间(变量)之后才可以访问. 初始应该有一个变量为0;访问成功之后把当前的时间赋值给这个变零. 这样再次访问时把当前的 ...

随机推荐

  1. 学习打造自己的DEBUG_NEW

    学习范例http://www.cppblog.com/Robertxiao/archive/2012/11/05/194547.html 在使用MFC库开发程序时,我非常喜欢MFC框架中的内存泄漏诊断 ...

  2. MySQL大数据分页的优化思路和索引延迟关联

    之前上次在部门的分享会上,听了关于MySQL大数据的分页,即怎样使用limit offset,N来进行大数据的分页,现在做一个记录: 首先我们知道,limit offset,N的时候,MySQL的查询 ...

  3. 【转载】H264--1--编码原理以及I帧B帧P帧

    ---------------------- 前言 ----------------------- H264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称,在编码方面,我理解的他的理 ...

  4. [转]LIBSVM-3.18在python环境下的使用

    http://blog.csdn.net/lj695242104/article/details/39500039 1)安装Python,直接去官方网站 2)运行“cmd”,直接输入python,查看 ...

  5. python常用函数

    dict排序: a={'A':4,'B':3,'C':2,'D':1} sorted(a.iteritems(),key=operator.itemgetter(1),reverse=False) # ...

  6. 第一次正式小用Redis存储

    由于要做一个同一个页面上多种图表数据的下载,考虑到Azure上面的session很不稳定(可用Redis provider存储session,较稳定),故决定改为Azure支持的Redis,顺便也学习 ...

  7. 笔记本Linux推荐

    1.CUB LINUX Cub Linux 是一个最好的选择.他的前身来自著名的 Chromium OS , Cub Linux 能够运行在各种各样的笔记本上面.即便是早年的老机,亦或是现在的新机.从 ...

  8. ZT 趋势移动安全apk

    趋势移动安全 应用截图   应用简介 趋势移动安全( Mobile Security) 是一款专业的Android移动安全软件.利用趋势科技世界领先的云安全技术,保护用户避免被移动恶意程序骚扰,避免个 ...

  9. linux中的开机和关机命令

    与关机.重新启动相关的命令 * 将数据同步写入硬盘中的命令  sync * 惯用的关机命令  shutdown * 重新启动.关机  reboot halt poweroff sync 强制将内存中的 ...

  10. python数据处理相关的一些知识点(学习点)

    自己总结了一下就是存储,消息处理(异步,阻塞,队列,消息中间件) 参考岗位需求 数据爬虫工程师的岗位职责:1.分布式网络爬虫研发:不断完善现有抓取系统,通过对抓取.解析.调度.存储等模块的拆分与优化, ...