Как создать упорядоченное объединение по убыванию в EF - PullRequest
0 голосов
/ 07 мая 2018

Я пытаюсь улучшить производительность кода и набор запросов EF. Здесь задействованы три стола. Devices - список устройств со специфическими для устройства свойствами. Monitors - это список отдельных элементов данных, которые можно выбрать для устройства. MonitorSamples хранит отдельные образцы каждого монитора для данной временной отметки. Я пытаюсь вернуть список последних образцов данных для каждого монитора для каждого устройства.

Ниже приведены неоптимизированные методы возврата этих данных. Я хотел бы перенести как можно больше ответственности на базу данных, а не создавать столько запросов друг к другу. Если оптимизировать только один порядок, когда запрос возвращает самое последнее значение MonitorSample для каждого устройства, это сократит количество запросов, но если бы я мог оптимизировать это в один запрос базы данных, это было бы лучше. Я подумал о создании представления для представления этих данных, но если бы я мог сделать это в linq или стандартном запросе, это было бы намного лучше. Из-за индекса на временной метке производительность возврата единственного MonitorSample велика, но когда запрашивается много из них, производительность начинает сильно снижаться.

Я пытаюсь найти лучший способ создать объединение, включающее всю необходимую информацию, и выделить его в новый объект, с которым я могу работать. У меня проблема с упорядочением данных по меткам времени для каждого отдельного монитора, который будет включен в объединение.

public class MonitorSampleData
{
    public string Name;
    public DateTime Time;
    public int Sequence;
    public string Type;
    public string Unit;
    public string Value;
}

public class DeviceData
{
    public string DeviceName;
    public string DeviceType;
    public ICollection<MonitorSampleData> Monitors;
}

public partial class Device
{
    public int Id;
    public string Name;
    public string Description;
    public string Location;
    public int? SampleRate;
    public bool? SynchronizedSampling;
    public virtual DeviceType Type;
    public virtual DeviceGroup Group;
    public virtual DeviceState State;
    public virtual ICollection<Monitor> Monitors;      
}

public partial class Monitor
{
    public int Id;
    public string Name;
    public bool Enabled;
    public int Sequence;
    public virtual Device Device;
    public virtual MonitorUnitType UnitType;
    public virtual MonitorDataType DataType;
    public virtual ICollection<MonitorSample> Samples;     
}

public partial class MonitorSample
{    
    public int Id;
    public System.DateTime Time;
    public string Value;
    public virtual Monitor Monitor;
}

public ICollection<DeviceData> GetLatestDeviceData()
{
    ICollection<DeviceData> data = new List<DeviceData>();
    using (var context = new ApplicationDbContext())
    {
        var devices = context.GetDevices();
        foreach (var device in devices)
        {
            var deviceData = new DeviceData();
            deviceData.DeviceName = device.Name;
            deviceData.DeviceType = device.Type.ShortName;
            foreach (var monitor in device.Monitors)
            {
                var sample = context.GetLatestDataByMonitor(monitor);

                if (sample != null)
                {
                    MonitorSampleData monitorData = new MonitorSampleData();
                    monitorData.Name = monitor.Name;
                    monitorData.Time = sample.Time;
                    monitorData.Sequence = monitor.Sequence;
                    monitorData.Type = monitor.DataType.Name;
                    monitorData.Unit = monitor.UnitType.Name;
                    monitorData.Value = sample.Value;
                    deviceData.Monitors.Add(monitorData);
                }
            }
            data.Add(deviceData);
        }
    }
    return data;
}

public MonitorSample GetLatestDataByMonitor(Monitor monitor)
{
    MonitorSample sample = null;
    if (monitor != null)
    {
        sample = (from s in MonitorSamples
                    where s.Monitor.Id == monitor.Id
                    orderby s.Time descending
                    select s).FirstOrDefault();
    }
    return sample;
}

1 Ответ

0 голосов
/ 07 мая 2018

Походит на стандартный запрос LINQ проекции.

Используйте свойства навигации, замените foreach на from, var sample = на let sample = и не используйте пользовательские методы, но вставьте все в выражения инициализатора запроса и класса.

Таким образом, результат будет получен с помощью одного запроса к базе данных.

1011 * Е.Г. *

var data =
    (from device in context.Devices
     select new DeviceData
     {
         DeviceName = device.Name,
         DeviceType = device.Type.ShortName,
         Monitors = 
             (from monitor in device.Monitors
              let sample = (from s in monitor.Samples
                            orderby s.Time descending
                            select s).FirstOrDefault()
              select new MonitorSampleData
              {
                  Name = monitor.Name,
                  Time = sample.Time,
                  Sequence = monitor.Sequence,
                  Type = monitor.DataType.Name,
                  Unit = monitor.UnitType.Name,
                  Value = sample.Value
              }).ToList()
     }).ToList();

Вместо

let sample = (from s in monitor.Samples
              orderby s.Time descending
              select s).FirstOrDefault()

вы можете попробовать функционально эквивалентную конструкцию

from sample in monitor.Samples
    .DefaultIfEmpty()
    .OrderByDescending(s => s.Time)
    .Take(1)

что может улучшить перевод SQL.

Обновление: Я пропустил проверку if (sample != null) в исходном коде. Таким образом, реальным эквивалентом LINQ будет вторая конструкция с удаленным .DefaultIfEmpty(), что приведет к INNER JOIN:

from sample in monitor.Samples
    .OrderByDescending(s => s.Time)
    .Take(1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...