用于联接多个表的LINQ语句(实体框架6)

时间:2019-01-20 21:31:27

标签: c# entity-framework linq join

我是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 < / strong>有多张照片

我现在想读取包含所有相关对象的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 :(得分:0)

使用然后包含

尝试以下操作
using (var db = new LocationScoutContext())
{
   photoPlacesFound = db.PhotoPlaces.Include(pp => pp.ParkingLocations)
   .ThenInclude(pp => pp.ShootingLocations)
   .ThenInclude(pp => pp.Photos)
   .ToList();
}

答案 1 :(得分:0)

数据库查询的较慢部分之一是将所选数据从数据库管理查询传输到本地进程。因此,只选择您实际打算使用的值是明智的。

例如,每个PhotoPlace具有零个或多个ParkingLocations,每个ParkingLocaction都使用外键恰好属于一个PhotoPlace。一对多关系非常简单。

因此,如果PhotoPlace 4具有1000个ParkingLocations,则每个ParkingLocation都将有一个外键指向其所属的PhotoPlace。如预期的那样,该外键的值为4。

当您使用Include来获取带有其ParkingLocations的PhotoPlace时,还要选择外键。在您已经知道此值的情况下,发送1000倍的值4是多么浪费。

  

使用实体框架时,请始终使用“选择”而不是“包含”。仅在计划更新获取的数据时才使用“包含”。

以下查询将更加高效,因为它仅选择您实际计划使用的属性:

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(),
    });

一些改进

我注意到您将一对多关系声明为列表而不是ICollection。您确定PhotoPlace.ParkingLocation [4]具有正确的含义吗?

我还注意到您没有声明虚拟表之间的关系

  

在实体框架中,非虚拟属性表示表的列。表之间的关系(一对多,多对多...)由虚拟属性表示。

只要遵循entity framework code first conventions,大多数元素就不需要属性或流畅的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]

最后:由于构造函数没有执行任何操作,因此我将其删除