【问题标题】:LINQ statement for joining multiple tables (Entity Framework 6)用于连接多个表的 LINQ 语句(实体框架 6)
【发布时间】:2019-06-14 07:47:26
【问题描述】:

我是 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有多个ParkingLocation,一个ParkingLocation有多个ShootingLocation,一个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))

带有另一个“选择”的语句,但这不起作用。非常欢迎任何帮助。

【问题讨论】:

标签: c# entity-framework linq join


【解决方案1】:

使用 thenInclude

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

【讨论】:

    【解决方案2】:

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

    例如,每个PhotoPlace 有零个或多个ParkingLocations,每个ParkingLocaction 恰好属于一个PhotoPlace,使用外键。一个相当简单的一对多关系。

    因此,如果 PhotoPlace 4 有 1000 个 ParkingLocation,那么每个 ParkingLocation 都会有一个指向其所属 PhotoPlace 的外键。正如预期的那样,这个外键的值为 4。

    当您使用Include 获取带有其ParkingLocations 的PhotoPlaces 时,您还可以选择外键。发送 1000 倍的值 4 真是太浪费了,而您已经知道这个值。

    使用实体框架时,始终使用 Select 而不是 Include。仅当您计划更新获取的数据时才使用 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] 有正确的含义吗?

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

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

    只要您遵循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;}
    }
    

    由于我遵循约定,模型构建器能够检测表和列的名称。它可以检测主键。由于 virtual 关键字,它知道表之间的关系以及这些关系中使用的外键。

    因为我使用 ICollection<...> 你的编译器不会接受像 List[4] 这样的未定义功能

    最后:因为构造函数没有做任何事情,我删除了它们

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-15
      • 2013-04-05
      • 1970-01-01
      • 2013-07-06
      • 1970-01-01
      • 2014-02-07
      • 1970-01-01
      • 2020-08-14
      相关资源
      最近更新 更多