Используйте Entity Framework, я хочу включить только первые дочерние объекты, а не child of child (sub of sub)
Используя Entity Framework, я хочу включить только первый уровень дочерних объектов, а не дочерних.
У меня есть эти два класса:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public ICollection<OffersTBL> OffersTBLs { get; set; }
}
public class OffersTBL
{
public int ID { get; set; }
public string Name { get; set; }
public int CatId { get; set; }
public string BusinessesTBLID { get; set; }
public virtual BusinessesTBL BusinessesTBLs { get; set; }
}
Когда я пытаюсь привести все предложения в соответствии с полем CatId, мне также нужно возвращать значения BusitiesTBL, но метод также возвращает предложения снова для каждого объекта BuseriesTBL. Мой код:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs);
}
Вы можете увидеть неправильный результат на: http://mycustom.azurewebsites.net/api/OffersApi/GetOffersTBLsCat/4
Как вы можете видеть, он возвращает все предложения по каждому бизнес-объекту, в то время как бизнес-объект по каждому предложению, и я хочу только возвращать предложения с его бизнес-объектом без предложения в Business obj.
Может ли кто-нибудь помочь, пожалуйста?
Ответы
Ответ 1
Это происходит потому, что Entity Framework выполняет привязку отношений, которая является процессом, который автоматически заполняет свойства навигации, когда объекты, которые там находятся, присутствуют в контексте. Таким образом, с круговыми ссылками вы можете бесконечно развернуть навигационные свойства, даже когда ленивая загрузка отключена. Сериализатор Json делает именно это (но, по-видимому, ему поручено иметь дело с круговыми ссылками, поэтому он не попадает в бесконечный цикл).
Трюк заключается в том, чтобы предотвратить зависание отношений. Исправление отношений зависит от контекста ChangeTracker
, который кэширует объекты для отслеживания их изменений и ассоциаций. Но если нечего отследить, там ничего не исправить. Вы можете остановить отслеживание, вызвав AsNoTracking()
:
db.OffersTBLs.Include(s => s.BusinessesTBLs)
.AsNoTracking()
Если кроме того вы также отключите ленивую загрузку в контексте (установив contextConfiguration.LazyLoadingEnabled = false
), вы увидите, что в строку Json заселяются только OffersTBL.BusinessesTBLs
и что BusinessesTBL.OffersTBLs
- пустые массивы.
Бонус в том, что AsNoTracking()
увеличивает производительность, потому что трекер изменений не занят, отслеживая все объекты, которые EF материализуется. Фактически, вы всегда должны использовать его в отключенной настройке.
Ответ 2
Вы отключили ленивую загрузку на OffersTBL, сделав ее не виртуальной. Что делать, если вы активируете ленивую загрузку? например:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
//put a virtual here
public virtual ICollection<OffersTBL> OffersTBLs { get; set; }
}
Затем, когда вы сериализуете, обязательно не вызывайте/не включайте OffersTBL. Если OffersTBL все еще возвращаются, это происходит потому, что вы извлекаете их где-нибудь в своем коде. Если это происходит, отредактируйте свой вопрос и вставьте весь код, включая логику сериализации.
Ответ 3
Так как OffersTBL имеет связь с BusinessesTBL и BusinessesTBL с предложениями TBL, вы можете бесконечно бросать объекты, такие как OffersTBL.BusinessesTBL.OffersTBL.BusinessesTBL и т.д.
Чтобы контролировать глубину вложенности сущностей, я обычно использую helperclasses с необходимыми свойствами в них.
Для BusinessTBL
public class BusinessesTBLHelper
{
private BusinessesTBLHelper(BusinessesTBL o){
ID = o.ID;
FirstName = o.FirstName;
lastName = o.LastName;
OffersTBLids = new List<int>();
foreach(OffersTBL offersTbl in o.OffersTBLs){
OffersTBLids.Add(offersTbl.ID);
}
}
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public IEnumerable<int> OffersTBLids { get; set; } //no references anymore
}
И то же самое для вашего объекта OffersTBL.
public class OffersTBLHelper
{
private OffersTBLHelper(OffersTBL o){
ID = o.ID;
Name = o.Name;
CatId = o.CatId;
BusinessesTBLID = o.BusinessesTBLID;
BusinessesTBLs = new BusinessesTBLHelper(o.BusinessesTBLs);
}
public string ID { get; set; }
public string Name{ get; set; }
public intCatId{ get; set; }
public string BusinessesTBLID { get; set; }
public BusinessesTBLHelper BusinessesTBLs { get; set; }
}
В запросе базы данных вы можете напрямую создавать новые helperobjects из queryresult:
public IEnumerable<OffersTBLHelper> GetOffersTBLsCat(int id)
{
return db.OffersTBLs.where(s => s.CatId == id).Select(x=> new OffersTBLHelper(x)).ToList();
}
Теперь у вас есть все OfferTBL с BusinessesTBL под. Цикл останавливается здесь, потому что у BusinessesTBL нет под ним объявлений. Однако для дальнейших ссылок и идентификации они содержат только идентификаторы в списке.
Ответ 4
Предполагая, что объект isnt null и просто пуст:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs).Where(x => !x.BusinessesTBLs.OffersTBLs.Any());
}
Изменить: Фильтр перед включением:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Where(x => !x.BusinessesTBLs.OffersTBLs.Any())
.Include(s => s.BusinessesTBLs);
}