高级LINQ分组和分层数据的投影查询(EF 4.0 + LINQ + ASP.NET MVC + HighCharts)

Dan*_*ski 4 c# linq entity-framework highcharts asp.net-mvc-3

问题概述:

我有数据库表,描述使用Web服务从多个设备收集到集中式数据库的数据.

存储结果的表有意针对性能进行了规范化:通过多个索引更快地进行查询和分组. 我正在使用Entity Framework和Linq进行数据访问.

我需要使用分层分组和投影来正确设计Linq查询.


设备数据库建模概述:

目前我有2种类型的设备

1. Rfid设备

第一个表是RfidTag,它描述了收集数据的标签,1个单个RfidTag = 1个单个传感器.因此,例如1个单个标签可以获得有关温度的数据.

第二个表是RfidReader,它描述了从附加标签收集并发送所有数据的读取模块.附加RfidTags的数量对单个RfidReader没有限制.但是,在读取期间,可以将一个RfidTag附加到一个RfidReader.

CREATE TABLE [dbo].[RfidTag]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderId INT NULL,                  -- Reference to reader
    SensorTypeId INT NOT NULL,          -- Reference to sensor type
    SensorParameters NVARCHAR(50) NULL, -- Sensor parameters
    Hex  NVARCHAR(50) NOT NULL,         -- Hex tag identifier stored as string
    Name NVARCHAR(50) NOT NULL,         -- Tag name
    [Description] NVARCHAR(200) NULL,   -- Tag description
    --
)

CREATE TABLE [dbo].[RfidReader]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime  DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    Name        NVARCHAR(20),        -- Tag name
    [Description]   NVARCHAR(200),   -- Tag description                                             
    SerialNumber    NVARCHAR(12),    -- Unique device serial name               
    --
)
Run Code Online (Sandbox Code Playgroud)

RFID读取每个可以连接到特定的测量区,其为具体的结构描述.

CREATE TABLE [dbo].[RfidReaderPlacement]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    --
    ReaderId INT NOT NULL,              -- Reference to Reader.
    MeasurementZoneId INT NOT NULL,     -- Reference to Measurement Zone.
    StartDate DATETIME NOT NULL,        -- Start date of reading.
    StopDate  DATETIME,                 -- End date of reading.
    --
)
Run Code Online (Sandbox Code Playgroud)

RfidTag收集的单个数据保存在非规范化表中.这个表存储了数百万条记录,并且非常重载.从该表中我们将使用LINQ查询收集数据.

CREATE TABLE [dbo].[RfidReading]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderPlacementId INT NOT NULL, -- Reference to Rfid Reader Placement
    ConstructionId  INT NOT NULL,   -- Reference to Construction
    MeasurementZoneId INT NOT NULL, -- Reference to Measurement Zone
    ReaderId INT NOT NULL,          -- Reference to Rfid Reader
    TagId    INT NOT NULL,          -- Reference to Rfid Tag
    SensorTypeId INT NOT NULL,      -- Reference to Sensor Type
    ReadingDate DATETIME NOT NULL,  -- Reading date
    Value FLOAT NOT NULL            -- Measured value
    --
)
Run Code Online (Sandbox Code Playgroud)

2. ZigBee设备

第一个表是ZigBeeNodeProbe,它描述了收集数据的单个探测器,1个单个ZigBeeNodeProbe = 1个单个传感器.因此,例如1个单探针可以获得有关温度的数据.

第二个表是ZigBeeNode,它描述了包含连接探测的单个设备,1个单个ZigBeeNode = 3个ZigBeeNodeProbes.

第三个表是ZigBeeReader,它描述了从附加节点收集并发送所有数据的读取模块.附加的ZigBeeNodes(已附加ZigBeeNodeProbes)的数量对单个ZigBeeReader没有限制.但是,在读取期间,可以将一个ZigBeeNode连接到一个ZigBeeReader.

CREATE TABLE [dbo].[ZigBeeNodeProbe]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    --
    NodeId INT NULL,                    -- Reference to node
    SensorTypeId INT NOT NULL,          -- Reference to sensor type
    SensorParameters NVARCHAR(50) NULL, -- Sensor parameters
    SocketNumber  INT NOT NULL,         -- Socket number used in parent ZigBeeNode
    Name NVARCHAR(50) NOT NULL,         -- Node name
    [Description] NVARCHAR(200) NULL,   -- Node description
    --
)

CREATE TABLE [dbo].[ZigBeeNode]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderId INT NULL,                    -- Reference to reader
    NetworkAddress  NVARCHAR(50) NOT NULL,-- Node address in ZigBee network
    Name NVARCHAR(50) NOT NULL,           -- Given name
    [Description] NVARCHAR(200) NULL,     -- Tag description
    SocketCount INT NOT NULL DEFAULT 0,   -- Count of available sockets to plug in probe
    NodeFrequency INT NULL,               -- Node frequency
)

CREATE TABLE [dbo].[ZigBeeReader]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime  DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    Name        NVARCHAR(20),        -- Tag name
    [Description]   NVARCHAR(200),   -- Tag description                                             
    SerialNumber    NVARCHAR(12),    -- Unique device serial name               
    --
)
Run Code Online (Sandbox Code Playgroud)

每个ZigBeeReader 可以连接到特定的测量区,其为具体的结构描述.

CREATE TABLE [dbo].[ZigBeeReaderPlacement]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    --
    ReaderId INT NOT NULL,              -- Reference to Reader.
    MeasurementZoneId INT NOT NULL,     -- Reference to Measurement Zone.
    StartDate DATETIME NOT NULL,        -- Start date of reading.
    StopDate  DATETIME,                 -- End date of reading.
    --
)
Run Code Online (Sandbox Code Playgroud)

