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

时间:2016-08-25 20:47:46

标签: c# .net entity-framework linq entity-framework-6

我有以下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是一个非常大的表,并且在框架爆炸之前我已经达到了超过1.5GB的已用内存并抛出{{1}例外。

在执行类似的操作时,有没有办法保持延迟加载功能?

2 个答案:

答案 0 :(得分:0)

为了继续使用返回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();

这是更好的

答案 1 :(得分:0)

正如评论中所解释的那样,你不能真正在两个不同的数据库中运行单个查询,至少在没有设置一些帮助构造的情况下(它们将存在于任一数据库中,实际上谁知道这是否会提高性能)全部)。

但是,这并不意味着我们根本无法改进您的查询。如果我们不能依赖数据库引擎来执行查询,我们可以自己做。在这种情况下,您所做的只是对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;
}

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