Ссылка EF теряется, когда Web API возвращает IQueryable


Я искал в течение достаточно долгого времени теперь на эту проблему.

Вот в чем дело. Я создаю веб-сайт, который обращается к веб-API, чтобы получить его данные. Мой веб-API использует библиотеку, работающую с шаблоном репозитория. Моя модель базы данных (EF Model-first) была собрана в библиотеке. В этой модели у меня есть пропуск базового класса. Затем у меня есть два производных класса, CustomerCard : Pass и Voucher : Pass. моя модель от EF Designer

У меня есть метод, чтобы получить все Карточки клиентов.

public IQueryable<CustomerCard> GetAllPasses() {
        IList<CustomerCard> allCards = new List<CustomerCard>();

        var c_cards = context.Passes;
        foreach (var c_card in c_cards) {
            if (c_card is CustomerCard) {
                allCards.Add((CustomerCard)c_card);
            }
        }
        return allCards.AsQueryable<CustomerCard>();
    }

В моем ApiController я использую этот метод для получения пропусков и возврата их на веб-сайт, например:

    [HttpGet]
    [Queryable]
    public IQueryable<CustomerCard> GetAllPasses(string version) {
        return passRepo.GetAllPasses().AsQueryable();
    }

Мой Web API возвращает формат JSON. Это мой конфиг для сохранения ссылок и прочего:

        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling =
            Newtonsoft.Json.PreserveReferencesHandling.Objects;
        json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
        config.Formatters.Remove(config.Formatters.XmlFormatter);

Я использую IQueryable, потому что хочу иметь возможность размещать данные на своем веб-сайте. Метод api доступен по адресу '/ api / v1 / passes/all'.

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

Как вы можете видеть в моей модели , класс CustomerCard имеет свойство User. Это указывает, кому принадлежит карточка клиента.

Итак, этот вызов загружает пользователя из pass: 'api / v1 / passes / all?$top=1' но когда я вызываю этот экземпляр, пользовательский экземпляр NULL : 'api / v1 / passes / all?$top=1 и $ skip=1'.

Однако, когда я вызываю ' api / v1 / passes / all?$top=2', пользователь ибо второй проход уже загружен. Так вот где мой мозг взорвался! Я что, не понимаю? Почему ссылка на пользователя не идет вместе со второй? Может быть, это как-то связано с функцией ленивой загрузки EF?

Править Когда я использую метод расширения Include on context.проходит, выбрасывается Ошибка:

Указанный путь включения недопустим. EntityType ' LCS_Model.Передайте не объявляет свойство навигации с именем "пользователь".

Это потому что проходит как dbset, содержит CustomerCard, а также ваучер. Есть ли способ, которым я могу сказать своему контексту ожидать или преобразовать его в пользовательскую карточку?

Кто-нибудь, пожалуйста, может мне помочь. Если вы не понимаете моего вопроса, спрашивайте!

Всем спасибо!

EDIT 2

Метод на моем контроллере API теперь

[HttpGet]
[Queryable]
public IQueryable<CustomerCard> GetAllPasses(string version) {
     return context.Passes.Include("User").OfType<CustomerCard>();
}

Это дает мне правильные пункты. У меня есть 2 карты клиентов в моей БД. Оба от одного и того же пользователя. Мой API все еще загружает пользователя. То в тот момент, когда мой сайт получает ответ, свойство User становится нулевым. Я предполагаю, что это потому, что он все еще ссылается на того же пользователя из первого элемента массива. Разве это возможно? И если да,то как я могу предотвратить это?

1 3

1 ответ:

Да, вы должны убедиться, что все связанные записи включены, когда вы делаете запрос. Смотрите this для некоторых примеров. Во вторых... Я не могу понять, почему вы делаете всю эту работу с циклом for... Это очень много абсолютно ненужной и бесполезной работы для сервера, чтобы сделать, если вы хотите сделать любую подкачку. Я думаю, что помимо любых других фильтров, которые вы, возможно, захотите применить, ваши GetAllPasses должны выглядеть примерно так.

public IQueryable<CustomerCard> GetAllPasses() {
    return context.Passes.Include(r => r.User);
}

Edit (2): мне нужно читать лучше. Мне пришлось признаюсь, я не знаком с наследованием типов в EF. Я нашел некоторые вещи, которые могут работать здесь: таблица на иерархию, таблица для конкретного типа , а также Смотрите также MSDN Queryable.OfType. Это предположение, но давайте попробуем:

public IQueryable<CustomerCard> GetAllPasses() {
    return context.Passes.OfType<CustomerCard>().Include(r => r.User);
}