Может кто-нибудь объяснить, почему эти два запроса linq дают разные результаты? - PullRequest
11 голосов
/ 15 февраля 2010

У меня есть два запроса linq (к EF4), которые возвращают разные результаты. Первый запрос содержит правильных результатов, но не отформатирован / спроецирован правильно.

второй запрос - это то, что я хочу, но в нем отсутствуют некоторые данные.

Схема

альтернативный текст 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();

альтернативный текст http://img231.imageshack.us/img231/6541/image2ys.png

Обратите внимание на свойство GameFile. Это не ноль. Это здорово :) Обратите внимание на запрос linq? Я жду загрузки a 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

ПРИМЕЧАНИЕ : на изображении выше есть опечатка ... пожалуйста, обратите внимание, что типизированный код включаемых ассоциаций является правильным (т. Е. 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 ]

2 голосов
/ 16 февраля 2010

Я подозреваю, что предложение Крейга Стунца может сработать, но если оно не сработает, то, безусловно, должно сработать:

 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 голос
/ 16 февраля 2010

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, потому что вы намереваетесь их обновить. В этом случае я бы написал отдельный метод хранилища для LogEntries, как предложил Томас. Но я сделал бы это, только если бы намеревался обновить их, и я мог бы написать это как метод обновления, а не как метод «Get».

0 голосов
/ 15 февраля 2010

Кажется, вам нужен еще один метод хранилища, который сделает это за вас; _connectedClientsRepository.GetLogEntriesOfConnectedClients().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...