在上一篇,咱们搭建了一个项目框架,基本上是一个完整的项目。目前而言,大部分的应用基本都是这个结构。好的,不废话了,进入今天的议题:完成并实现数据层的基础实现。数据库
一般状况下,一个项目的数据实体中字段并非彻底没有规律可寻。一般状况下,必须有一个主键。有些时候,会要求在数据表中增长上次修改时间和建立时间,以及建立人和修改人的主键。架构
因此,咱们能够建立一个泛型父类,来帮咱们定义这些公共字段:框架
using System;
namespace Data.Infrastructure
{
public class BaseEntity<T>
{
public T Id { get; set; }
public string ModifyUserId { get; set; }
public DateTime? ModifyTime { get; set; }
public string CreatorId { get; set; }
public DateTime? CreateTime { get; set; }
}
}
看上述代码里,命名空间并不在Data里,而是在Data.Infrastructure里。这个命名空间 Infrastructure 用来存放一些项目的架构类或者接口,里面还会其余的类。ide
那么,给这个类补充一些可能有用的方法:大数据
public void Create(object userId)
{
CreatorId = userId.ToString();
CreateTime = DateTime.Now;
}
public void Create(object userId, DateTime createTime)
{
CreatorId = userId.ToString();
CreateTime = createTime;
}
public void Modify(object userId)
{
ModifyUserId = userId.ToString();
ModifyTime = DateTime.Now;
}
public void Modify(object userId, DateTime modifyTime)
{
ModifyUserId = userId.ToString();
ModifyTime = modifyTime;
}
这里用来保存用户ID的字段,我都用了字符串作保存,是借用字符串类型保存数据时能容纳更多的数据类型。spa
在正常开发中,一个完整的数据操做接口会有不少分类,可是不少时候咱们须要分开增删改和查询这两种操做。对于数据库而言,视图和有些数据表都是不被容许改变的,这时候就须要咱们只对调用方开放查询接口,而不开放修改接口。code
因此,在Domain下应该有如下两个接口:对象
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Domain.Infrastructure
{
/// <summary>
/// 修改接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IModifyRepository<T>
{
/// <summary>
/// 插入数据
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
T Insert(T entity);
/// <summary>
/// 插入数据
/// </summary>
/// <param name="entities"></param>
void Insert(params T[] entities);
/// <summary>
/// 插入数据
/// </summary>
/// <param name="entities"></param>
void Insert(IEnumerable<T> entities);
/// <summary>
/// 保存已提交的修改
/// </summary>
/// <param name="entity"></param>
void Update(T entity);
/// <summary>
/// 保存已提交的修改
/// </summary>
/// <param name="entities"></param>
void Update(params T[] entities);
/// <summary>
/// 更新数据
/// </summary>
/// <param name="predicate"></param>
/// <param name="updator"></param>
void Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updator);
/// <summary>
/// 删除
/// </summary>
/// <param name="entity"></param>
void Delete(T entity);
/// <summary>
/// 删除数据
/// </summary>
/// <param name="entities"></param>
void Delete(params T[] entities);
/// <summary>
/// 根据条件删除数据
/// </summary>
/// <param name="predicate"></param>
void Delete(Expression<Func<T,bool>> predicate);
/// <summary>
/// 删除主键对应的数据
/// </summary>
/// <param name="key"></param>
void DeleteByKey(object key);
/// <summary>
/// 删除主键对应的数据
/// </summary>
/// <param name="keys"></param>
void DeleteByKeys(params object[] keys);
}
}
上述是更新接口,那么咱们回过头来写查询接口,查询接口的方法有不少。咱们先建立一个接口文件:blog
using System;
using System.Linq.Expressions;
namespace Domain.Infrastructure
{
/// <summary>
/// 查询接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ISearchRepository<T>
{
}
}
一个查询接口应该包括如下方法:排序
获取单个数据
/// <summary>
/// 根据主键获取数据
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
T Get(object key);
/// <summary>
/// 查询
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
T Get(Expression<Func<T,bool>> predicate);
统计数量:
/// <summary>
/// 返回数据库中的数据条目
/// </summary>
/// <returns></returns>
int Count();
/// <summary>
/// 返回数据库中的数据条目,类型为Long
/// </summary>
/// <returns></returns>
long LongCount();
/// <summary>
/// 返回符合条件的数据数目
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
int Count(Expression<Func<T,bool>> predicate);
/// <summary>
/// 返回长整形的符合条件的数目
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
long LongCount(Expression<Func<T,bool>> predicate);
存在性判断
/// <summary>
/// 是否存在知足条件的数据
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
bool IsExists(Expression<Func<T, bool>> predicate);
查询
// <summary>
/// 返回数据库中全部记录
/// </summary>
/// <returns></returns>
List<T> Search();
/// <summary>
/// 返回全部符合条件的数据
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
List<T> Search(Expression<Func<T,bool>> predicate);
/// <summary>
/// 返回一个延迟查询的对象
/// </summary>
/// <returns></returns>
IEnumerable<T> Query();
/// <summary>
/// 返回一个延迟查询的对象,并预设了一个查询条件
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
IEnumerable<T> Query(Expression<Func<T,bool>> predicate);
排序
/// <summary>
/// 排序查询,默认升序
/// </summary>
/// <param name="predicate"></param>
/// <param name="order"></param>
/// <typeparam name="P"></typeparam>
/// <returns></returns>
List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order);
/// <summary>
/// 排序查找,指定是否降序排列
/// </summary>
/// <param name="predicate"></param>
/// <param name="order"></param>
/// <param name="isDesc"></param>
/// <typeparam name="P"></typeparam>
/// <returns></returns>
List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order, bool isDesc);
分页
实际上分页的接口定义模型须要两个类的辅助,若是没有这两个类,接口的定义会变得十分复杂,不利于代码的可读性:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Data.Infrastructure
{
/// <summary>
/// 分页条件模型
/// </summary>
/// <typeparam name="T"></typeparam>
public class PageCondition<T>
{
/// <summary>
/// 查询条件
/// </summary>
/// <value></value>
public Expression<Func<T, bool>> Predicate { get; set; }
/// <summary>
/// 排序字段
/// </summary>
/// <value></value>
public string OrderProperty { get; set; }
/// <summary>
/// 升序排序或者降序排序,升序为 asc或者空,降序为desc
/// </summary>
/// <value></value>
public string Sort{get;set;}
/// <summary>
/// 每页最大数据容量
/// </summary>
/// <value></value>
public int PerpageSize { get; set; }
/// <summary>
/// 当前页
/// </summary>
/// <value></value>
public int CurrentPage { get; set; }
}
/// <summary>
/// 分页结果
/// </summary>
/// <typeparam name="T"></typeparam>
public class PageModel<T>
{
/// <summary>
/// 数据
/// </summary>
/// <value></value>
public List<T> Items { get; set; }
/// <summary>
/// 当前页码
/// </summary>
/// <value></value>
public int CurrentPage { get; set; }
/// <summary>
/// 每页最大数据容量
/// </summary>
/// <value></value>
public int PerpageSize { get; set; }
/// <summary>
/// 查询数据总数
/// </summary>
/// <value></value>
public long TotalCount { get; set; }
/// <summary>
/// 总页码
/// </summary>
/// <value></value>
public int TotalPages { get; set; }
}
}
这是两个辅助类,能够简单看一下若是这些参数不进行封装直接传给方法,能够预见方法的参数列表会特别长,这对于可读性和可维护性来讲简直就是灾难。我曾经接手过一个项目的维护,上一位开发者在一个方法写了近15个参数,并且还有大量的可选参数,嗯,十分头疼。因此,我不建议你们这样写,一个方法参数超过4个我建议仍是封装一下。
那么,看一看方法的声明:
/// <summary>
/// 根据分页参数设置,进行分页查询
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
PageModel<T> Search(PageCondition<T> condition);
这是使用参数封装了请求的写法,小伙伴们能够试试不用封装,方法是如何声明的。
在这一篇带领你们梳理了一下数据访问的接口定义,对一个系统来讲,这些方法都是有必要的(但不是每一个方法使用频率都同样高)。也是简单的跟你们分享一下我在实际工做中写代码的总结。