Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# EF核心:如何在相关模型的模型中获得最佳平均值_C#_Sql Server_Entity Framework_Entity Framework Core_Blazor Server Side - Fatal编程技术网

C# EF核心:如何在相关模型的模型中获得最佳平均值

C# EF核心:如何在相关模型的模型中获得最佳平均值,c#,sql-server,entity-framework,entity-framework-core,blazor-server-side,C#,Sql Server,Entity Framework,Entity Framework Core,Blazor Server Side,我已经有了一个使用实体框架(EF核心)的Blazor服务器应用程序 我对两个模型使用代码优先的方法,Entry和Target 每个条目都有一个目标。因此,一个目标可以有多个条目指向它 目标的模型如下所示: public class Target { public string TargetId { get; set; } [Required] public string Name { get; set; } [InverseProperty(&qu

我已经有了一个使用实体框架(EF核心)的Blazor服务器应用程序

我对两个模型使用代码优先的方法,
Entry
Target

每个条目都有一个目标。因此,一个目标可以有多个条目指向它

目标的模型如下所示:

public class Target
  {
    public string TargetId { get; set; }
    
    [Required]
    public string Name { get; set; }

    [InverseProperty("Target")]
    public List<Entry> Entries { get; set; }

    [NotMapped]
    public double AverageEntryRating => Entries != null ? Entries.Where(e => e.Rating > 0).Select(e => e.Rating).Average() : 0;
  }
public class Entry
  {
    public string EntryId { get; set; }
    
    public int Rating { get; set; }
    
    [Required]
    public string TargetId {get; set; }

    [ForeignKey("TargetId")]
    public Target Target { get; set; }    
  }
正如您在我的
目标
模型中看到的,我想知道每个
目标
的平均评级是多少,基于指向
目标
的所有条目的平均值-这就是为什么目标中有这个(
未映射
)属性:

public double AverageEntryRating => Entries != null ? Entries.Where(e => e.Rating > 0).Select(e => e.Rating).Average() : 0;
但是(当然)这并不总是有效的,因为不能保证在访问属性时加载目标的条目

我尝试以不同的方式解决它,例如在我的TargetService中有一个方法,在该方法中,我可以传入targetId并给出结果:

 public double GetTargetMedianEntryRating(string targetId) {
        var median = _context.Entries
            .Where(e => e.TargetId == targetId && e.Rating > 0)
            .Select(e => e.Rating)
            .DefaultIfEmpty()
            .Average();
        return median;
    }
但是当我在一个表中列出我的目标,然后在一个单元格中显示这个值(传入foreach循环的当前targetId)时,我得到了一个并发异常,因为数据库上下文在多个线程中使用(我猜一个线程通过行/目标循环,另一个线程通过获取平均值)。。。所以这让我陷入了新的麻烦

就我个人而言,我更喜欢使用
Target
模型上的
AverageEntryRating
属性,因为这对我来说很自然,而且像这样访问值也很方便


但是,当我访问此属性时,如何确保加载了条目。或者这不是一个好方法,因为这意味着我必须为所有目标加载条目,这将导致性能下降?如果是的话,什么是获得平均值/中值的好方法?

我可以考虑几个选项,这取决于你的情况。可能会有更多的选择,但至少我希望这能给你一些你没有考虑过的选择

具有始终包含所有条目的BaseQuery扩展方法

无论何时查询
目标
,都可以确保执行
。包括(x=>x.Entries)
。您甚至可以创建一个数据库上下文的扩展方法,名为
TargetBaseQuery()
,它包含了您使用它时所需的所有关系。然后,您将确保在访问属性
AverageEntryRating
时,将加载每个目标的
条目
列表

缺点是性能受到影响,因为每次加载目标时,都需要加载其所有条目。。。这就是你查询的每个目标。 然而,如果您需要让它快速工作,这可能是最简单的。务实的方法是这样做,测量性能影响,如果速度太慢,那么尝试其他方法,而不是进行过早的优化。当然,风险在于,它现在可能运行得很快,但将来可能会严重扩展。所以由你来决定

另一个要考虑的是,不要每次都包括条目,而只有在那些你知道你需要平均值的地方。然而,这可能会成为一个可维护性问题

使用不同的模型和服务方法来计算TargetStats

您可以创建另一个类模型来存储
目标的相关数据,但它不会持久化到数据库中。例如:

public class TargetStats
  {    
    public Target Target { get; set; }

    public double AverageEntryRating { get; set; }
  }
然后,在您的服务中,您可以有这样一种方法(尚未测试,因此它可能无法正常工作,但您知道了)

public List GetTargetStats(){
var targetStats=_context.Target
.Include(x=>x.Entries)
.选择(x=>new TargetStats
{
目标=x,
AverageEntryRatings=x.Entries。其中(e=>e.Rating>0)。选择(e=>e.Rating)。Average(),
})
托利斯先生()
返回targetStats;
}
这样做的唯一好处是,您不必降低所有与目标相关的查询的性能,而只需降低那些需要平均评级的查询的性能。 但这个查询可能仍然很慢。要进一步调整它,您可以编写原始SQL而不是LINQ,或者在数据库中创建一个可以查询的视图

将目标的平均评级存储并更新为一列

为了保持代码干净并在读取时具有良好的性能,最好的方法可能是将平均值作为一列存储在目标表中。这将把计算的性能成本转移到目标或其相关条目的保存/更新上,但由于数据已经可用,读数将非常快。如果阅读的频率比更新的频率高,那么可能值得去做


你可以看看,因为他们谈论了一些不同的性能调整替代方案。

我可以考虑几个选项,这取决于你的情况。可能会有更多的选择,但至少我希望这能给你一些你没有考虑过的选择

具有始终包含所有条目的BaseQuery扩展方法

无论何时查询
目标
,都可以确保执行
。包括(x=>x.Entries)
。您甚至可以创建一个数据库上下文的扩展方法,名为
TargetBaseQuery()
,它包含了您使用它时所需的所有关系。然后,您将确保在访问属性
AverageEntryRating
时,将加载每个目标的
条目
列表

缺点是性能受到影响,因为每次加载目标时,都需要加载其所有条目。。。那就是
public List<TargetStats> GetTargetStats() {
        var targetStats = _context.Target
            .Include(x => x.Entries)
            .Select(x => new TargetStats 
            {
              Target = x, 
              AverageEntryRatings = x.Entries.Where(e => e.Rating > 0).Select(e => e.Rating).Average(),
            })
            .ToList()

        return targetStats;
    }