简体   繁体   中英

Is there a more elegant solution to calculating the Timespan that a certain value is occurring in Entity Framework?

My problem: I have a table in our database that is called DevicePropertyUpdates. We use this table to record a state change in a device. I need to know the total amount of time that a certain mode is active during a specified time range.

For example, if it is mode A from 00:00 to 4:00, mode B from 4:00 to 10:00, mode A from 10:00 to 16:00, and Mode C from 16:00 to 24:00. If I want to know how long mode A was active for the day, I add up 00:00 to 4:00 and 10:00 to 16:00 and get 10 total hours.

My current code is below. Very iterative and I believe will force entity framework to materialize all these modes for the entire date range. I'm looking for a more elegant solution just to further my problem solving ability and to write cleaner code. I'm hoping I just missed something obvious that I can learn from. The kind of ideas I was consisering was .Zip, .GroupBy, or some kind of join to find the differences but I couldn't think of anything useful.

The architect prefers an entity framework approach to this. The DevicePropertyUpdates table has the following structure.

Timestamp - datetime

StringValue - varchar(max)

Name - varchar(100)

Sample SQL

Row 1: "OperatingMode", "A", "7/1/2014 1:00:00"

Row 2: "OperatingMode", "A", "7/1/2014 2:00:00"

Row 3: "OperatingMode", "B", "7/1/2014 2:15:00"

Row 4: "OperatingMode", "A", "7/1/2014 3:33:00"

using (Container container = ContextFactory.CreateInstance())
{
    IEnumerable<Model.DevicePropertyUpdate> updates =
        container.DevicePropertyUpdates.Where(
            dpu => dpu.Name == "OperatingMode" && dpu.Timestamp <= endTime && dpu.Timestamp >= startTime);

    TimeSpan totalTime = TimeSpan.Zero;
    DateTime? start = null;

    foreach (var update in updates)
    {
        if (update.StringValue == operatingMode)
        {
            if (!start.HasValue)
            {
                start = update.Timestamp;
            }
        }
        else
        {
            if (start.HasValue)
            {
                totalTime += update.Timestamp - startTime;
                start = null;
            }
        }
    }
    //Include edge case if matching operating mode was the last to occur in the time range.
    if (start.HasValue)
    {
        totalTime += endTime - startTime;
    }

    return totalTime;
}

You could sum up all the ON Times from Timestamp till endtime. Then sum all the IDLE time and sumstract the result. This would require two SQL Statements and a resultset that follows a few rules. It has to start with an ON an end with an IDLE an every ON must have a corresponding IDLE.

|    ON     IDLE      ON     IDLE             ON       IDLE            |
     ------------------------------------------------------------------
                      -------------------------------------------------
                                              -------------------------


             ----------------------------------------------------------
                             ------------------------------------------
                                                       ---------------- 

Your Linq would look like this.

using (var db = new TestContext()) {
    DateTime startDate = new DateTime(2014,05,01), endDate = new DateTime(2014,06,1);

    var baseQuery = db.DevicePropertyUpdates
                        .Where(p => p.TimeStamp >= startDate && p.TimeStamp < endDate)
                        .Where(p => p.Name == "OperatingMode");

    var OnSum = baseQuery.Where(p => p.StringValue == "ON")
                       .Sum(p => SqlFunctions.DateDiff("ss",p.TimeStamp, endDate));
    var IdleSum = baseQuery.Where(p => p.StringValue == "IDLE")
                       .Sum(p => SqlFunctions.DateDiff("ss", p.TimeStamp,endDate));

    var duration = TimeSpan.FromSeconds(OnSum.Value - IdleSum.Value);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM