有人可以解释为什么这两个linq查询返回不同的结果?

时间:2010-02-15 15:01:36

标签: c# .net sql linq linq-to-entities

我有两个linq(到EF4)查询,返回不同的结果。第一个查询包含正确的结果,但未正确格式化/投影。

第二个查询是我想要的,但它缺少一些数据。

模式

alt text http://img220.imageshack.us/img220/9678/schema.png

查询1

var xxxx = (from cp in _connectedClientRepository
            .GetConnectedClients(new[] { "LogEntry", "LogEntry.GameFile" })
            .AsExpandable()
            .Where(predicate)
            select cp)
    .ToList();

alt text http://img231.imageshack.us/img231/6541/image2ys.png

注意属性GameFile。它 null。这很棒:)请注意linq查询?我急切加载一个LogEntry,然后急切地加载GameFile(对于每个急切加载的LogEntry)。

这就是我所追求的 - >对于急切加载的每个LogEntry,请急切加载GameFile。但这个预测结果是错误的......

好的..下一个......

查询2

var yyy = (from cp in _connectedClientRepository
            .GetConnectedClients(new[] { "LogEntry", "LogEntry.GameFile" })
            .AsExpandable()
            .Where(predicate)
        select cp.LogEntry)
    .ToList();

alt text http://img24.imageshack.us/img24/4417/image1tu.png

注意:上面的图片中有一个拼写错误...请注意包含关联的类型代码是正确的(即。LogEntry.GameFile),而图片上的拼写错误。

现在正确投影 - >所有LogEntries结果。但请注意GameFile属性现在是如何null?我不确定为什么:(我以为我正确地渴望加载正确的链。所以这是正确的投影,但结果不正确。

强制性存储库代码。

public IQueryable<ConnectedClient> GetConnectedClients(
    string[] includeAssociations)
{
    return Context.ConnectedClients
        .IncludeAssociations(includeAssociations)
        .AsQueryable();
}

public static class Extensions
{
    public static IQueryable<T> IncludeAssociation<T>(
        this IQueryable<T> source, string includeAssociation)
    {
        if (!string.IsNullOrEmpty(includeAssociation))
        {
            var objectQuery = source as ObjectQuery<T>;

            if (objectQuery != null)
            {
                return objectQuery.Include(includeAssociation);
            }
        }

        return source;
    }

    public static IQueryable<T> IncludeAssociations<T>(
        this IQueryable<T> source, params string[] includeAssociations)
    {
        if (includeAssociations != null)
        {
            foreach (string association in includeAssociations)
            {
                source = source.IncludeAssociation(association);
            }
        }

        return source;
    }
}

更新

  • 1:修复了代码示例中注意到的一些拼写错误。
  • 2:添加了存储库代码,以帮助任何困惑的人。

3 个答案:

答案 0 :(得分:2)

我怀疑Craig Stuntz的建议可能会有效,但如果没有,那么以下内容肯定会有效:

 var xxxx =_connectedClientRepository
        .GetConnectedClients(new[] { "LogEntry", "LogEntry.GameFile" })
        .AsExpandable()
        .Where(predicate)
        .ToList() // execute query
        .Select(cp => cp.LogEntry); // use linq-to-objects to project the result

答案 1 :(得分:1)

Include()适用于查询结果,而不是中间查询。您可以在此帖子中详细了解Include()。因此,一种解决方案是将Include()应用于整个查询,如下所示:

var q = ((from cp in _connectedClientRepository.GetConnectedClients()
                                               .AsExpandable()
                                               .Where(predicate) 
          select cp.LogEntry) 
         as ObjectQuery).Include("GameFile").ToList();

那可能工作,但它很难看。我们可以做得更好吗?

我可以想办法解决这个问题。大多数情况下,它取决于您是否确实需要返回的实体类型。我不能说如果没有看到你的其余代码就是这种情况。通常,当您要更新(或以其他方式修改)它们时,您需要返回实体类型。如果您选择显示或计算目的,返回POCO而不是实体类型通常是更好的策略。您可以使用projection执行此操作,当然它适用于EF 1.在这种情况下,您将更改存储库方法以返回POCO类型:

 public IQueryable<ClientInfo> GetConnectedClients()
 {
      return from cp in _context.Clients
             where // ...
             select new ClientInfo
             {
                 Id = cp.Id,
                 ClientName = cp.ClientName,
                 LogEntry = new LogEntryInfo
                            {
                                LogEntryId = cp.LogEntry.LogEntryId,
                                GameFile = new GameFileInfo
                                           {
                                               GameFileId = cp.LogEntry.GameFile.GameFileId,
                                               // etc.
                                           },
                                // etc.
                            },
                  // etc.
             };
 }

请注意,使用投影时,没有急切加载,没有延迟加载,也没有显式加载。只有你的意图,表达为查询。即使您在存储库外进一步撰写查询,LINQ提供程序也会找出您需要的内容,

另一方面,您可能需要返回实体类型而不是POCO,因为您打算更新它们。在这种情况下,我会像Tomas建议的那样为LogEntries编写一个单独的存储库方法。但是如果我打算更新它们,我只会这样做,我可能会把它写成更新方法,而不是“Get”方法。

答案 2 :(得分:-1)

您似乎还需要一个存储库方法,它将为您执行此操作; _connectedClientsRepository.GetLogEntriesOfConnectedClients()