EF 일반 데이터 계층 캡슐화 클래스 예제 요약 자세한 설명
ORM에 대한 간략한 이야기
4년 전 학교에서 Ling to Sql을 처음 접했을 때, SQL 문을 직접 작성하지 않는 것이 얼마나 편리한지 즉시 깨달았습니다. EF, Dapper, Hibernate, ServiceStack.OrmLiteetc와 같은 많은 ORM 프레임워크와 접촉했습니다. 물론 각 ORM에는 고유한 장점과 단점이 있습니다. 또한 정원에는 SqlSugar, Chloe.ORM, CYQ.Data 등과 같이 자신이 작성한 ORM을 오픈 소스로 제공한 훌륭한 사람들이 많이 있습니다. 이러한 오픈 소스 ORM의 사용에 대해서는 이야기하지 않겠습니다. 적어도 오픈 소스의 정신 측면에서는 칭찬할 만하다고 생각합니다. 저는 또한 이 위대한 거장들의 소스 코드를 다운로드하여 연구했습니다.
모든 ORM의 궁극적인 포인트는 프로그래머의 반복적인 작업을 줄여주는 편리성입니다. 물론 아직까지도 수기 SQL을 사용하여 전체 프로젝트를 진행하기에는 너무 바쁜 회사도 있다고 생각합니다. SQL을 손으로 쓰는 것을 추천하지 않는 것은 아니지만, 개인적으로 가장 기본적인 추가, 삭제, 수정, 쿼리 등을 모두 손으로 작성한다면 테스트는 능력이 아니라 인내라고 생각합니다. 어떤 사람들은 손으로 쓴 SQL 방식이 강력한 제어성과 높은 성능을 가지고 있다고 말합니다. 제가 말하고 싶은 것은 ORM도 이를 어떻게 사용하느냐에 달려 있다는 것입니다.
ORM의 장점은 매우 명확하고 개발하기 편리하지만 아마도 이 장점 때문에 많은 게으른 프로그래머들이 점차 SQL 문 작성 방법을 잊어버리게 될 것입니다. 저는 EF를 사용하고 SQL을 작성하는 많은 프로그래머 친구들을 만났습니다. 저는 더 이상 뷰나 저장 프로시저를 사용하고 싶지 않습니다. 개인적으로는 여전히 손으로 작성한 SQL이 필요하다고 생각합니다. 그렇지 않으면 어느 날 다른 사람의 프로그램에 "exec xxxx"라고 쓰여 있는 것을 보면 문득 "아, 어디서 본 것 같은데..."라는 생각이 들 것이다. 그래서 제가 말씀드리고 싶은 것은 "아직 행동할 때가 되면 행동을 취해야 한다"는 것입니다.
Entity Framework에 대한 간략한 설명
Entity Framework는 Microsoft의 ORM 프레임워크로, Entity Framework가 지속적으로 개선되고 강화되면서 다른 ORM에 비해 사용 비율이 여전히 높다고 생각합니다. 제가 현재 가장 많이 사용하는 것은 EF와 Dapper입니다. 실제로 개발 과정에서 EF를 사용하는 것이 훨씬 더 편리할 것입니다. 결국 EF는 오랜 세월을 거쳐 성숙도와 성능도 많이 향상되었습니다. 엔티티 프레임워크 확장 등. 그리고 .net 개발자로서 이 프로젝트도 매우 다양하고 정보도 많으며 이 분야에 대한 Microsoft의 업데이트도 매우 강력합니다. 하지만 방금 출시된 EF Core에도 몇 가지 함정이 있습니다. 아직은 초기 단계이므로 앞으로는 점점 더 나아질 것이라고 믿습니다.
Entity Framework는 코드 우선, DB 우선, 모델 우선의 세 가지 개발 모드를 제공합니다. 현재 가장 일반적으로 사용되는 것은 코드 우선입니다. 세 가지 모드의 간단한 사용법과 차이점은 이 글을 참고하세요.
몇몇 친구들이 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 > 0 ? $"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 > 0 ?$"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 <= 0) {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 - 1) * 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 之前一定要 OrderByvar data = Db.Set<T>().OrderBy(ordering);if (whereLambda != null) { data = data.Where(whereLambda); } rows = data.Count();return data.PageBy((pageIndex - 1) * 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}
扩展类,实现读写分离
上面的通用类是比较基础简单通用的,适合于单库读写操作。对于EF实现读写分离,之前网上找过类似的参考文章,很多人文章都是使用 DbCommandInterceptor拦截器 来实现,具体的做法是通过拦截到sql语句,然后根据具体条件去判断是走主库还是从库。这种做法不是不行,只是个人感觉不是很好扩展,而且要在拦截器里面做限制判断。
其实说白了EF本身就是一个读写分离的orm。用过EF的人知道,EF提供访问数据库的是 DbContext 这个对象,所以想实现读写分离的就很简单了,只要在程序中使用两个不同的DbContext对象,一个负责读,一个负责写就好了。
所以在上面提供的通用封装类中稍微做下修改,修改如下DbContextFactory中获取DbContext的方法,实现一个读的DbContext和一个写的DbContext对象的获取。
这里要注意下,对于读的DbContext来说,要做下设置
1.使用 Database.SetInitializer(new NullDatabaseInitializer
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 > 0 ? $"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 > 0 ?$"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 <= 0) {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 - 1) * 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 之前一定要 OrderByvar data = SlaveDb.Set<T>().OrderBy(ordering);if (whereLambda != null) { data = data.Where(whereLambda); } rows = data.Count();return data.PageBy((pageIndex - 1) * 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}
这样简单的读写分离就实现了,实现逻辑也比较清晰,方便扩展。
进一步改造,实现多从库读取
一般做读写分离,都会做一主多从,特别对读取量比较大的项目,这样多库读取就能减轻读库的压力。所以对于上面的方法,做下改造。
上面可以看到,主库和从库都是通过 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(0, 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; } }
这样简单的一主多从也实现了。
参考文章
源码分享
所有的代码提供给大家的更多的是一种思路和学习的参考,如果有什么不足的地方也欢迎大家批评指正,如果觉得对你有帮助,不要吝啬你的鼠标,帮忙点个星,点个赞吧。
위 내용은 EF 일반 데이터 계층 캡슐화 클래스 예제 요약 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











