C# 在实体EF核心中包含计算属性

C# 在实体EF核心中包含计算属性,c#,entity-framework-core,C#,Entity Framework Core,我有一个真正的数据模型的代码优先部署,我需要执行通常与视图相关联的功能,但目前我不确定如何完成它 为了简化问题,我的模型包含注册用户创建的东西和其他用户可以交互的帖子。我正在设计一种算法来计算这些东西的流行程度,可以归结为一个权重因子,它与交互类型乘以某个日期周期的倒数有关 例如,假设“喜欢”、“评论”和“购买”的权重分别为1、2和3。也就是说我的时间是一周。在这种情况下,3周前的购买与当前一周的购买一样重 因此,模型的相关部分是Thing,它有一个主键和一些附加元数据;InteractionT

我有一个真正的数据模型的代码优先部署,我需要执行通常与视图相关联的功能,但目前我不确定如何完成它

为了简化问题,我的模型包含注册用户创建的东西和其他用户可以交互的帖子。我正在设计一种算法来计算这些东西的流行程度,可以归结为一个权重因子,它与交互类型乘以某个日期周期的倒数有关

例如,假设“喜欢”、“评论”和“购买”的权重分别为1、2和3。也就是说我的时间是一周。在这种情况下,3周前的购买与当前一周的购买一样重

因此,模型的相关部分是
Thing
,它有一个主键和一些附加元数据;
InteractionType
,它有一个主键、一个
Weight
值和一些其他元数据;
交互
具有复合主键(由
事物
主键、
用户
对象主键和
交互类型
主键组成)和
日期值
列以及任何其他上下文相关的元数据

最终,我希望有一种与实体框架兼容的方式来执行以下查询:

;WITH InteractionScore
AS
(
  SELECT t.Id ThingId, SUM(it.Weight/CAST(DATEDIFF(wk, i.DateValue, GETDATE()) AS float)) IntScore
  FROM Things t
  INNER JOIN Interactions i ON t.Id = i.ThingId
  INNER JOIN InteractionTypes it ON i.InteractionTypeId = it.Id
  GROUP BY t.ID
)
SELECT TOP(10) t.*
FROM Things t
LEFT JOIN InteractionScore isc ON t.Id = isc.ThingId
ORDER BY ISNULL(isc.IntScore, 0.0) DESC
数据模型类定义如下:

namespace Project.Entities
{
  public class User
  {
    [Key]
    public int Id {get; set;}
    public string IdentityRef {get;set;}
    public string DisplayName {get;set;}
    [InverseProperty("Owner")]
    public ICollection<Thing> OwnedThings {get; set;}
  }

  public class InteractionType
  {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public double Weight { get; set; }
  }

  public class Thing
  {
    [Key]
    public int Id {get;set;}
    public int OwnerId {get; set;}
    [ForeignKey("OwnerId")]
    public User Owner {get; set;}
    public string Summary {get; set;}
    public string Description {get; set;}
    public ICollection<Interaction> Interactions {get;set;}
  }

  public class Interaction
  {
    public int ThingId { get; set; }
    public int UserId { get; set; }
    public int InteractionTypeId {get; set;}
    public Thing Thing {get; set;}
    public User User {get;set;}
    public InteractionType InteractionType {get;set;}
    public DateTime DateValue {get;set;}  
    public string Comment {get; set;}
    public string PurchaseReference {get;set;}      
  }

}
public class Thing
{
    [Key]
    public int Id {get;set;}
    public int OwnerId {get; set;}
    [ForeignKey("OwnerId")]
    public User Owner {get; set;}
    public string Summary {get; set;}
    public string Description {get; set;}
    public ICollection<Interaction> Interactions {get;set;}
    public ThingStatistics Stats {get;set;}
}
public partial class AggregationHack : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
       migrationBuilder.Sql("GO; CREATE VIEW ThingStatistics AS SELECT t.ID ThingId, ISNULL(it.Weight/CAST(DATEDIFF(wk, i.DateValue, GETDATE()) + 1 AS float), 0) InteractionScore FROM Things t LEFT JOIN Interactions i INNER JOIN InteractionTypes it ON i.InteractionTypeId = it.Id ON t.ID = i.ThingId GROUP BY t.Id; GO;");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
       migrationBuilder.Sql("DROP VIEW ThingStatistics; GO;");
    }
}
然后我扩展了Thing类,如下所示:

namespace Project.Entities
{
  public class User
  {
    [Key]
    public int Id {get; set;}
    public string IdentityRef {get;set;}
    public string DisplayName {get;set;}
    [InverseProperty("Owner")]
    public ICollection<Thing> OwnedThings {get; set;}
  }

  public class InteractionType
  {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public double Weight { get; set; }
  }

