在两个不同的上下文中执行查询,但保持延迟加载可用

本文关键字:查询 执行 延迟加载 上下文 两个 | 更新日期: 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;
}

这将只对每个数据库运行一个查询。