ZigBeeNodeProbe收集的单个数据保存在非规范化表中.这个表存储了数百万条记录,并且非常重载.从该表中我们将使用LINQ查询收集数据.

CREATE TABLE [dbo].[ZigBeeReading]
(
    Id INT IDENTITY(1,1),
    CreatedDateTime DATETIME NOT NULL DEFAULT GETDATE(),
    ModifiedDateTime DATETIME,
    --
    ReaderPlacementId INT NOT NULL, -- Reference to ZigBee Reader Placement
    ConstructionId    INT NOT NULL, -- Reference to Construction
    MeasurementZoneId INT NOT NULL, -- Reference to Measurement Zone
    ReaderId      INT NOT NULL,     -- Reference to ZigBee Reader
    NodeId        INT NOT NULL,     -- Reference to ZigBee Node
    ProbeId       INT NOT NULL,     -- Reference to ZigBee Node Probe
    SensorTypeId      INT NOT NULL, -- Reference to Sensor Type
    ReadingDate   DATETIME NOT NULL,-- Reading date
    Value         FLOAT NOT NULL    -- Measured value
    --
)
Run Code Online (Sandbox Code Playgroud)

Quering,分组和投影问题:

如您所见,我们有2个分散的表,其中包含由2种类型的设备收集的数据.是的,我们可以假设RfidTag的业务建模几乎与ZigBeeNodeProbe相同.

RfidReader
-- RFidTag
-- RFidTag
...

ZigBeeReader
-- ZigBeeNode
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
-- ZigBeeNode
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
---- ZigBeeNodeProbe
...
Run Code Online (Sandbox Code Playgroud)

现在我们必须查询这两个表,将来自不同表的项目值分配到相同的视图模型中,并添加用于过滤数据的特定分组.

常见场景:

我们想要创建将在特定测量区域中呈现平均温度的图表,记住特定测量区域可能包含多个附加读取器(Rfid和ZigBee)母猪我们必须提供一系列数据.

我正在使用必须启用的HightStock http://www.highcharts.com/products/highstock图表:

  • 放大1个月,3个月,6个月等
  • 从,到目前为止
  • 导出功能
  • 启用和禁用系列的图例

图表示例:

http://jsfiddle.net/hNHUY/1/

问题:

如何为分层数据创建LINQ分组和投影查询? 我们需要为RfidReading和ZigBeeReading表提供通用视图模型.

我的第一次尝试是:

public class ReadingReaderDataModel
{
    public string SeriesName { get; set; }
    public ReadingPeriod ReadingPeriod { get; set; }

    public IEnumerable<ReadingNodeDataModel> ReadingNodeDataModels { get; set; }
    public ReadingReaderDataModel()
    {
        ReadingNodeDataModels = new List<ReadingNodeDataModel>();
    }
}

public class ReadingNodeDataModel
{
    public string NodeName { get; set; }
    public IEnumerable<double> DataValues { get; set; }
    public ReadingNodeDataModel()
    {
        DataValues = new LinkedList<double>();
    }
}

public enum ReadingPeriod
{
    OneMonth, ThreeMonths, SixMonths, YearToDay, OneYear, All
}

public enum ReaderType
{
    Rfid, ZigBee
}
Run Code Online (Sandbox Code Playgroud)

稍后我必须为ASP.NET MVC控制器设计LINQ投影,在这里我不知道如何创建必须包含按OneMonth,ThreeMonths,SixMonths,YearToDay,OneYear,All分组的时间段的平均值的正确查询.

任何人都可以帮我设计这个高级LINQ查询吗?

编辑

请不要指出任何大型数据集的"建议"......

这不是关于性能的问题.

这个问题是关于复杂的LINQ查询.我正在寻找合适的LINQ代码而不是像"那车更快,你应该尝试使用那辆车"的答案......代码答案请...如果你甚至没有尝试理解场景,并提供任何代码,请不要参加...

我特意添加了一个赏金来找到LINQ的解决方案,因为它是高级LINQ问题,就像这个问题的主题一样.

Ner*_*roS 6

据我所知,您在数据库中拥有数百万条记录,并且您的应用程序需要非常好的性能,并且您的结构非常复杂.

远离EF,我的意思是.您可以将它用于较小的数据集,但对于任何需要从大型查询中获得卓越性能的内容,甚至不要去那里.

我的建议是手动编写查询,因为如果你想尽可能地优化EF,你几乎无法跳过很多代码,基本上是以LINQ方式编写SQL(即指定所有SELECT语句) .

如果您想要更具体一点,请使用Dapper.这是运行StackOverflow的"ORM".这里有速度比较:http://code.google.com/p/dapper-dot-net/

但是,如果你已经开始使用EF ......

不要尝试编写一个查询.它不会发生.相反,拆分他们.为您需要制作的每个小组选择不同的一个.这是迄今为止最简单的方法.我相信你应该已经有一个LINQ查询只能获得一个组.现在重复一遍你所需要的其他小组,特别是如果你有大量的Wheres和OrderBy.

来源:每天处理大型数据集查询的人.

  • 狒狒说的话.做你想做的事情并不困难(无论如何都很困难),但问题的实际情况是你只是不愿意这样做.你认为你这样做,但你没有.这种深度和大规模的东西从来没有打算用于完整的ORM作品.当然,使用微型ORM并没有错,它只是将获取的结果转换为POCO,但最终你想要手工编码.相信我(以及这​​些评论中的其他人)告诉你的. (3认同)