Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)
在本系列的第一篇随笔《Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)》中介绍了Entity Framework 实体框架的一些基础知识,以及构建了一个简单的基于泛型的仓储模式的框架;在随笔《Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)》则持续优化这个仓储模式的实体框架,主要介绍业务逻辑层的构建,以及利用Unity和反射进行动态的对象注册。本篇主要介绍基类接口的统一和异步操作的实现等方面,逐步把我框架接口命名的方式进行统一,并增加所有必要用到的增删改查、分页、lambda表达式条件处理,以及异步操作等特性,这样能够尽可能的符合基类这个特殊类的定义,实现功能接口的最大化重用和统一。
1、基类接口的统一命名和扩展
在我以前的基于Enterprise Library的框架里面,定义了一个超级的数据访问基类,是特定数据访问类基类的基类,AbstractBaseDAL的数据访问层基类定义了很多通用的接口,具有非常强大的操作功能,如下所示。

这里面的很多接口命名我都经过了一些推敲,或者我基于我或者我客户群体的使用习惯和理解考虑,也是想沿承这些命名规则,扩充我这个基于泛型的仓储模式的实体框架基类接口。

下面是各类不同接口的定义内容。
1)增加操作
/// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="t">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
bool Insert(T t);
2)删除操作
/// <summary>
/// 根据指定对象的ID,从数据库中删除指定对象
/// </summary>
/// <param name="id">对象的ID</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
bool Delete(object id); /// <summary>
/// 从数据库中删除指定对象
/// </summary>
/// <param name="id">指定对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
bool Delete(T t);
3)修改操作
/// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="t">指定的对象</param>
/// <param name="key">主键的值</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
bool Update(T t, object key);
4)主键查询以及条件查询操作
/// <summary>
/// 查询数据库,返回指定ID的对象
/// </summary>
/// <param name="id">ID主键的值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
T FindByID(object id); /// <summary>
/// 根据条件查询数据库,如果存在返回第一个对象
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>存在则返回指定的第一个对象,否则返回默认值</returns>
T FindSingle(Expression<Func<T, bool>> match);
5)集合查询(分返回IQueryable和ICollection<T>两种方式)
/// <summary>
/// 返回可查询的记录源
/// </summary>
/// <returns></returns>
IQueryable<T> GetQueryable(); /// <summary>
/// 根据条件表达式返回可查询的记录源
/// </summary>
/// <param name="match">查询条件</param>
/// <param name="orderByProperty">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns></returns>
IQueryable<T> GetQueryable(Expression<Func<T, bool>> match, string sortPropertyName, bool isDescending = true); /// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns></returns>
ICollection<T> Find(Expression<Func<T, bool>> match); /// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="match">条件表达式</param>
/// <param name="orderByProperty">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns></returns>
ICollection<T> Find<TKey>(Expression<Func<T, bool>> match, Expression<Func<T, TKey>> orderByProperty, bool isDescending = true);
6)分页查询操作
/// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="match">条件表达式</param>
/// <param name="info">分页实体</param>
/// <param name="orderByProperty">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns>指定对象的集合</returns>
ICollection<T> FindWithPager(Expression<Func<T, bool>> match, PagerInfo info); /// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="match">条件表达式</param>
/// <param name="info">分页实体</param>
/// <param name="orderByProperty">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns>指定对象的集合</returns>
ICollection<T> FindWithPager<TKey>(Expression<Func<T, bool>> match, PagerInfo info, Expression<Func<T, TKey>> orderByProperty, bool isDescending = true);
这样我们在BaseDAL里面,把这些接口全部实现了,那么所有继承这个基类对象的数据访问对象,就具有这些标准的接口了,也给我们开发实现了整体性的统一。
首先我们来看看这个基类BaseDAL的初始化定义代码。
/// <summary>
/// 数据访问层基类
/// </summary>
/// <typeparam name="T">实体对象类型</typeparam>
public abstract class BaseDAL<T> : IBaseDAL<T> where T : class
{
#region 变量及构造函数 /// <summary>
/// DbContext对象
/// </summary>
protected DbContext baseContext; /// <summary>
/// 指定类型的实体对象集合
/// </summary>
protected DbSet<T> objectSet; /// <summary>
/// 是否为降序
/// </summary>
public bool IsDescending { get; set; } /// <summary>
/// 排序属性
/// </summary>
public string SortPropertyName { get; set; } /// <summary>
/// 参数化构造函数
/// </summary>
/// <param name="context">DbContext对象</param>
public BaseDAL(DbContext context)
{
this.baseContext = context;
this.objectSet = this.baseContext.Set<T>(); this.IsDescending = true;
this.SortPropertyName = "ID";
} #endregion
有了这些DbContext对象以及DbSet<T>对象,具体的接口实现就很容易了,下面我抽几个代表性的函数来介绍实现。
1)增加对象
/// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="t">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool Insert(T t)
{
ArgumentValidation.CheckForNullReference(t, "传入的对象t为空"); objectSet.Add(t);
return baseContext.SaveChanges() > ;
}
2)删除对象
/// <summary>
/// 根据指定对象的ID,从数据库中删除指定对象
/// </summary>
/// <param name="id">对象的ID</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool Delete(object id)
{
T obj = objectSet.Find(id);
objectSet.Remove(obj);
return baseContext.SaveChanges() > ;
}
3)修改对象
/// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="t">指定的对象</param>
/// <param name="key">主键的值</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool Update(T t, object key)
{
ArgumentValidation.CheckForNullReference(t, "传入的对象t为空"); bool result = false;
T existing = objectSet.Find(key);
if (existing != null)
{
baseContext.Entry(existing).CurrentValues.SetValues(t);
result = baseContext.SaveChanges() > ;
}
return result;
}
4)根据条件查询
/// <summary>
/// 根据条件查询数据库,如果存在返回第一个对象
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>存在则返回指定的第一个对象,否则返回默认值</returns>
public virtual T FindSingle(Expression<Func<T, bool>> match)
{
return objectSet.FirstOrDefault(match);
} /// <summary>
/// 根据条件表达式返回可查询的记录源
/// </summary>
/// <param name="match">查询条件</param>
/// <param name="orderByProperty">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns></returns>
public virtual IQueryable<T> GetQueryable(Expression<Func<T, bool>> match, string sortPropertyName, bool isDescending = true)
{
IQueryable<T> query = this.objectSet;
if (match != null)
{
query = query.Where(match);
}
return query.OrderBy(sortPropertyName, isDescending);
}
5)分页查询
/// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="match">条件表达式</param>
/// <param name="info">分页实体</param>
/// <param name="orderByProperty">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns>指定对象的集合</returns>
public virtual ICollection<T> FindWithPager(Expression<Func<T, bool>> match, PagerInfo info)
{
int pageindex = (info.CurrenetPageIndex < ) ? : info.CurrenetPageIndex;
int pageSize = (info.PageSize <= ) ? : info.PageSize; int excludedRows = (pageindex - ) * pageSize; IQueryable<T> query = GetQueryable().Where(match);
info.RecordCount = query.Count(); return query.Skip(excludedRows).Take(pageSize).ToList();
}
更多的代码就不一一贴出,反正我们全部实现自己所需的各种操作就可以了,这里要提的是,我们尽可能利用Lambda表达式进行条件处理,包括查询、删除等条件处理。
对上面的这些常规接口,我们调用代码处理的例子如下所示。
private void btnProvince_Click(object sender, EventArgs e)
{
DateTime dt = DateTime.Now; var list = BLLFactory<ProvinceBLL>.Instance.GetAll(s=>s.ProvinceName);
this.dataGridView1.DataSource = list; Console.WriteLine("花费时间:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds);
} private void btnCity_Click(object sender, EventArgs e)
{
DateTime dt = DateTime.Now; CityBLL bll = new CityBLL();
var result = bll.GetAll(); this.dataGridView1.DataSource = result; Console.WriteLine("花费时间:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds);
}
如果需要考虑分页,以上接口已经定义了分页处理的接口和实现了,我们在业务对象里面直接调用接口就可以了,具体代码如下所示。
CityBLL bll = new CityBLL();
PagerInfo info = new PagerInfo();
info.PageSize = ;
info.CurrenetPageIndex = ; ICollection<City> list;
if (i++ % == )
{
sortType = "自定义排序";
//使用自定义排序
list = bll.FindWithPager(s => s.CityName.Contains("南"), info, o => o.ID, true);
}
else
{
sortType = "默认字段排序";
//使用默认字段排序
list = bll.FindWithPager(s => s.CityName.Contains("南"), info);
} this.dataGridView1.DataSource = list;
2、异步操作的定义和调用
在EF里面实现异步(并行)非常容易,在.NET 4.5里由于async/await关键字的出现,使得实现异步变得更加容易。
使用await关键字后,.NET会自动把返回结果包装在一个Task类型的对象中。使用await表达式时,控制会返回到调用此方法的线程中;在await等待的方法执行完毕后,控制会自动返回到下面的语句中。发生异常时,异常会在await表达式中抛出。
我们基本上所有的增删改查、分页等接口,都可以使用异步操作来定义这些新接口,代码如下所示。
1)增加对象异步实现
异步定义的接口如下所示
/// <summary>
/// 插入指定对象到数据库中(异步)
/// </summary>
/// <param name="t">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
Task<bool> InsertAsync(T t);
接口的实现如下所示
/// <summary>
/// 插入指定对象到数据库中(异步)
/// </summary>
/// <param name="t">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual async Task<bool> InsertAsync(T t)
{
ArgumentValidation.CheckForNullReference(t, "传入的对象t为空"); objectSet.Add(t);
return await baseContext.SaveChangesAsync() > ;
}
和普通的接口定义不一样的地方,我们看到异步的接口都是以Async结尾,并且返回值使用Task<T>进行包装,另外实现里面,增加了async的定义,方法体里面增加 await 的关键字,这些就构成了异步操作的接口定义和接口实现了。
2)条件删除异步实现
我们再来看一个复杂一点的条件删除操作,代码如下所示。
定义接口
/// <summary>
/// 根据指定条件,从数据库中删除指定对象(异步)
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
Task<bool> DeleteByConditionAsync(Expression<Func<T, bool>> match);
接口实现
/// <summary>
/// 根据指定条件,从数据库中删除指定对象(异步)
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual async Task<bool> DeleteByConditionAsync(Expression<Func<T, bool>> match)
{
objectSet.Where<T>(match).ToList<T>().ForEach(d => baseContext.Entry<T>(d).State = EntityState.Deleted);
return await baseContext.SaveChangesAsync() > ;
}
我们定义的这些异步接口,基本上都是类似的操作,但是我们应该如何调用异步的处理呢?
好像有两个调用代码方式。
1)使用async和await关键字处理
private async void btnCity_Click(object sender, EventArgs e)
{
DateTime dt = DateTime.Now; CityBLL bll = new CityBLL();
var result = await bll.GetAllAsync(); this.dataGridView1.DataSource = result; Console.WriteLine("花费时间:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds);
}
2)使用 await Task.Run的处理方式
private async void btnCity_Click(object sender, EventArgs e)
{
DateTime dt = DateTime.Now; CityBLL bll = new CityBLL();
var result = await Task.Run(() =>
{
var list = bll.GetAllAsync();
return list;
}); this.dataGridView1.DataSource = result; Console.WriteLine("花费时间:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds);
}
两种方式都能正常运行,并得到所要的效果。
本篇主要介绍了基类接口的统一封装、并增加所有必要的增删改查、分页查询、Lambda条件等处理方式,还有就是增加了相关的异步操作接口和实现,随着我们对通用功能的进一步要求,可以为基类增加更多的接口函数。
这个系列文章索引如下:
Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)
Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)
Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)
Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)的更多相关文章
- Entity Framework 实体框架的形成之旅--为基础类库接口增加单元测试,对基类接口进行正确性校验(10)
本篇介绍Entity Framework 实体框架的文章已经到了第十篇了,对实体框架的各个分层以及基类的封装管理,已经臻于完善,为了方便对基类接口的正确性校验,以及方便对以后完善或扩展接口进行回归测试 ...
- Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)
在前面几篇关于Entity Framework 实体框架的介绍里面,已经逐步对整个框架进行了一步步的演化,以期达到统一.高效.可重用性等目的,本文继续探讨基于泛型的仓储模式实体框架方面的改进优化,使我 ...
- Entity Framework 实体框架的形成之旅--几种数据库操作的代码介绍(9)
本篇主要对常规数据操作的处理和实体框架的处理代码进行对比,以便更容易学习理解实体框架里面,对各种数据库处理技巧,本篇介绍几种数据库操作的代码,包括写入中间表操作.联合中间表获取对象集合.递归操作.设置 ...
- Entity Framework 实体框架的形成之旅--界面操作的几个典型的处理(8)
在上篇随笔<Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合>里面,介绍了在Entity Framework 实体框架里面引入了 ...
- Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合
在使用Entity Framework 实体框架的时候,我们大多数时候操作的都是实体模型Entity,这个和数据库操作上下文结合,可以利用LINQ等各种方便手段,实现起来非常方便,一切看起来很美好.但 ...
- Entity Framework 实体框架的形成之旅--Code First的框架设计(5)
在前面几篇介绍了Entity Framework 实体框架的形成过程,整体框架主要是基于Database First的方式构建,也就是利用EDMX文件的映射关系,构建表与表之间的关系,这种模式弹性好, ...
- Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)
在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...
- Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)
很久没有写博客了,一些读者也经常问问一些问题,不过最近我确实也很忙,除了处理日常工作外,平常主要的时间也花在了继续研究微软的实体框架(EntityFramework)方面了.这个实体框架加入了很多特性 ...
- Entity Framework 实体框架的形成之旅--实体框架的开发的几个经验总结
在前阵子,我对实体框架进行了一定的研究,然后把整个学习的过程开了一个系列,以逐步深入的方式解读实体框架的相关技术,期间每每碰到一些新的问题需要潜入研究.本文继续前面的主题介绍,着重从整体性的来总结一下 ...
随机推荐
- java提高篇(八)----详解内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面 ...
- 【案例分享】SQL job服务连接不到存储引擎
在SQL Server agent的日志中发现有大量错误的信息,都是关于作业执行失败的日志 从日志的内容来看,是作业执行过程中无法连接到存储引擎, 可奇怪的是,存储引擎就是本机,而且已经正常启动了,截 ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇
在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP. ...
- Setting up SSL for SCM-Manager with Microsoft CA and TortoiseHg
You can configure SSL for SCM-Manager so that the communication of your repositories are encrypted. ...
- EF架构~为IEnumerable接口添加增删查等操作,原因是IEnumerable导航属性更放心
回到目录 对EF开发来说,导航属性肯定都用过,事实上,它是由VS IDE工具根据你的数据库关系结构自动生成的外键属性,在类视图中可以看到相关属性,它是以外键表名来标识的,如果是一对多的关系,那么,它会 ...
- [Spring框架]Spring JDBCTmplate基础入门总结.
前言:前面有讲过 Spring IOC以及AOP的基本使用方法, 这里就再来讲下Spring JDBCTemplate的使用方法. 一, 概述这里先说一下Spring 整合的一些模板: 从上图中可以看 ...
- 浅谈JAVA集合框架
浅谈JAVA集合框架 Java提供了数种持有对象的方式,包括语言内置的Array,还有就是utilities中提供的容器类(container classes),又称群集类(collection cl ...
- Sqlserver 中exists 和 in
如图,现在有两个数据集,左边表示#tempTable1,右边表示#tempTable2.现在有以下问题: 1.求两个集的交集? 2.求tempTable1中不属于集#tempTable2的集? 先创建 ...
- KnockoutJS 3.X API 第四章 表单绑定(7) event绑定
目的 event绑定即为事件绑定,即当触发相关DOM事件的时候回调函数.例如keypress,mouseover或者mouseout等 例如: Mouse over me Details var vi ...
- Java并发包中Semaphore的工作原理、源码分析及使用示例
1. 信号量Semaphore的介绍 我们以一个停车场运作为例来说明信号量的作用.假设停车场只有三个车位,一开始三个车位都是空的.这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦.以后 ...