簡體   English   中英

將計算的屬性包括在實體EF Core中

[英]Include calculated property in entity EF Core

我對數據模型進行了真正的代碼優先部署,並且我需要執行通常會與視圖相關聯的功能,但是目前尚不確定如何完成此功能。

為了簡化問題,我的模型具有注冊用戶創建和發布的內容,其他用戶可以與之交互。 我正在針對這些事物的流行程度設計一種算法,歸結為與交互類型乘以某個日期周期的倒數相關的加權因子。

例如,假設我們有“喜歡”,“評論”和“購買”,其權重分別為1、2和3。 假設我的時間是1周。 在這種情況下,三周前購買的商品在當前一周的重量為“贊”。

因此,模型的相關部分是具有主鍵和一些其他元數據的Thing 具有主鍵,權Weight值和一些其他元數據的InteractionType Interaction ,其具有復合主鍵(由的Thing主鍵, User對象主鍵,並且InteractionType主鍵)和一個DateValue柱和任何其它內容相關的元數據。

最終,我希望有一種與實體框架兼容的方式來執行以下查詢:

;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;}      
  }

}

消費應用程序中的DbContext定義

namespace Project.Consumer
{
  public class ApplicationData : DbContext
  {
    public DbSet<User> Users {get;set;}
    public DbSet<Thing> Things {get;set;}        

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<Interaction>()
               .HasKey(i => new {i.ThingId, i.UserId, i.InteractionTypeId});            
    }
  }

  public List<Thing> GetMostPopularThings(ApplicationData ctx)
  {
    return ctx.Things.OrderByDescending(lambdaMagic).Take(10).ToList();
  }
}

lambdaMagic當然是貪吃蛇了,但是正如我說的,我不確定是否有記錄的方式來做到這一點,而我只是想念它還是什么。 我已經看到了按照ctx.Things.FromSql("select string or sp here").Take(10).ToList(); 也許還可以,但是我真的想減少項目外部存在的事物的數量(例如SP定義)

自編寫此問題以來,我繼續創建了另一個名為ThingStatistics的模型類:

public class ThingStatistics
{
  [Key]      
  public int ThingId {get; set;}
  [ForeignKey("ThingId")]
  public Thing Thing {get; set;}
  public double InteractionScore {get;set;}
}

然后,我對Thing類進行了如下擴充:

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;}
}

我執行了Add-Migration步驟,然后按如下所示更改了結果Up / Down:

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解決方案可能還不錯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM