持久化从文件到数据库引发的架构变动
作者:网络转载 发布时间:[ 2015/7/23 13:42:43 ] 推荐标签:测试开发技术
虽然只是1行代码的重复,但是越看越不对劲。假如复杂一些的项目,有很多LINQ查询时,有多种持久化方式,还有针对单元测试的mock,这将会造成大量重复代码。
当一个变化会引发重复代码时,错的肯定不是变化本身,而是代码本身——代码的设计有问题。现在重复代码在眼前,现在不解决,更待何时。
要解决重复代码问题, 先要看一下相同(重复)代码之前的不同之处在哪里,然后在表面上看起来的不同之处找出共同点,用接口封装不同。这是代码设计中的求同存异法(注:实际没有这个方法,写这篇博文时臆造出来的)。
回到上面的代码,.Where(x => x.Id == Id).FirstOrDefault(); 之前的不同之处是 _refreshTokens 与 context.Set<RefreshToken>(),前者的类型是 List<RefreshToken>,后者的类型是 System.Data.Entity.DbSet ,这2个不同有什么共同之处呢?
在Visual Studio中按F12键向上求索,终于找到了1个共同之处,那是IQueryable——DbSet实现了IQueryable接口,List可以转换为IQueryable(通过AsQueryable方法)。既然找到了共同之处,那我们可以通过它消灭重复代码,将2个RefreshTokenRepository变成1个RefreshTokenRepository。
public class RefreshTokenRepository : IRefreshTokenRepository
{
private IQueryable<RefreshToken> _refreshTokens;
public RefreshTokenRepository()
{
}
public async Task<RefreshToken> FindById(string Id)
{
return _refreshTokens.Where(x => x.Id == Id).FirstOrDefault();
}
}
上面的代码实现了求同——从2个不同之处找到了共同之处,但如何存异呢?也是如何根据不同的持久化存储方式给上面代码中的_refreshTokens成员变量赋值呢?这又带来了_refreshTokens的求同存异问题。
这时你有没有想到,有一个东西是为求同存异而生,它是——接口(Interface)。
那我们引入一个接口来解决_refreshTokens的赋值问题,这个接口暂且叫做IUnitOfWork吧。IUnitOfWork的代码如下:
public interface IUnitOfWork : IDisposable
{
IQueryable<TEntity> Set<TEntity>() where TEntity : class;
}
于是RefreshTokenRepository可以通过IUnitOfWork接口给_refreshTokens赋值:
public class RefreshTokenRepository : IRefreshTokenRepository
{
private IQueryable<RefreshToken> _refreshTokens;
public RefreshTokenRepository(IUnitOfWork unitOfWork)
{
_refreshTokens = unitOfWork.Set<RefreshToken>();
}
public async Task<RefreshToken> FindById(string Id)
{
return await _refreshTokens.Where(x => x.Id == Id).FirstOrDefaultAsync();
}
}
接着我们针对文件存储的持久化方式,实现一个FileStorageUnitOfWork:
publicclassFileStorageUnitOfWork:IUnitOfWork
{
publicIQueryable<TEntity>Set<TEntity>()whereTEntity:class
{
returnReadFromFile<TEntity>().AsQueryable<TEntity>();
}
privateIList<TEntity>ReadFromFile<TEntity>()
{
IList<TEntity>entities=null;
varjsonFilePath=HostingEnvironment.MapPath(string.Format("~/App_Data/{0}.json",typeof(TEntity)));
if(File.Exists(jsonFilePath))
{
varjson=File.ReadAllText(jsonFilePath);
entities=JsonConvert.DeserializeObject<List<TEntity>>(json);
}
if(entities==null)entities=newList<TEntity>();
returnentities;
}
}
再接着针对数据库存储的持久化方式,基于Entity Framework实现一个EfUnitOfWork(EF的映射配置省略):
public class EfUnitOfWork : DbContext, IUnitOfWork
{
public new IQueryable<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
}
后,想用什么持久化方式,用IOC容器(比如Unity)注入对应的UnitOfWork。
要用文件存储,注入FileStorageUnitOfWork:
container.RegisterType<IUnitOfWork, FileStorageUnitOfWork>(new HttpContextLifetimeManager<IUnitOfWork>());
要用数据库存储,注入EfUnitOfWork:
container.RegisterType<IUnitOfWork, EfUnitOfWork>(new HttpContextLifetimeManager<IUnitOfWork>());
这样,我们可以轻松地将oauth refresh token的持久化方式从文件存储换到数据库存储,从数据库存储换到文件存储。或者哪天突发奇想换到NoSQL,也是手到擒来的事。
写了这么多废话,实际上只是为了一个接口的粉墨登场——IUnitOfWork。为了在持久化方式变化的情况下,保持Repository层的不变,我们引入了IUnitOfWork接口,让Repositroy依赖IUnitOfWork,将持久化方式封装在IUnitOfWork的实现中,从而解决了持久化方式变动带来的重复代码问题。再次实际体会了:小接口,大力量。
【附】
变化之后的架构如下:
Presentation层-WebAPI:CNBlogsRefreshTokenProvider
Application层-接口:IRefreshTokenService
Application层-实现:RefreshTokenService
Domain层-实体:RefreshToken
Repository层-接口:IRefreshTokenRepository
Repository层-实现:RefreshTokenRepository
UnitOfWork层-接口:IUnitOfWork
UnitOfWork层-实现:FileStorageUnitOfWork与EfUnitOfWork
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
在测试数据库性能时,需要注意哪些方面的内容?测试管理工具TC数据库报错的原因有哪些?怎么解决?数据库的三大范式以及五大约束编程常用的几种时间戳转换(java .net 数据库)优化mysql数据库的几个步骤数据库并行读取和写入之Python实现深入理解数据库(DB2)缓冲池(BufferPool)国内三大云数据库测试对比预警即预防:6大常见数据库安全漏洞数据库规划、设计与管理数据库-事务的概念SQL Server修改数据库物理文件存在位置使用PHP与SQL搭建可搜索的加密数据库用Python写一个NoSQL数据库详述 SQL 中的数据库操作详述 SQL 中的数据库操作Java面试准备:数据库MySQL性能优化

sales@spasvo.com