DDREASE는 하드 드라이브, SSD, RAM 디스크, CD, DVD 및 USB 저장 장치와 같은 파일 또는 블록 장치에서 데이터를 복구하기 위한 도구입니다. 한 블록 장치에서 다른 블록 장치로 데이터를 복사하여 손상된 데이터 블록은 남겨두고 양호한 데이터 블록만 이동합니다. ddreasue는 복구 작업 중에 간섭이 필요하지 않으므로 완전히 자동화된 강력한 복구 도구입니다. 게다가 ddasue 맵 파일 덕분에 언제든지 중지하고 다시 시작할 수 있습니다. DDREASE의 다른 주요 기능은 다음과 같습니다. 복구된 데이터를 덮어쓰지 않지만 반복 복구 시 공백을 채웁니다. 그러나 도구에 명시적으로 지시된 경우에는 잘릴 수 있습니다. 여러 파일이나 블록의 데이터를 단일 파일로 복구

0. 이 글은 어떤 내용을 담고 있나요? 우리는 다재다능하고 빠른 최첨단 생성 단안 깊이 추정 모델인 DepthFM을 제안합니다. DepthFM은 전통적인 깊이 추정 작업 외에도 깊이 인페인팅과 같은 다운스트림 작업에서 최첨단 기능을 보여줍니다. DepthFM은 효율적이며 몇 가지 추론 단계 내에서 깊이 맵을 합성할 수 있습니다. 이 작품을 함께 읽어보아요~ 1. 논문 정보 제목: DepthFM: FastMoncularDepthEstimationwithFlowMatching 저자: MingGui, JohannesS.Fischer, UlrichPrestel, PingchuanMa, Dmytr

Google이 추진하는 JAX의 성능은 최근 벤치마크 테스트에서 Pytorch와 TensorFlow를 능가하여 7개 지표에서 1위를 차지했습니다. 그리고 JAX 성능이 가장 좋은 TPU에서는 테스트가 이루어지지 않았습니다. 개발자들 사이에서는 여전히 Tensorflow보다 Pytorch가 더 인기가 있습니다. 그러나 앞으로는 더 큰 모델이 JAX 플랫폼을 기반으로 훈련되고 실행될 것입니다. 모델 최근 Keras 팀은 기본 PyTorch 구현을 사용하여 세 가지 백엔드(TensorFlow, JAX, PyTorch)와 TensorFlow를 사용하는 Keras2를 벤치마킹했습니다. 첫째, 그들은 주류 세트를 선택합니다.

지연이 발생하고 iPhone의 모바일 데이터 연결 속도가 느립니까? 일반적으로 휴대폰의 셀룰러 인터넷 강도는 지역, 셀룰러 네트워크 유형, 로밍 유형 등과 같은 여러 요소에 따라 달라집니다. 더 빠르고 안정적인 셀룰러 인터넷 연결을 얻기 위해 할 수 있는 일이 몇 가지 있습니다. 수정 1 – iPhone 강제 다시 시작 때로는 장치를 강제로 다시 시작하면 셀룰러 연결을 포함한 많은 항목이 재설정됩니다. 1단계 – 볼륨 높이기 키를 한 번 눌렀다가 놓습니다. 그런 다음 볼륨 작게 키를 눌렀다가 다시 놓습니다. 2단계 - 프로세스의 다음 부분은 오른쪽에 있는 버튼을 누르는 것입니다. iPhone이 다시 시작되도록 하세요. 셀룰러 데이터를 활성화하고 네트워크 속도를 확인하세요. 다시 확인하세요 수정 2 – 데이터 모드 변경 5G는 더 나은 네트워크 속도를 제공하지만 신호가 약할 때 더 잘 작동합니다

