【发布时间】:2019-07-23 19:53:15
【问题描述】:
我在编写稍微复杂的 Select 语句时遇到问题。
我的 EF 对象如下所示:
public partial class SensorEvent
{
public SensorEvent()
{
SensorData = new HashSet<SensorData>();
}
public int Id { get; set; }
public int SensorId { get; set; }
public int RecordTime { get; set; }
public Sensor Sensor { get; set; }
public ICollection<SensorData> SensorData{ get; set; }
}
public partial class SensorData
{
public int Id { get; set; }
public int SensorEventId { get; set; }
public string DataType { get; set; }
public string Description { get; set; }
public SensorEvent SensorEvent { get; set; }
}
public partial class Sensor
{
public Sensor()
{
SensorEvent = new HashSet<SensorEvent>();
SensorPlacement = new HashSet<SensorPlacement>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SensorEvent> SensorEvent{ get; set; }
public ICollection<SensorPlacement> SensorPlacement{ get; set; }
}
public partial class Room
{
public Sensor()
{
SensorPlacement = new HashSet<SensorPlacement>();
}
public int Id { get; set; }
public int FloorId { get; set; }
public string Name { get; set; }
public Floor Floor { get; set; }
public ICollection<SensorPlacement> SensorPlacement{ get; set; }
}
public partial class Floor
{
public Floor()
{
Room = new HashSet<Room>();
}
public int Id { get; set; }
public int BuildingId { get; set; }
public string Name { get; set; }
public Building Building { get; set; }
public ICollection<Room> Room{ get; set; }
}
public partial class Building
{
public Building()
{
Floor = new HashSet<Floor>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Floor> Floor{ get; set; }
}
public partial class SensorPlacement
{
public int Id { get; set; }
public int SensorId { get; set; }
public int RoomId { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public Sensor Sensor { get; set; }
public Room Room { get; set; }
}
让我解释一下。插入到数据库的主要数据是 SensorEvent。 SensorEvent 本质上是从传感器发送的 SensorData 对象列表,以及发送它的传感器和记录时间。到目前为止非常简单,如果我想展示所有 SensorEvent 以及它们来自哪个传感器,我可以返回以下域对象
public class DomainSensorEvent
{
public int Id { get; set; }
public int SensorName { get; set; }
public int RecordTime { get; set; }
public IList<DomainSensorData> SensorData{ get; set; }
}
public partial class DomainSensorData
{
public int Id { get; set; }
public string DataType { get; set; }
public string Description { get; set; }
}
我可以像这样得到它(为了简单起见,忽略 Where、Take、OrderBy 和 Skip 部分)。
public List<DomainSensorEvent> GetDomainSensorEvents()
{
return DbContext.SensorEvent.Select(dse => new DomainSensorEvent
{
Id = dse.Id,
SensorName = dse.Sensor.Name,
RecordTime = dse.RecordTime,
SensorData = dse.SensorData.Select(sd => new DomainSensorData
{
Id = sd.Id,
DataType = sd.DataType,
Description = sd.Description
}).ToList()
}).ToList();
}
到目前为止,很简单,当我想在记录此数据时包括传感器的放置位置(哪个房间,哪个楼层和建筑物)时,就会出现问题。看,我在插入时没有这些信息。传感器可以四处移动,因此 SensorPlacement 表可能会在插入该时间间隔内来自该传感器的 SensorEvent 之后更新。这意味着不是将 SensorEvents 链接到 Room 和 Sensor,而是在查询时解析 Room。是的,这意味着如果 SensorPlacement 尚未更新,则查询可能会返回错误的房间,但它最终会是正确的。更复杂的当然是 Room 中可能没有发生 SensorEvent,所以我还需要检查 null。
新的域模型和查询(我需要帮助的部分)如下所示。
public class DomainSensorEvent
{
public int Id { get; set; }
public int SensorName { get; set; }
public int Room { get; set; }
public int Floor { get; set; }
public int Building { get; set; }
public int RecordTime { get; set; }
public IList<DomainSensorData> SensorData{ get; set; }
}
public List<DomainSensorEvent> GetDomainSensorEvents()
{
return DbContext.SensorEvent.Select(dse => new DomainSensorEvent
{
Id = dse.Id,
SensorName = dse.Sensor.Name,
Room = dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault() != null ?
dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault().Room.Name : null,
Floor = dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault() != null ?
dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault().Room.Floor.Name : null,
Building = dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault() != null ?
dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault().Room.Floor.Building.Name : null,
RecordTime = dse.RecordTime,
SensorData = dse.SensorData.Select(sd => new DomainSensorData
{
Id = sd.Id,
DataType = sd.DataType,
Description = sd.Description
}).ToList()
}).ToList();
}
最明显的问题当然是同一个语句重复了 6 次(使已经很慢的查询变得更糟)。我试图通过将其存储为临时变量来解决它,但显然您只能在 Select 查询中使用一条语句来转换为 SQL。我曾尝试在此之前运行一个额外的 Select 语句,返回一个带有 SensorEvent 和相应 SensorPlacement 的异常类型,但这会破坏导航属性。使用 Joins 也会使导航属性的使用变得困难。
我是不是从错误的角度看待这个问题?是否有一些我遗漏的语法,或者我需要以另一种方式做到这一点?我知道最好的解决方案是能够在插入时知道 SensorPlacement,但目前还不可能。
【问题讨论】:
标签: c# linq entity-framework-core