Ссылка 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 ответ:
Да, вы должны убедиться, что все связанные записи включены, когда вы делаете запрос. Смотрите 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); }