在两个不同的上下文中执行查询,但保持延迟加载可用
本文关键字:查询 执行 延迟加载 上下文 两个 | 更新日期: 2023-09-27 18:03:57
我有以下LINQ查询:
//two different contexts, databases, tables...
NoteSet = lmCtx.LMNotes.AsEnumerable();
EmpSet = tessCtx.Employees.AsEnumerable();
var lmAccountNotes = (from lmnote in NoteSet
join createdby in EmpSet on lmnote.lnt_createdById equals createdby.EmployeeID
join modifiedby in EmpSet on lmnote.lnt_modifiedById equals modifiedby.EmployeeID
where lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1
select new NoteInfo { Note = lmnote, CreatedBy = createdby, ModifiedBy = modifiedby }).ToList();
这适用于小表上的查询,但是NoteSet
是一个相当大的表,在框架爆炸并抛出OutOfMemory
异常之前,我的进程已经使用了超过1.5GB的内存。
有没有办法在执行这样的操作时保持延迟加载的特性?
为了保持查询返回NoteInfo
对象,我将其更改为:
//LMNotes is the actual huge database...
var m = lmCtx.LMNotes.Where(x => x.lnt_recordId == 5566).ToList();
var lmAccountNotes = (from lmnote in m
join createdby in EmpSet on lmnote.lnt_createdById equals createdby.EmployeeID
join modifiedby in EmpSet on lmnote.lnt_modifiedById equals modifiedby.EmployeeID
where lmnote.lnt_recordId == 566 && lmnote.lnt_tableId == 1
select new NoteInfo { Note = lmnote, CreatedBy = createdby, ModifiedBy = modifiedby }).ToList();
这样比较好
正如在评论中所解释的那样,您不能真正跨两个不同的数据库运行单个查询,至少在没有设置一些帮助构造的情况下是不能的(这些帮助构造将存在于任何一个数据库上,实际上谁知道这是否会提高性能)。
然而,这并不意味着我们不能改善你的查询。如果我们不能依赖数据库引擎来执行查询,我们可以自己执行。在本例中,您所做的基本上只是对LMNotes
实体进行查询,然后加入Employees
集合中的雇员。
所以一个朴素的解决方案可能是这样的:
var notes = lmCtx.LMNotes
.Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1)
.Select(lmnote =>
{
return new NoteInfo
{
Note = lmnote,
CreatedBy = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == lmnote.lnt_createdById),
ModifiedBy = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == lmnote.lnt_modifiedById)
};
})
.ToList();
当然,虽然这在LMNotes
上运行单个查询,但对于结果中的每个注释仍然运行两个单独的查询。因此,它并不比EF在上面所做的更好。
我们可以做的是添加一些查找。我怀疑员工的集合是有限的,所以只获取每个员工一次是有意义的。像这样:
private Dictionary<int, Employee> employees = new Dictionary<int, Employee>();
private Employee GetEmployee(int employeeId)
{
Employee employee;
if (!employees.TryGetValue(employeeId, out employee))
{
employee = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == employeeId);
employees[employeeId] = employee;
}
return employee;
}
public List<NoteInfo> GetNotes()
{
return lmCtx.LMNotes
.Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1)
.Select(lmnote =>
{
return new NoteInfo
{
Note = lmnote,
CreatedBy = GetEmployee(lmnote.lnt_createdById),
ModifiedBy = GetEmployee(lmnote.lnt_modifiedById)
};
})
.ToList();
}
这将只查找每个雇员一次,然后缓存雇员对象。
或者,您也可以在这里进行第二次传递,并在第一次阅读笔记后立即获取所有员工。像这样:
public List<NoteInfo> GetNotes()
{
var employeeIds = new HashSet<int>();
var notes = lmCtx.LMNotes
.Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1)
.Select(lmnote =>
{
// remember the ids for later
employeeIds.Add(lmnote.lnt_createdById);
employeeIds.Add(lmnote.lnt_modifiedById);
return new NoteInfo
{
Note = lmnote,
CreatedBy = null,
ModifiedBy = null
};
})
.ToList();
var employees = tessCtx.Employees
.Where(e => employeeIds.Contains(e.EmployeeId))
.ToList()
.ToDictionary(e => e.EmployeeId);
foreach (var noteInfo in notes)
{
noteInfo.CreatedBy = employees[noteInfo.Note.lnt_createdById];
noteInfo.ModifiedBy = employees[noteInfo.Note.lnt_modifiedById];
}
return notes;
}
这将只对每个数据库运行一个查询。