我试图过滤掉我在控制器中传递给我的视图的项目,虽然我有一个解决方案,但我很想知道正确的方法,因为我&#39 ;我确定它不是最好的。
我有一份工作订单表和每个工单的相关任务。在我的网站中,我不删除行,但使用WhenDeleted日期标记它们,因此在填充我的工作订单视图(单个,而不是列表)时,我想要一个带有子任务的WorkOrder实体,但我想过滤掉标记为的任务删除。
这是我的代码,它有效,但似乎我应该能够通过初始调用来保存旅行。
if (db.tblWorkOrder.Any(x => x.ID == id))
{
tblWorkOrder model = db.tblWorkOrder.First(x => x.ID == id);
model.tblTask = model.tblTask.Where(x => x.WhenDeleted == null).ToList()
}
解决方案: 这里标出的重复问题有答案。还有其他人在这里有更强大的google-fu,我认为这有助于了解我试图做的事情的术语。对于其他首先发现这个问题的人来说,这是其中的答案:
db.Configuration.ProxyCreationEnabled = false; // disable lazy-loading
if (db.tblWorkOrder.Any(x => x.ID == id))
{
tblWorkOrder model = db.tblWorkOrder.Where(b => b.ID == id)
.Select(b => new
{
b,
tblTask = b.tblTask.Where(p => p.WhenDeleted == null).OrderBy(x => x.Position)
})
.AsEnumerable()
.Select(x => x.b)
.First();
}
答案 0 :(得分:0)
这应该可以保存到db的行程,因为查询仅在命中SingleOrDefault()
时执行。
tblWorkOrder model = (from p in db.tblWorkOrder
where p.ID == id
select new tblWorkOrder
{
// other tblWorkOrder properties
tblTask = from q in p.Tasks where q.WhenDeleted == null select q
}).SingleOrDefault();
答案 1 :(得分:0)
不幸的是,一旦将父实体读入内存,EF就不支持显式加载部分子实体集。读取tblTask属性后,所有相关任务都将读入内存。一种选择是使用您想要的数据将匿名对象读入内存:
db.tblWorkOrder.Where(x => x.ID == id)
.Select(x => new { x.ID, ..., tasks = x.tblTask.Where(t => t.WhenDeleted == null) })
.First();
另一种方法是对dbContext使用两个显式查询:
tblWorkOrder model = db.tblWorkOrder.First(x => x.ID == id);
model.tblWorkOrder = db.tblTask.Where(t => t.WhenDeleted == null).ToList();
然而,这种风险(与您的原始方法一样)是通过修改"活跃"实体对象(在这种情况下是模型),在上下文中调用SaveChanges()现在会尝试更新数据库,这不是你想要的。
另一种方法是为任务表创建一个视图,过滤掉已删除的任务,并为该视图创建单独的EF模型和导航属性。
一般来说,我发现尝试直接使用EF实体类型作为视图模型并不能很好地工作,因为实体类型与数据库结构紧密相关,而视图模型经常需要分歧(就像这里的情况一样)。我认为最好的长期方法是为视图模型创建单独的类,只使用实体查询数据库并填充模型。
答案 2 :(得分:0)
除了其他答案之外,另一种方法是为您的实体创建构造函数:
public TblWorkOrder(TblWorkOrder tbl, IEnumerable<Task> tasks)
{
this = tbl;
this.tbltask = tasks;
}
然后您可以在Select
中使用该构造函数:
if (db.tblWorkOrder.Any(x => x.ID == id))
{
tblWorkOrder model = db.tblWorkOrder.Where(x => x.ID == id).AsEnumerable()
.Select(x => new TblWorkOrder(x,x.tblTask.Where(x => x.WhenDeleted == null))).First();
}
由于对AsEnumerable()
的调用,结果集将在内存中处理,这使您可以在select中使用构造函数。这不会是性能问题,因为你只会获取1个实体(就像你现在这样)。
此外,由于延迟加载,最好Include()
任务保存另一次数据库之旅。
答案 3 :(得分:0)
有多种方法可以实现这一目标,其中一种方法可以限制您对数据库的干扰:
var result = db.tblTask.Where(x => x.WhenDeleted == null && x.WorkOrderId = id).GroupBy(x => x.WorkOrder).FirstOrDefault();
if(result != null){
return new {WorkOrder = result.Key, Tasks = result.ToList()};
}
另一个我认为更干净的approuch将使用EntityFramework.Filters,您可以在上下文中配置这样的过滤器
DbInterception.Add(new FilterInterceptor());
modelBuilder.Conventions.Add(FilterConvention.Create<tblTask>("OnlyActive", (task) => task.WhenDeleted == null));
并在您的查询之前应用它:
db.EnableFilter("OnlyActive");
tblWorkOrder model = db.tblWorkOrder.First(x => x.ID == id);
db.DisableFilter("OnlyActive");