Оператор LINQ для объединения нескольких таблиц (Entity Framework 6) - PullRequest
0 голосов
/ 21 января 2019

Я новичок в LINQ / Entity Framework и изо всех сил пытаюсь объединить свои таблицы.

У меня есть следующие классы:

public class PhotoPlace
{
    #region attributes
    [Key]
    public long Id { get; set; }
    public List<ParkingLocation> ParkingLocations { get; set; }
    public SubjectLocation PlaceSubjectLocation { get; set; }
    #endregion

    #region constructors   
    public PhotoPlace()
    { 
    }
    #endregion
}

public class ParkingLocation : LocationBase
{
    #region attributes
    public PhotoPlace PhotoPlace { get; set; }
    public List<ShootingLocation> ShootingLocations { get; set; }
    #endregion

    public ParkingLocation()
    {
    }
}

public class ShootingLocation : LocationBase
{
    #region attributes
    public ParkingLocation ParkingLocation { get; set; }      
    public List<Photo> Photos { get; set; }
    #endregion

    #region constructors
    public ShootingLocation()
    { 
    }
    #endregion
}

public class Photo
{
    #region attributes
    public long Id { get; set; }
    public byte[] ImageBytes { get; set; }

    public ShootingLocation ShootingLocation { get; set; }
    #endregion

    #region constructors
    public Photo()
    {
    }
    #endregion
}

Таким образом, PhotoPlace имеет несколько ParkingLocations , ParkingLocation имеет несколько ShootingLocations , ShootingLocation имеет несколько фотографии .

Теперь я хочу прочитать PhotoPlace со всеми зависимыми объектами: До того, как я добавил фотографии, все было в порядке со следующим утверждением:

using (var db = new LocationScoutContext())
{
    photoPlacesFound = db.PhotoPlaces.Include(pp => pp.PlaceSubjectLocation)
       .Include(pp => pp.ParkingLocations.Select(pl => pl.ShootingLocations))
       .Include(pp => pp.PlaceSubjectLocation.SubjectCountry)
       .Include(pp => pp.PlaceSubjectLocation.SubjectArea)
       .Include(pp => pp.PlaceSubjectLocation.SubjectSubArea).ToList();
 }

Другие классы не должны иметь значения. Я пытался продлить

.Include(pp => pp.ParkingLocations.Select(pl => pl.ShootingLocations))

оператор с другим "Выбрать", но это не сработает. Любая помощь приветствуется.

Ответы [ 2 ]

0 голосов
/ 21 января 2019

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

Например, каждый PhotoPlace имеет ноль или более ParkingLocations, каждый ParkingLocaction принадлежит ровно одному PhotoPlace, используяиностранный ключ.Довольно прямое отношение «один ко многим».

Так что если у PhotoPlace 4 есть 1000 ParkingLocations, то у каждого ParkingLocation будет внешний ключ к PhotoPlace, к которому он принадлежит.Как и ожидалось, значение этого внешнего ключа будет равно 4.

Когда вы используете Include для извлечения PhotoPlaces с помощью ParkingLocations, вы также выбираете внешние ключи.Какая трата посылать в 1000 раз больше значения 4, хотя вы уже знаете это значение.

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

Следующий запрос будет гораздо более эффективным, поскольку он только выбирает свойства, которые вы фактически планируете использовать:

var photoPlacesFound = db.PhotoPlaces
    .Where(photoPlace => ...)          // if you don't want all PhotoPlaces
    .Select(photoPlace => new
    {
         // Select only the PhotoPlace properties you actually plan to use:
         Id = photoPlace.Id,
         ...

         ParkingLocations = PhotoPlace.ParkingLocations
             .Select(parkingLocation => new
             {
                  // again: select only the ParkingLocation properties you plan to use
                  Id = parkingLocation.Id,
                  Name = parkingLocation.Name,

                  // not needed: you already know the value:
                  // PhotoPlaceId = parkingLocation.PhotoPlaceId, // foreign key

                  ShootingLocations = parkingLocations.ShootingLocations
                      .Select(shootingLocation => new
                      {
                           // you know the drill by now: only select the ...
                      })
                      .ToList(),

             })
             .ToList(),
    });

Некоторые улучшения

Я заметил, что вы объявили свои отношения один-ко-многим как списки вместо ICollections.Вы уверены, что PhotoPlace.ParkingLocation [4] имеет правильное значение?

Я также заметил, что вы не объявляли виртуальные отношения между таблицами

В платформе сущностейне виртуальные свойства представляют столбцы таблиц.Отношения между таблицами (один-ко-многим, многие-ко-многим, ...) представлены виртуальными свойствами.

До тех пор, пока вы сначала будете следовать коду структуры сущностей условные обозначения , вам не понадобятся атрибуты или свободный API для большинства элементов, что делает ваш код компактным и легким для чтения:

public class PhotoPlace
{
    public long Id { get; set; }
    ... // other properties

    // every PhotoPlace has zero or more ParkingLocations (one-to-many)
    public virtual ICollection<ParkingLocation> ParkingLocations { get; set; }
}

public class ParkingLocation
{
    public long Id {get; set;}
    ...

    // every ParkingLocation belongs to exactly one PhotoPlace using foreign key
    public long PhotoPlaceId {get; set;}
    public virtual PhotoPlace PhotoPlace {get; set;}

    // every ParkingLocation has zero or more ShootingLocations (one-to-many)
    public virtual ICollection<ShootingLocation> ShootingLocations {get; set;}
}

Поскольку я следовал соглашениям, ModelBuilder способен обнаруживатьназвания таблиц и столбцов.Он может обнаружить первичные ключи.Из-за виртуального ключевого слова ему известно отношение между таблицами и внешними ключами, используемыми в этих отношениях.

Поскольку я использую ICollection <...>, ваш компилятор не примет неопределенную функциональность, такую ​​как List [4]

Наконец: поскольку конструкторы ничего не делают, я их удалил

0 голосов
/ 21 января 2019

Попробуйте следующее, используя thenInclude

using (var db = new LocationScoutContext())
{
   photoPlacesFound = db.PhotoPlaces.Include(pp => pp.ParkingLocations)
   .ThenInclude(pp => pp.ShootingLocations)
   .ThenInclude(pp => pp.Photos)
   .ToList();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...