세상은 미친 듯이 큰 모델을 만들고 있습니다. 인터넷의 데이터만으로는 충분하지 않습니다. 훈련 모델은 '헝거게임'처럼 생겼고, 전 세계 AI 연구자들은 이러한 데이터를 탐식하는 사람들에게 어떻게 먹이를 줄지 고민하고 있습니다. 이 문제는 다중 모드 작업에서 특히 두드러집니다. 아무것도 할 수 없던 시기에, 중국 인민대학교 학과의 스타트업 팀은 자체 새로운 모델을 사용하여 중국 최초로 '모델 생성 데이터 피드 자체'를 현실화했습니다. 또한 이해 측면과 생성 측면의 두 가지 접근 방식으로 양측 모두 고품질의 다중 모드 새로운 데이터를 생성하고 모델 자체에 데이터 피드백을 제공할 수 있습니다. 모델이란 무엇입니까? Awaker 1.0은 중관촌 포럼에 최근 등장한 대형 멀티모달 모델입니다. 팀은 누구입니까? 소폰 엔진. 런민대학교 힐하우스 인공지능대학원 박사과정 학생인 Gao Yizhao가 설립했습니다.

테슬라의 로봇 옵티머스(Optimus)의 최신 영상이 공개됐는데, 이미 공장에서 작동이 가능한 상태다. 정상 속도에서는 배터리(테슬라의 4680 배터리)를 다음과 같이 분류합니다. 공식은 또한 20배 속도로 보이는 모습을 공개했습니다. 작은 "워크스테이션"에서 따고 따고 따고 : 이번에 출시됩니다. 영상에는 옵티머스가 공장에서 이 작업을 전 과정에 걸쳐 사람의 개입 없이 완전히 자율적으로 완료하는 모습이 담겨 있습니다. 그리고 Optimus의 관점에서 보면 자동 오류 수정에 중점을 두고 구부러진 배터리를 집어 넣을 수도 있습니다. NVIDIA 과학자 Jim Fan은 Optimus의 손에 대해 높은 평가를 했습니다. Optimus의 손은 세계의 다섯 손가락 로봇 중 하나입니다. 가장 능숙합니다. 손은 촉각적일 뿐만 아니라

이번 주, 오픈AI(OpenAI), 마이크로소프트(Microsoft), 베조스(Bezos), 엔비디아(Nvidia)가 투자한 로봇 회사인 FigureAI는 약 7억 달러의 자금 조달을 받았으며 내년 내에 독립적으로 걸을 수 있는 휴머노이드 로봇을 개발할 계획이라고 발표했습니다. 그리고 Tesla의 Optimus Prime은 계속해서 좋은 소식을 받았습니다. 올해가 휴머노이드 로봇이 폭발하는 해가 될 것이라는 데는 누구도 의심하지 않는다. 캐나다에 본사를 둔 로봇 회사인 SanctuaryAI는 최근 새로운 휴머노이드 로봇인 Phoenix를 출시했습니다. 관계자들은 이 로봇이 인간과 같은 속도로 자율적으로 많은 작업을 완료할 수 있다고 주장한다. 인간의 속도로 자동으로 작업을 완료할 수 있는 세계 최초의 로봇인 Pheonix는 각 물체를 부드럽게 잡고 움직이며 우아하게 왼쪽과 오른쪽에 배치할 수 있습니다. 자동으로 물체를 식별할 수 있습니다.

다중 모드 문서 이해 기능을 위한 새로운 SOTA! Alibaba mPLUG 팀은 최신 오픈 소스 작업인 mPLUG-DocOwl1.5를 출시했습니다. 이 작품은 고해상도 이미지 텍스트 인식, 일반 문서 구조 이해, 지침 따르기, 외부 지식 도입이라는 4가지 주요 과제를 해결하기 위한 일련의 솔루션을 제안했습니다. 더 이상 고민하지 말고 먼저 효과를 살펴보겠습니다. 복잡한 구조의 차트도 한 번의 클릭으로 인식하고 마크다운 형식으로 변환 가능: 다양한 스타일의 차트 사용 가능: 보다 자세한 텍스트 인식 및 위치 지정도 쉽게 처리 가능: 문서 이해에 대한 자세한 설명도 제공 가능: 아시다시피, " 문서 이해"는 현재 대규모 언어 모델 구현을 위한 중요한 시나리오입니다. 시장에는 문서 읽기를 지원하는 많은 제품이 있습니다. 그 중 일부는 주로 텍스트 인식을 위해 OCR 시스템을 사용하고 텍스트 처리를 위해 LLM을 사용합니다.