  public class Thing
  {
    [Key]
    public int Id {get;set;}
    public int OwnerId {get; set;}
    [ForeignKey("OwnerId")]
    public User Owner {get; set;}
    public string Summary {get; set;}
    public string Description {get; set;}
    public ICollection<Interaction> Interactions {get;set;}
  }

  public class Interaction
  {
    public int ThingId { get; set; }
    public int UserId { get; set; }
    public int InteractionTypeId {get; set;}
    public Thing Thing {get; set;}
    public User User {get;set;}
    public InteractionType InteractionType {get;set;}
    public DateTime DateValue {get;set;}  
    public string Comment {get; set;}
    public string PurchaseReference {get;set;}      
  }

}
public class Thing
{
    [Key]
    public int Id {get;set;}
    public int OwnerId {get; set;}
    [ForeignKey("OwnerId")]
    public User Owner {get; set;}
    public string Summary {get; set;}
    public string Description {get; set;}
    public ICollection<Interaction> Interactions {get;set;}
    public ThingStatistics Stats {get;set;}
}
public partial class AggregationHack : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
       migrationBuilder.Sql("GO; CREATE VIEW ThingStatistics AS SELECT t.ID ThingId, ISNULL(it.Weight/CAST(DATEDIFF(wk, i.DateValue, GETDATE()) + 1 AS float), 0) InteractionScore FROM Things t LEFT JOIN Interactions i INNER JOIN InteractionTypes it ON i.InteractionTypeId = it.Id ON t.ID = i.ThingId GROUP BY t.Id; GO;");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
       migrationBuilder.Sql("DROP VIEW ThingStatistics; GO;");
    }
}

所以,现在我可以做
ctx.Things.Include(t=>t.Stats).OrderByDescending(t=>t.Stats.InteractionScore).Take(10).ToList()
,这很有效,但我不知道这是否正确,因为它感觉像是一种黑客行为…

ORM的思想是连接大部分被导航属性所取代。因此,您的基本C#查询如下所示

ctx.Things
   .Include(t => t.Interactions).ThenInclude(i => i.InteractionType) 
   .Select(t => new { 
      t.ThingId, 
      IntScore = t.Interactions.Sum(i => i.InteractionType.Weight /  ...) 
    })
   .OrderBy(x => x.IntScore)
   .Select(x => x.ThingId)
   .Take(10);
在那些
上,您需要更换DateDiff(NuGet上有一个第三方pkg)。

虽然这更简单,可读性更强,但很难通过这种方式影响查询的性能,您必须进行测试。您的SQL解决方案可能并不全是坏的

ORM的思想是连接大部分被导航属性所取代。因此,您的基本C#查询如下所示

ctx.Things
   .Include(t => t.Interactions).ThenInclude(i => i.InteractionType) 
   .Select(t => new { 
      t.ThingId, 
      IntScore = t.Interactions.Sum(i => i.InteractionType.Weight /  ...) 
    })
   .OrderBy(x => x.IntScore)
   .Select(x => x.ThingId)
   .Take(10);
在那些
上,您需要更换DateDiff(NuGet上有一个第三方pkg)。

虽然这更简单,可读性更强,但很难通过这种方式影响查询的性能,您必须进行测试。您的SQL解决方案可能并不全是坏的

与实体框架兼容的方式将涉及C类上的一些C代码。发布(简化的)类开始。@HenkHolterman,我添加了更多的上下文和一些通用C#代码,让您了解到目前为止我所做的工作。感谢您的关注。与实体框架兼容的方法将涉及C类上的一些C代码。发布(简化的)类开始。@HenkHolterman,我添加了更多的上下文和一些通用C#代码,让您了解到目前为止我所做的工作。谢谢你看。我必须测试一下它的性能。我更愿意这样做,因为所有元素都驻留在代码中,而我的解决方案只是利用EF中的弱引用。这里的主要问题是,当人们浏览到我的网站时,这会驱动他们看到的第一个页面,因此它需要非常快速地加载。感谢您的回复,我将寻找DateDiff功能的NuGet软件包。让我们听听它是如何实现的,速度方面的。如果你不得不做很多调整的话,可能会发布一个自我回答。只是想补充一点,上面的查询可以简化为ctx.Things.Select(x=>x.ThingId)。接受(10),因为你不是在过滤,只是在投影。include也没用。我必须测试一下它的性能。我更愿意这样做,因为所有元素都驻留在代码中,而我的解决方案只是利用EF中的弱引用。这里的主要问题是,当人们浏览到我的网站时,这会驱动他们看到的第一个页面,因此它需要非常快速地加载。感谢您的回复,我将寻找DateDiff功能的NuGet软件包。让我们听听它是如何实现的,速度方面的。如果你不得不做很多调整的话,可能会发布一个自我回答。只是想补充一点,上面的查询可以简化为ctx.Things.Select(x=>x.ThingId)。接受(10),因为你不是在过滤,只是在投影。包括也没用。