浅谈orm

  记得四年前在学校第一次接触到 Ling to Sql,那时候瞬间发现不用手写sql语句是多么的方便,后面慢慢的接触了许多orm框架,像 EF,Dapper,Hibernate,ServiceStack.OrmLite 等。当然每种orm都有各自的优势,也有不足的地方。园子里也有很多大神开源了他们写的orm,如SqlSugar,Chloe.ORM,CYQ.Data 等。先不说这些开源的orm使用度怎么样,我觉得起码从开源的精神上就很可嘉了,我也曾下载过这几位大神的源码进行学习。
  所有orm最终体现的一点就是方便,减少程序员重复性的工作,当然目前还有一些公司还是使用手写sql的方式来做,我觉得整个项目都使用手写sql来做真的是有点闲到蛋疼,并不是不推荐手写sql的方式,只是个人觉得最基本的增删改查这些都手写的话,那其实考验不是能力,而是耐力。有人说手写sql的方式可控性强,性能高,我想说的是orm也能做到,关键是你怎么去使用。
  orm的优点非常明显,开发便捷,但或许也是由于这个优点,让很多偷懒的程序员也会渐渐忘了sql语句的写法,我遇到过很多的程序员朋友用了EF后,手写sql,视图、存储过程这些都不想用了,我个人觉手写sql这种还是必要的。不然某一天你看到别人的程序里面写着 “exec xxxx”,你就会突然觉得“啊,好像在哪里见过.....”。所以我想说的是“该出手时还是得出手"。

浅谈Entity Framework

  Entity Framework 是微软家的orm框架,随着 Entity Framework 不断的完善强化,目前相信使用的比例相对其他的orm来说还是较高的。像我目前使用的最多的就是EF和Dapper。确实,EF用起来开发过程中会方便很多,毕竟EF走过了这么年头,无论是成熟度,还是性能等都提高了很多,也有很多开发者为EF提供了扩展功能,如entity framework extended 等。而且作为.net开发者来说项目通用性也很强,资料也多,微软在这块的更新力度也很给力。不过之前刚出的EF Core也存在一些坑,毕竟还是初期阶段,相信现在后面会越来越好的。

  Entity Framework  提供了三种开发模式,code first,db first,model first。目前用的最多的就属code first了。至于这三种模式的简单使用和区别,大家可以参考下这篇文章

  我曾听一些朋友说过说EF使用起来性能很差,生成的sql语句很难看等。我觉得说这种话之前还是先检查下代码或者多看下一些EF文章吧,要先确保自己没给自己挖坑,然后才能指责别人的不好。如果真心觉得EF或者其他的orm用起来很不爽,那就自己写一个吧,我也曾经和同事用Dapper扩展一个通用的orm,当时是出于一种学习和使用方便的角度。

Entity Framework 通用数据层类

  这里提供下 EF 通用数据层父类方法,其实网上也有很多人提供了自己项目中的 EF 通用数据层父类方法,所以这里提供的并不是最优和最好的选择,只能说是可以通用的类,方便大家学习和使用,具体代码如下:

DbContextFactory DbContext工厂类

    public class DbContextFactory
{
public DbContext GetDbContext()
{
string key = typeof(DBContext.DbContextFactory).Name + "XJHDbContext";
DbContext dbContext = CallContext.GetData(key) as DbContext;
if (dbContext == null)
{
dbContext = new XJHDbContext();
CallContext.SetData(key, dbContext);
}
return dbContext;
}
}

DbBase 数据层通用操作类

 public class DbBase
{
protected DbContext Db = new DbContextFactory().GetDbContext(); #region 自定义其他方法 /// <summary>
/// 执行存储过程或自定义sql语句--返回集合(自定义返回类型)
/// </summary>
/// <param name="sql"></param>
/// <param name="parms"></param>
/// <param name="cmdType"></param>
/// <returns></returns>
public List<TModel> Query<TModel>(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
{
//存储过程(exec getActionUrlId @name,@ID)
if (cmdType == CommandType.StoredProcedure)
{
StringBuilder paraNames = new StringBuilder();
foreach (var sqlPara in parms)
{
paraNames.Append($" @{sqlPara},");
}
sql = paraNames.Length > ? $"exec {sql} {paraNames.ToString().Trim(',')}" : $"exec {sql} ";
}
var list = Db.Database.SqlQuery<TModel>(sql, parms.ToArray());
var enityList = list.ToList();
return enityList;
} /// <summary>
/// 自定义语句和存储过程的增删改--返回影响的行数
/// </summary>
/// <param name="sql"></param>
/// <param name="parms"></param>
/// <param name="cmdType"></param>
/// <returns></returns>
public int Execute(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
{
//存储过程(exec getActionUrlId @name,@ID)
if (cmdType == CommandType.StoredProcedure)
{
StringBuilder paraNames = new StringBuilder();
foreach (var sqlPara in parms)
{
paraNames.Append($" @{sqlPara},");
}
sql = paraNames.Length > ?
$"exec {sql} {paraNames.ToString().Trim(',')}" :
$"exec {sql} ";
}
int ret = Db.Database.ExecuteSqlCommand(sql, parms.ToArray());
return ret;
} #endregion 自定义其他方法
} /// <summary>
/// mssql数据库 数据层 父类
/// </summary>
/// <typeparam name="T"></typeparam>
public class DbBase<T> : DbBase where T : class, new()
{
#region INSERT /// <summary>
/// 新增 实体
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public void Insert(T model)
{
Db.Set<T>().Add(model); } /// <summary>
/// 普通批量插入
/// </summary>
/// <param name="datas"></param>
public void InsertRange(List<T> datas)
{
Db.Set<T>().AddRange(datas);
} #endregion INSERT #region DELETE /// <summary>
/// 根据模型删除
/// </summary>
/// <param name="model">包含要删除id的对象</param>
/// <returns></returns>
public void Delete(T model)
{
Db.Set<T>().Attach(model);
Db.Set<T>().Remove(model);
} /// <summary>
/// 删除
/// </summary>
/// <param name="whereLambda"></param>
public void Delete(Expression<Func<T, bool>> whereLambda)
{
Db.Set<T>().Where(whereLambda).Delete();
} #endregion DELETE #region UPDATE /// <summary>
/// 单个对象指定列修改
/// </summary>
/// <param name="model">要修改的实体对象</param>
/// <param name="proNames">要修改的 属性 名称</param>
/// <param name="isProUpdate"></param>
/// <returns></returns>
public void Update(T model, List<string> proNames, bool isProUpdate = true)
{
//将 对象 添加到 EF中
Db.Set<T>().Attach(model);
var setEntry = ((IObjectContextAdapter)Db).ObjectContext.ObjectStateManager.GetObjectStateEntry(model);
//指定列修改
if (isProUpdate)
{
foreach (string proName in proNames)
{
setEntry.SetModifiedProperty(proName);
}
}
//忽略类修改
else
{
Type t = typeof(T);
List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
foreach (var item in proInfos)
{
string proName = item.Name;
if (proNames.Contains(proName))
{
continue;
}
setEntry.SetModifiedProperty(proName);
}
}
} /// <summary>
/// 单个对象修改
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public void Update(T model)
{
DbEntityEntry entry = Db.Entry<T>(model);
Db.Set<T>().Attach(model);
entry.State = EntityState.Modified; } /// <summary>
/// 批量修改
/// </summary>
/// <param name="whereLambda"></param>
/// <param name="updateExpression"></param>
public void Update(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> updateExpression)
{
Db.Set<T>().Where(whereLambda).Update(updateExpression);
} /// <summary>
/// 批量修改
/// </summary>
/// <param name="models"></param>
/// <returns></returns>
public void UpdateAll(List<T> models)
{
foreach (var model in models)
{
DbEntityEntry entry = Db.Entry(model);
entry.State = EntityState.Modified;
} } /// <summary>
/// 批量统一修改
/// </summary>
/// <param name="model">要修改的实体对象</param>
/// <param name="whereLambda">查询条件</param>
/// <param name="modifiedProNames"></param>
/// <returns></returns>
public void Update(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
{
//查询要修改的数据
List<T> listModifing = Db.Set<T>().Where(whereLambda).ToList();
Type t = typeof(T);
List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo>();
proInfos.ForEach(p =>
{
if (modifiedProNames.Contains(p.Name))
{
dictPros.Add(p.Name, p);
}
});
if (dictPros.Count <= )
{
throw new Exception("指定修改的字段名称有误或为空");
}
foreach (var item in dictPros)
{
PropertyInfo proInfo = item.Value; //取出 要修改的值
object newValue = proInfo.GetValue(model, null); //批量设置 要修改 对象的 属性
foreach (T oModel in listModifing)
{
//为 要修改的对象 的 要修改的属性 设置新的值
proInfo.SetValue(oModel, newValue, null);
}
} } #endregion UPDATE #region SELECT /// <summary>
/// 根据主键查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public T FindById(dynamic id)
{
return Db.Set<T>().Find(id);
} /// <summary>
/// 获取默认一条数据,没有则为NULL
/// </summary>
/// <param name="whereLambda"></param>
/// <returns></returns>
public T FirstOrDefault(Expression<Func<T, bool>> whereLambda = null)
{
if (whereLambda == null)
{
return Db.Set<T>().FirstOrDefault();
}
return Db.Set<T>().FirstOrDefault(whereLambda);
} /// <summary>
/// 获取全部数据
/// </summary>
/// <returns></returns>
public List<T> GetAll(string ordering = null)
{
return ordering == null
? Db.Set<T>().ToList()
: Db.Set<T>().OrderBy(ordering).ToList();
} /// <summary>
/// 带条件查询获取数据
/// </summary>
/// <param name="whereLambda"></param>
/// <param name="ordering"></param>
/// <returns></returns>
public List<T> GetAll(Expression<Func<T, bool>> whereLambda, string ordering = null)
{
var iQueryable = Db.Set<T>().Where(whereLambda);
return ordering == null
? iQueryable.ToList()
: iQueryable.OrderBy(ordering).ToList();
} /// <summary>
/// 带条件查询获取数据
/// </summary>
/// <param name="whereLambda"></param>
/// <returns></returns>
public IQueryable<T> GetAllIQueryable(Expression<Func<T, bool>> whereLambda = null)
{
return whereLambda == null ? Db.Set<T>() : Db.Set<T>().Where(whereLambda);
} /// <summary>
/// 获取数量
/// </summary>
/// <param name="whereLambd"></param>
/// <returns></returns>
public int GetCount(Expression<Func<T, bool>> whereLambd = null)
{
return whereLambd == null ? Db.Set<T>().Count() : Db.Set<T>().Where(whereLambd).Count();
} /// <summary>
/// 判断对象是否存在
/// </summary>
/// <param name="whereLambd"></param>
/// <returns></returns>
public bool Any(Expression<Func<T, bool>> whereLambd)
{
return Db.Set<T>().Where(whereLambd).Any();
} /// <summary>
/// 分页查询
/// </summary>
/// <param name="pageIndex">当前页码</param>
/// <param name="pageSize">每页大小</param>
/// <param name="rows">总条数</param>
/// <param name="orderBy">排序条件(一定要有)</param>
/// <param name="whereLambda">查询添加(可有,可无)</param>
/// <param name="isOrder">是否是Order排序</param>
/// <returns></returns>
public List<T> Page<TKey>(int pageIndex, int pageSize, out int rows, Expression<Func<T, TKey>> orderBy, Expression<Func<T, bool>> whereLambda = null, bool isOrder = true)
{
IQueryable<T> data = isOrder ?
Db.Set<T>().OrderBy(orderBy) :
Db.Set<T>().OrderByDescending(orderBy); if (whereLambda != null)
{
data = data.Where(whereLambda);
}
rows = data.Count();
return data.PageBy((pageIndex - ) * pageSize, pageSize).ToList();
} /// <summary>
/// 分页查询
/// </summary>
/// <param name="pageIndex">当前页码</param>
/// <param name="pageSize">每页大小</param>
/// <param name="rows">总条数</param>
/// <param name="ordering">排序条件(一定要有)</param>
/// <param name="whereLambda">查询添加(可有,可无)</param>
/// <returns></returns>
public List<T> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<Func<T, bool>> whereLambda = null)
{
// 分页 一定注意: Skip 之前一定要 OrderBy
var data = Db.Set<T>().OrderBy(ordering);
if (whereLambda != null)
{
data = data.Where(whereLambda);
}
rows = data.Count();
return data.PageBy((pageIndex - ) * pageSize, pageSize).ToList();
} /// <summary>
/// 查询转换
/// </summary>
/// <typeparam name="TDto"></typeparam>
/// <param name="whereLambda"></param>
/// <returns></returns>
public List<TDto> Select<TDto>(Expression<Func<T, bool>> whereLambda)
{
return Db.Set<T>().Where(whereLambda).Select<TDto>().ToList();
} #endregion SELECT #region ORTHER /// <summary>
/// 执行存储过程或自定义sql语句--返回集合
/// </summary>
/// <param name="sql"></param>
/// <param name="parms"></param>
/// <param name="cmdType"></param>
/// <returns></returns>
public List<T> Query(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
{
return Query<T>(sql, parms, cmdType);
} /// <summary>
/// 提交保存
/// </summary>
/// <returns></returns>
public int SaveChanges()
{
return Db.SaveChanges();
} /// <summary>
/// 回滚
/// </summary>
public void RollBackChanges()
{
var items = Db.ChangeTracker.Entries().ToList();
items.ForEach(o => o.State = EntityState.Unchanged);
} #endregion ORTHER }

DbBase

扩展类,实现读写分离

  上面的通用类是比较基础简单通用的,适合于单库读写操作。对于EF实现读写分离,之前网上找过类似的参考文章,很多人文章都是使用 DbCommandInterceptor拦截器 来实现,具体的做法是通过拦截到sql语句,然后根据具体条件去判断是走主库还是从库。这种做法不是不行,只是个人感觉不是很好扩展,而且要在拦截器里面做限制判断。

  其实说白了EF本身就是一个读写分离的orm。用过EF的人知道,EF提供访问数据库的是 DbContext 这个对象,所以想实现读写分离的就很简单了,只要在程序中使用两个不同的DbContext对象,一个负责读,一个负责写就好了。

  所以在上面提供的通用封装类中稍微做下修改,修改如下DbContextFactory中获取DbContext的方法,实现一个读的DbContext和一个写的DbContext对象的获取。

  这里要注意下,对于读的DbContext来说,要做下设置
  1.使用 Database.SetInitializer(new NullDatabaseInitializer<ReadDbContext>()); 改变ef初始化策略,因为一般我们使用读写分离的话从库都是同步于主库的,所以不使用ef的自动创建数据库策略。
  2.重写 SaveChanges 方法,对应从库来说,只提供读取的功能,所以防止误操作,这里禁用掉SaveChanges方法,一般需要使用从读的保存方法,就对外抛出异常。

  代码如下:

支持读写分离的 DbContextFactory 类

  public class DbContextFactory
{
public DbContext GetWriteDbContext()
{
string key = typeof(DbContextFactory).Name + "WriteDbContext";
DbContext dbContext = CallContext.GetData(key) as DbContext;
if (dbContext == null)
{
dbContext = new WriteDbContext();
CallContext.SetData(key, dbContext);
}
return dbContext;
} public DbContext GetReadDbContext()
{
string key = typeof(DbContextFactory).Name + "ReadDbContext";
DbContext dbContext = CallContext.GetData(key) as DbContext;
if (dbContext == null)
{
dbContext = new ReadDbContext();
CallContext.SetData(key, dbContext);
}
return dbContext;
}
}

对应的 DbBase 类也做下修改,主要将上面的Db对象改作 MasterDb  SlaveDb 对象,并且把上面的读写方法坐下调整,修改后如下:

支持读写分离的 DbBase类

public class DbBase
{
//是否读写分离(可以配置在配置文件中)
private static readonly bool IsReadWriteSeparation = true; #region EF上下文对象(主库) protected DbContext MasterDb => _masterDb.Value;
private readonly Lazy<DbContext> _masterDb = new Lazy<DbContext>(() => new DbContextFactory().GetWriteDbContext()); #endregion EF上下文对象(主库) #region EF上下文对象(从库) protected DbContext SlaveDb => IsReadWriteSeparation ? _slaveDb.Value : _masterDb.Value;
private readonly Lazy<DbContext> _slaveDb = new Lazy<DbContext>(() => new DbContextFactory().GetReadDbContext()); #endregion EF上下文对象(从库) #region 自定义其他方法 /// <summary>
/// 执行存储过程或自定义sql语句--返回集合(自定义返回类型)
/// </summary>
/// <param name="sql"></param>
/// <param name="parms"></param>
/// <param name="cmdType"></param>
/// <returns></returns>
public List<TModel> Query<TModel>(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
{
//存储过程(exec getActionUrlId @name,@ID)
if (cmdType == CommandType.StoredProcedure)
{
StringBuilder paraNames = new StringBuilder();
foreach (var sqlPara in parms)
{
paraNames.Append($" @{sqlPara},");
}
sql = paraNames.Length > ? $"exec {sql} {paraNames.ToString().Trim(',')}" : $"exec {sql} ";
}
var list = SlaveDb.Database.SqlQuery<TModel>(sql, parms.ToArray());
var enityList = list.ToList();
return enityList;
} /// <summary>
/// 自定义语句和存储过程的增删改--返回影响的行数
/// </summary>
/// <param name="sql"></param>
/// <param name="parms"></param>
/// <param name="cmdType"></param>
/// <returns></returns>
public int Execute(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
{
//存储过程(exec getActionUrlId @name,@ID)
if (cmdType == CommandType.StoredProcedure)
{
StringBuilder paraNames = new StringBuilder();
foreach (var sqlPara in parms)
{
paraNames.Append($" @{sqlPara},");
}
sql = paraNames.Length > ?
$"exec {sql} {paraNames.ToString().Trim(',')}" :
$"exec {sql} ";
}
int ret = MasterDb.Database.ExecuteSqlCommand(sql, parms.ToArray());
return ret;
} #endregion 自定义其他方法
} /// <summary>
/// mssql数据库 数据层 父类
/// </summary>
/// <typeparam name="T"></typeparam>
public class DbBase<T> : DbBase where T : class, new()
{
#region INSERT /// <summary>
/// 新增 实体
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public void Insert(T model)
{
MasterDb.Set<T>().Add(model);
} /// <summary>
/// 普通批量插入
/// </summary>
/// <param name="datas"></param>
public void InsertRange(List<T> datas)
{
MasterDb.Set<T>().AddRange(datas);
} #endregion INSERT #region DELETE /// <summary>
/// 根据模型删除
/// </summary>
/// <param name="model">包含要删除id的对象</param>
/// <returns></returns>
public void Delete(T model)
{
MasterDb.Set<T>().Attach(model);
MasterDb.Set<T>().Remove(model);
} /// <summary>
/// 删除
/// </summary>
/// <param name="whereLambda"></param>
public void Delete(Expression<Func<T, bool>> whereLambda)
{
MasterDb.Set<T>().Where(whereLambda).Delete();
} #endregion DELETE #region UPDATE /// <summary>
/// 单个对象指定列修改
/// </summary>
/// <param name="model">要修改的实体对象</param>
/// <param name="proNames">要修改的 属性 名称</param>
/// <param name="isProUpdate"></param>
/// <returns></returns>
public void Update(T model, List<string> proNames, bool isProUpdate = true)
{
//将 对象 添加到 EF中
MasterDb.Set<T>().Attach(model);
var setEntry = ((IObjectContextAdapter)MasterDb).ObjectContext.ObjectStateManager.GetObjectStateEntry(model);
//指定列修改
if (isProUpdate)
{
foreach (string proName in proNames)
{
setEntry.SetModifiedProperty(proName);
}
}
//忽略类修改
else
{
Type t = typeof(T);
List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
foreach (var item in proInfos)
{
string proName = item.Name;
if (proNames.Contains(proName))
{
continue;
}
setEntry.SetModifiedProperty(proName);
}
}
} /// <summary>
/// 单个对象修改
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public void Update(T model)
{
DbEntityEntry entry = MasterDb.Entry<T>(model);
MasterDb.Set<T>().Attach(model);
entry.State = EntityState.Modified;
} /// <summary>
/// 批量修改
/// </summary>
/// <param name="whereLambda"></param>
/// <param name="updateExpression"></param>
public void Update(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> updateExpression)
{
MasterDb.Set<T>().Where(whereLambda).Update(updateExpression);
} /// <summary>
/// 批量修改
/// </summary>
/// <param name="models"></param>
/// <returns></returns>
public void UpdateAll(List<T> models)
{
foreach (var model in models)
{
DbEntityEntry entry = MasterDb.Entry(model);
entry.State = EntityState.Modified;
}
} /// <summary>
/// 批量统一修改
/// </summary>
/// <param name="model">要修改的实体对象</param>
/// <param name="whereLambda">查询条件</param>
/// <param name="modifiedProNames"></param>
/// <returns></returns>
public void Update(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
{
//查询要修改的数据
List<T> listModifing = MasterDb.Set<T>().Where(whereLambda).ToList();
Type t = typeof(T);
List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo>();
proInfos.ForEach(p =>
{
if (modifiedProNames.Contains(p.Name))
{
dictPros.Add(p.Name, p);
}
});
if (dictPros.Count <= )
{
throw new Exception("指定修改的字段名称有误或为空");
}
foreach (var item in dictPros)
{
PropertyInfo proInfo = item.Value; //取出 要修改的值
object newValue = proInfo.GetValue(model, null); //批量设置 要修改 对象的 属性
foreach (T oModel in listModifing)
{
//为 要修改的对象 的 要修改的属性 设置新的值
proInfo.SetValue(oModel, newValue, null);
}
}
} #endregion UPDATE #region SELECT /// <summary>
/// 根据主键查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public T FindById(dynamic id)
{
return SlaveDb.Set<T>().Find(id);
} /// <summary>
/// 获取默认一条数据,没有则为NULL
/// </summary>
/// <param name="whereLambda"></param>
/// <returns></returns>
public T FirstOrDefault(Expression<Func<T, bool>> whereLambda = null)
{
if (whereLambda == null)
{
return SlaveDb.Set<T>().FirstOrDefault();
}
return SlaveDb.Set<T>().FirstOrDefault(whereLambda);
} /// <summary>
/// 获取全部数据
/// </summary>
/// <returns></returns>
public List<T> GetAll(string ordering = null)
{
return ordering == null
? SlaveDb.Set<T>().ToList()
: SlaveDb.Set<T>().OrderBy(ordering).ToList();
} /// <summary>
/// 带条件查询获取数据
/// </summary>
/// <param name="whereLambda"></param>
/// <param name="ordering"></param>
/// <returns></returns>
public List<T> GetAll(Expression<Func<T, bool>> whereLambda, string ordering = null)
{
var iQueryable = SlaveDb.Set<T>().Where(whereLambda);
return ordering == null
? iQueryable.ToList()
: iQueryable.OrderBy(ordering).ToList();
} /// <summary>
/// 带条件查询获取数据
/// </summary>
/// <param name="whereLambda"></param>
/// <returns></returns>
public IQueryable<T> GetAllIQueryable(Expression<Func<T, bool>> whereLambda = null)
{
return whereLambda == null ? SlaveDb.Set<T>() : SlaveDb.Set<T>().Where(whereLambda);
} /// <summary>
/// 获取数量
/// </summary>
/// <param name="whereLambd"></param>
/// <returns></returns>
public int GetCount(Expression<Func<T, bool>> whereLambd = null)
{
return whereLambd == null ? SlaveDb.Set<T>().Count() : SlaveDb.Set<T>().Where(whereLambd).Count();
} /// <summary>
/// 判断对象是否存在
/// </summary>
/// <param name="whereLambd"></param>
/// <returns></returns>
public bool Any(Expression<Func<T, bool>> whereLambd)
{
return SlaveDb.Set<T>().Where(whereLambd).Any();
} /// <summary>
/// 分页查询
/// </summary>
/// <param name="pageIndex">当前页码</param>
/// <param name="pageSize">每页大小</param>
/// <param name="rows">总条数</param>
/// <param name="orderBy">排序条件(一定要有)</param>
/// <param name="whereLambda">查询添加(可有,可无)</param>
/// <param name="isOrder">是否是Order排序</param>
/// <returns></returns>
public List<T> Page<TKey>(int pageIndex, int pageSize, out int rows, Expression<Func<T, TKey>> orderBy, Expression<Func<T, bool>> whereLambda = null, bool isOrder = true)
{
IQueryable<T> data = isOrder ?
SlaveDb.Set<T>().OrderBy(orderBy) :
SlaveDb.Set<T>().OrderByDescending(orderBy); if (whereLambda != null)
{
data = data.Where(whereLambda);
}
rows = data.Count();
return data.PageBy((pageIndex - ) * pageSize, pageSize).ToList();
} /// <summary>
/// 分页查询
/// </summary>
/// <param name="pageIndex">当前页码</param>
/// <param name="pageSize">每页大小</param>
/// <param name="rows">总条数</param>
/// <param name="ordering">排序条件(一定要有)</param>
/// <param name="whereLambda">查询添加(可有,可无)</param>
/// <returns></returns>
public List<T> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<Func<T, bool>> whereLambda = null)
{
// 分页 一定注意: Skip 之前一定要 OrderBy
var data = SlaveDb.Set<T>().OrderBy(ordering);
if (whereLambda != null)
{
data = data.Where(whereLambda);
}
rows = data.Count();
return data.PageBy((pageIndex - ) * pageSize, pageSize).ToList();
} /// <summary>
/// 查询转换
/// </summary>
/// <typeparam name="TDto"></typeparam>
/// <param name="whereLambda"></param>
/// <returns></returns>
public List<TDto> Select<TDto>(Expression<Func<T, bool>> whereLambda)
{
return SlaveDb.Set<T>().Where(whereLambda).Select<TDto>().ToList();
} #endregion SELECT #region ORTHER /// <summary>
/// 执行存储过程或自定义sql语句--返回集合
/// </summary>
/// <param name="sql"></param>
/// <param name="parms"></param>
/// <param name="cmdType"></param>
/// <returns></returns>
public List<T> Query(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
{
return Query<T>(sql, parms, cmdType);
} /// <summary>
/// 提交保存
/// </summary>
/// <returns></returns>
public int SaveChanges()
{
return MasterDb.SaveChanges();
} /// <summary>
/// 回滚
/// </summary>
public void RollBackChanges()
{
var items = MasterDb.ChangeTracker.Entries().ToList();
items.ForEach(o => o.State = EntityState.Unchanged);
} #endregion ORTHER
}

DbBase

  这样简单的读写分离就实现了,实现逻辑也比较清晰,方便扩展。

进一步改造,实现多从库读取

  一般做读写分离,都会做一主多从,特别对读取量比较大的项目,这样多库读取就能减轻读库的压力。所以对于上面的方法,做下改造。
  上面可以看到,主库和从库都是通过 DbContextFactory 这个类来获取的,在GetReadDbContext 方法中每次都是获取 ReadDbContext 这个对象。那么对于多个从库的情况下,每次读取到底要去哪个库读取数据呢?这里就是一个算法规则的问题了,或者说是策略吧,如果使用过nginx的朋友就知道,nginx本身内部在实现负载均衡的时候提供了多种策略,比如轮询,加权轮询,ip_hash等策略。其实上面获取同一个ReadDbContext 的方法也算一种策略,叫单一策略,每次都获取单一的对象。

  多从库的情况下,我们简单的来实现另一种获取策略,随机策略,每次都随机获取到一个从库的对象,这种是最简单的策略,当然,正式使用的话大家可以发挥自己的创造力,写出多了的算法策略。

首先,定义一个策略接口,方便策略的扩展和切换,代码如下:

IReadDbStrategy 接口

 /// <summary>
/// 从数据库获取策略接口
/// </summary>
public interface IReadDbStrategy
{
/// <summary>
/// 获取读库
/// </summary>
/// <returns></returns>
DbContext GetDbContext();
}

单从库情况下,定义一个单一策略,代码如下:

单一策略

   /// <summary>
/// 单一策略
/// </summary>
public class SingleStrategy : IReadDbStrategy
{
public DbContext GetDbContext()
{
return new ReadDbContext();
}
}

多从库情况下,定义一个随机策略,代码如下:

随机策略

    /// <summary>
/// 随机策略
/// </summary>
public class RandomStrategy : IReadDbStrategy
{
//所有读库类型
public static List<Type> DbTypes; static RandomStrategy()
{
LoadDbs();
} //加载所有的读库类型
static void LoadDbs()
{
DbTypes = new List<Type>();
var assembly = Assembly.GetExecutingAssembly();
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.BaseType == typeof(BaseReadDbContext))
{
DbTypes.Add(type);
}
}
} public DbContext GetDbContext()
{
int randomIndex = new Random().Next(, DbTypes.Count);
var dbType = DbTypes[randomIndex];
var dbContext = Activator.CreateInstance(dbType) as DbContext;
return dbContext;
}
}

  这样,所有从库我们都基于策略去获取,扩展也比较方便。修改下 DbContextFactory 类的 GetReadDbContext 方法,通过策略接口来获取,代码如下:

支持一主多从的 DbContextFactory 类

  public class DbContextFactory
{
//todo:这里可以自己通过注入的方式来实现,就会更加灵活
private static readonly IReadDbStrategy ReadDbStrategy = new RandomStrategy();
public DbContext GetWriteDbContext()
{
string key = typeof(DbContextFactory).Name + "WriteDbContext";
DbContext dbContext = CallContext.GetData(key) as DbContext;
if (dbContext == null)
{
dbContext = new WriteDbContext();
CallContext.SetData(key, dbContext);
}
return dbContext;
} public DbContext GetReadDbContext()
{
string key = typeof(DbContextFactory).Name + "ReadDbContext";
DbContext dbContext = CallContext.GetData(key) as DbContext;
if (dbContext == null)
{
dbContext = ReadDbStrategy.GetDbContext();
CallContext.SetData(key, dbContext);
}
return dbContext;
}
}

  这样简单的一主多从也实现了。

参考文章

  http://www.cnblogs.com/zhaopei/p/5721789.html
  http://www.cnblogs.com/GuZhenYin/p/5482288.html

源码分享

  所有的代码提供给大家的更多的是一种思路和学习的参考,如果有什么不足的地方也欢迎大家批评指正,如果觉得对你有帮助,不要吝啬你的鼠标,帮忙点个星,点个赞吧。

  源码地址: https://github.com/qq1206676756/EF_DbHelper

EF通用数据层封装类(支持读写分离,一主多从)的更多相关文章

  1. EF 通用数据层类

    EF 通用数据层父类方法小结 转载:http://www.cnblogs.com/yq-Hua/p/4165344.html MSSql 数据库 数据层 父类 增删改查: using System; ...

  2. .Net中EF通用数据层小结

    增删改查: using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; u ...

  3. EF 通用数据层父类方法小结

    MSSql 数据库 数据层 父类 增删改查: using System;using System.Collections.Generic;using System.Data;using System. ...

  4. 实施MySQL ReplicationDriver支持读写分离

    MySQL 提供支持读写分离的驱动类: com.mysql.jdbc.ReplicationDriver 替代 com.mysql.jdbc.Driver 注意,所有参数主从统一: jdbc:mysq ...

  5. mysql读写分离--一主多从,冗余存储

    转载了https://blog.csdn.net/u013421629/article/details/78793966 https://blog.csdn.net/justdb/article/de ...

  6. Mycat实现读写分离,主备热切换

    实验环境:ubutu server 14 Master IP:172.16.34.212 Slave IP:172.16.34.34.156 Mycat server IP:172.16.34.219 ...

  7. LVS+MYCAT+读写分离+MYSQL主备同步部署手册

    LVS+MYCAT+读写分离+MYSQL主备同步部署手册 1          配置MYSQL主备同步…. 2 1.1       测试环境… 2 1.2       配置主数据库… 2 1.2.1  ...

  8. 【转载】LVS+MYCAT+读写分离+MYSQL主备同步部署手册(邢锋)

    LVS+MYCAT+读写分离+MYSQL主备同步部署手册 1          配置MYSQL主备同步…. 2 1.1       测试环境… 2 1.2       配置主数据库… 2 1.2.1  ...

  9. centos MySQL主从配置 ntsysv chkconfig setup命令 配置MySQL 主从 子shell MySQL备份 kill命令 pid文件 discuz!论坛数据库读写分离 双主搭建 mysql.history 第二十九节课

    centos  MySQL主从配置 ntsysv   chkconfig  setup命令  配置MySQL 主从 子shell  MySQL备份  kill命令  pid文件  discuz!论坛数 ...

随机推荐

  1. Lambda&Java多核编程-7-类型检查

    本篇主要介绍Lambda的类型检查机制以及周边的一些知识. 类型检查 在前面的实践中,我们发现表达式的类型能够被上下文所推断.即使同一个表达式,在不同的语境下也能够被推断成不同类型. 这几天在码一个安 ...

  2. sublime text 3 打造舒适黑色主题

    效果: 这里我使用了两个主题插件组合成的 Spacegray Afterglow Ctrl+Shift+P -> Package Control:Install Packages 分别输入Spa ...

  3. druid 搭建集群环境

    下载druid 下载地址 http://static.druid.io/artifacts/releases/druid-services-0.6.145-bin.tar.gz 解压 tar -zxv ...

  4. 关于Java中String类的hashCode方法

    首先来看一下String中hashCode方法的实现源码 public int hashCode() { int h = hash; if (h == 0 && value.lengt ...

  5. POJ 1741/1987 树的点分治

    树的点分治,主要思想是每次找子树的重心,计算经过根节点的情况数,再减去点对属于同一子树的情况. #include <iostream> #include <vector> #i ...

  6. IDEA第二章----配置git、tomcat(热部署)、database,让你的项目跑起来

    第一节:下载git客户端,整合idea 由于博主公司用的git版本管理,所以本系列都是基于git版本工具的,当然SVN与git配置类似.git同样支持安装版和解压版,支持各种操作系统,我这里下载的是W ...

  7. [ext4]010 磁盘布局 - 如何查找inode的磁盘位置

    在linux系统中,任何一个文件,都有一个inode与其对应,也就是说,在一个文件系统中,一个文件都有唯一的ino来标示他,那么在ext4系统中,ino是如何确定的哪? 当我们新创建的文件或目录时,会 ...

  8. JMS学习篇《一》ActiveMQ消息中间件的简单介绍与用法-概念篇

    原创说明:本篇博文为本人原创作品,转载请注明出处 1.何为消息中间件 消息中间件是一种在分布式应用中互相交换信息的一种技术,常见的成熟消息中间件有:RabbitMQ.SonicMQ,activeMQ. ...

  9. arcgis for javascript 之 clone()问题小计

    情景再现:      用户点击一个featurelayer的图斑,(属性信息从mysql中获取),同时高亮此地块,点击一下个地块时候,取消高亮.(请忽略跨域造成的图标错误,jetty试了好久不能跨域· ...

  10. 略过 Mysql 5.7的密码策略

    之前从mysql 5.6的时候,mysql 还没有密码策略这个东东,所以我们每个用户的密码都可以随心所欲地设置,什么123 ,abc 这些,甚至你搞个空格,那也是OK的. 而mysql.user 表里 ...