Отключенные ссылки на объекты с отложенной загрузкой и активной загрузкой не работают должным образом - PullRequest
4 голосов
/ 01 октября 2011

Я работал с WCF RIA Services и Silverlight и добился определенного успеха в предоставлении сервиса, который обслуживает данные, взятые из модели данных объекта ADO.NET, смоделированной из существующей базы данных SQL Server 2008 Express.База данных определяет множество связей между таблицами, которые я надеюсь использовать на стороне клиента для привязки данных.

Все шло гладко, пока я не попытался выполнить следующий метод обслуживания:

public IQueryable<Timeline> GetHighlights() {

    var self = from x in Database.Timelines
                where User.Id == x.UserId || User.Id == x.SenderId
                select x;

    var friends = from x in Database.FriendItems
                    where User.Id == x.UserId
                    from y in Database.Timelines
                    where x.FriendId == y.UserId || x.FriendId == y.SenderId
                    select y;

    return self.Concat(friends).OrderByDescending(s => s.Id);
}

Примечание: «Пользователь» - это внутреннее свойство класса, которое выбирает текущего пользователя, прошедшего проверку подлинности, а база данных просто оборачивает свойство ObjectContext (для простоты).

Объект «Временная шкала» содержит 2 свойства навигации: «Пользователь» и«Отправитель», связанный с сущностью «SilverfishUser».Когда я перебираю результаты запроса «self», я вижу, что ранее упомянутые свойства были заполнены текущим пользователем (что правильно).Однако, когда я повторяю результаты запроса «друзей», оба свойства равны нулю (перед сериализацией для клиента).

Я попытался установить:

this.ContextOptions.LazyLoadingEnabled = false;
//and
this.ContextOptions.ProxyCreationEnabled = false;

И у меня естьтакже пытался загружать ссылки с помощью метода запросов «Включить» (с отложенной загрузкой, оба включены и отключены), но безрезультатно.

Единственный способ, которым я успешно заполнил свойства «Пользователь» и «Отправитель» сущности временной шкалы, -следующее утверждение:

friends.ForEach(s => { 
    if (!s.UserReference.IsLoaded) s.UserReference.Load();
    if (!s.SenderReference.IsLoaded) s.SenderReference.Load();
});

Насколько я понимаю, операция «Загрузка» приводит к выполнению отдельного запроса к базе данных.Как видите, это представляет потенциально неэффективную ситуацию, когда у пользователя много друзей с большим количеством сообщений на временной шкале.Точную ситуацию я пытаюсь избежать, отключив ленивую загрузку.Я хочу вернуть клиенту полностью загруженную сущность, с которой можно связать минимальное количество запросов.

Я уже преодолел одну проблему, когда относительные свойства не были сериализованы для клиента путем применения [Включить] атрибут в определения свойств метаданных, сгенерированные мастером службы домена.Эта проблема кажется немного более сложной, решения, которые я попробовал, были широко изложены другими и должны решить мою проблему в теории, но они этого не делают.Опять же, единственный способ успешно заполнить сущность - это явно загрузить ссылки, используя сгенерированное свойство EntityReference <>, созданное для связанного свойства.

Любая помощь, опыт или информация по этой проблемес благодарностью.

[EDIT] Обновление некоторых моих исследований, когда я выполняю запрос, подобный следующему:

    var friends = Database.FriendItems
                .Include("Friend.Timeline")
                .Where(s => User.Id == s.UserId);

и доступ к свойствам навигации ("friends.First ().Friend.Timeline.First (). User ") значение не равно нулю.Только когда я выбираю временные шкалы в новую коллекцию, добавляя что-то вроде:

.SelectMany(s => s.Friend.Timeline);

, свойства навигации больше не имеют никакого значения.Теперь это только предположение, но я могу только предположить, что оно проецирует значения свойств в новый экземпляр объекта, поэтому он не перезаполняет эти свойства в попытке избежать циклических ссылок?Во всяком случае, это чертовски трудно решить.Надеюсь, есть кто-то, кто знает об этом больше, чем я.

1 Ответ

0 голосов
/ 01 октября 2011

Ну, мне удалось найти обходной путь, хотя и не очень элегантный.Я опубликую его как ответ, чтобы люди могли на него взглянуть, но я оставлю вопрос открытым и, если возможно, опишу более подходящее решение.

Вот мое исправление:

public IQueryable<Timeline> GetHighlights() {

    var self = from x in Database.Timelines
               where User.Id == x.UserId || User.Id == x.SenderId
               select x;

    var friends = from x in Database.FriendItems.Include("Friend.Timeline")
                  where (User.Id == x.UserId)
                  select x.Friend.Timeline;

    List<Timeline> highlights = new List<Timeline>();
    highlights.AddRange(self);

    friends.ForEach(x => x.ForEach(y => highlights.Add(y)));
    return highlights.AsQueryable().OrderByDescending(s => s.Id);
}

Я думаю причина, по которой это работает, заключается в том, что, создавая новую коллекцию вручную, я предотвращаю проецирование объектов в новые объекты, сохраняя при этом загруженные реляционные свойства.Опять же, это всего лишь предположение, но предположение следует за довольно хорошим шаблоном.

...