领域模型性能中的ICollection

ICollection in domain model performance

我有以下与实体框架一起使用的数据库上下文

public class MainContext: DbContext
{
    public MainContext()
        : base("name=MainContext")
    { }

    public virtual DbSet<Device> Devices { get; set; }
    public virtual DbSet<Point> Points { get; set; }
}

使用以下域模型

public class Point
{
    [Key]
    public int Id { get; set; }

    public string Info { get; set; }
    public DateTime FixTime { get; set; }

    public int DeviceId { get; set; }
    public virtual Device Device { get; set; }
}

public class Device
{
    [Key]
    public int Id { get; set; }

    public int SomeValue { get; set; }

    public virtual ICollection<Point> Points { get; set; }

    public bool IsActive()
    {
        Point lastPoint = Points.LastOrDefault();
        if (lastPoint == null) 
        { 
            return false; 
        }
        else
        {
            var diff = DateTime.Now - lastPoint.FixTime;

            if (diff.TotalSeconds > 10 )
            {
                return false;
            } 
            else
            {
                return true;
            }
        }
    }
}

我在设备 class 中调用 IsActive() 方法时遇到了巨大的性能问题。据我所知,它因为调用 Points.LastOrDefault() 查询了设备的所有可用数据库记录,而不是唯一的一个。我知道这是因为在我的 class 中使用了 ICollection 但那是 Entity Framework 的需求。在这种情况下有什么方法可以查询唯一的记录还是我把方法放在错误的地方?

你为什么不尝试 .OrderByOrderByDescending 然后 FirstOrDefault。如果您的数据库中有适当的索引,这应该足够快,并且只会带回一条记录。

Is there's any way to query the only record in such situation or it's just me putting the method in a wrong place?

如果你问我,后者。你比 Entity Framework 更清楚你到底想查询什么:只要你访问延迟加载的导航集合 属性 Points,它就会为该设备加载整个集合。

此外,LastOrDefault() 在数据库环境中没有什么意义,因为反向排序相对便宜。

此外,since you specify no order, the order isn't guaranteed, so this code is guaranteed to break some day (LastOrDefault() returning a different record)

也就是说,我不喜欢执行查询的实体模型,至少在 ORM 的情况下不喜欢,尤其是 Entity Framework,所以我会将该逻辑移到一个单独的 class.将其命名为 PointGetter 或为其命名。

在那里,您可以进行查询:

public class PointGetter
{
    public Point GetLastPoint(DbContext dbContext, Device device)
    {
        var lastPointForDevice = dbContext.Points
                                          .Where(p => p.Device == device)
                                          .OrderByDescending(p => p.FixTime)
                                          .FirstOrDefault();
        return lastPointForDevice;
    }
}