项目使用EF的Code first作为ORM框架。
情况是这样的,我给所有的实体都定义了IsDelete属性指示是否被删除,也就是说数据库中不会有数据真正被删除,都是修改isdelete属性。这样做是为了万一数据被误删后可以使用sql语句恢复。也为实现回收站功能提供了可能。
在仓储中,我可以在查找时仅找出isdelete属性为false的数据,这没有什么问题。
但问题是EF自带的延迟加载和动态代理,它们在加载导航属性时是全部加载的,也就是不会理会我写的isdelete属性,这样会带来很严重的逻辑问题。我目前没有很好的办法解决它,下面是我的解决方案:
方案1:在实体类导航属性的访问器中写过滤代码,但实体类是不应该关心这种工作的,这是仓储实现关心的事情,并且这样做代码量不少并且重复。
方案2:将对isdelete的判断写在业务逻辑层,没有比这更恐怖的事了,少写一个就是藏的很深的BUG,多写一个就多一个重复代码。
方案3:禁用EF的代理和延迟加载,这样的话实体类中的导航属性纯属鸡肋,变成了花瓶。
方案4:更改代理的运作方式,在其读取导航属性时进行筛选。这可能是最好的办法了,但我不知道该怎么做。
请问有没有更好的办法?如果没有,方案4该如何实现?
我也在为这个问题发愁。
解决的办法,既治标又治本的当然是你的方案四,只是这个方案~~~需要对EF很熟悉,目前我还没这么熟悉。正在研究ing
等有了进展互相通知下,谢谢
@外法猎人: 应该的。
@519740105: 一直没有解决,我现在使用方案1,自定义一个代码段解决,妥协的方案。
@外法猎人: 这几天在学习ASPNET5。
导航属性的延迟加载一不小心就会成为性能杀手,建议不用导航属性的延迟加载。需要加载导航属性时,在LINQ查询时进行Include。
谢谢,但include依然无法解决我遇到的问题
//查询的时候的处理 public IQueryable<TEntity> Get(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "", bool dataHasDel = false) { IQueryable<TEntity> query = dbSet; if (dataHasDel == false) { if (filter != null) { query = query.Where(filter).Where(p => p.IsDel == false); } else { query = query.Where(p => p.IsDel == false); } } else { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy != null) { return orderBy(query).AsNoTracking(); } else { return query.AsNoTracking(); } }
首先请废弃include include 会打破原子性
/// <summary> /// 删除 /// </summary> /// <param name="entityToDelete"></param> public virtual void Delete(TEntity entityToDelete) { GLRISCRM.Entity.IDelable iDelable = entityToDelete as GLRISCRM.Entity.IDelable; GLRISCRM.Entity.ILogicDelable iLogicDelable = entityToDelete as GLRISCRM.Entity.ILogicDelable; // 删除没启用 if (iDelable != null && iDelable.EnableDel == false) { throw (new Exception("当前数据不可删除!")); } // 逻辑删除 if (iLogicDelable != null) { iLogicDelable.IsDel = true; _Update(entityToDelete); return; } // 物理删除 if (context.Entry(entityToDelete).State == EntityState.Detached) { dbSet.Attach(entityToDelete); } dbSet.Remove(entityToDelete); }
但代理类对导航属性的延迟加载似乎无法在这里控制。
还是非常感谢,仓储写的很好。
@外法猎人:
我都说了 请废弃食用include 也就是说废弃 导航属性的外键关联
我的项目 查询只有2种情况
一种就是单表 一种就是多表
单表就是我上面写的
多表就是sql语句 说实话 多表的情况 ef 并没有优势
var orders = _dbContext.SalesOrders.Where(a => a.Id == 888).select(a => new {SalesOrder = a, SalesOrderItems = a.OrderItems.Where(b => b.IsDeleted == null || b.IsDeleted == false)}).ToList();