2017-05-01 47 views
0

我有一個真正的代碼優先部署我的數據模型,我需要執行通常會關聯到視圖的功能,但我不知道如何在時刻。在實體EF核心包含計算屬性

爲了簡化問題,我的模型包含註冊用戶創建的內容以及其他用戶可以與之交互的內容。我正在設計一個算法,說明這些東西是如何受歡迎的,這些算法歸結爲與交互類型相關的加權因子乘以某個日期時間的倒數。

舉一個例子,假設我們有「喜歡」,「評論」和「購買」,權重分別爲1,2和3。我們也說我的時間段是1周。在這種情況下,3周前購買的產品與當週的贊一樣多。

所以模型的相關部分是Thing它有一個主鍵和一些額外的元數據; InteractionType,它有一個主鍵,一個Weight值和一些其他元數據; 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當然是snark,但是當我說,我不確定是否有文件記錄的方式做到這一點,我只是想念它或什麼。我已經看到選擇按照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;} 
} 

我進行添加,遷移步驟,然後改變所產生的向上/向下的如下:

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()和這樣的作品,但我不知道這是否是正確的,因爲它感覺像這樣一個黑客...

+0

_an實體框架兼容way_將涉及C#類上的一些C#代碼。發佈(簡化)類開始。 –

+0

@HenkHolterman,我添加了一些更多的上下文和一些廣義的C#代碼,讓你知道我到目前爲止的內容。謝謝參觀。 – randcd

回答

2

的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解決方案可能並不全是壞的。

+0

我將不得不測試它看它是如何執行的。我寧願做這樣的事情,因爲所有的元素都駐留在代碼中,而不是我的解決方案,它只是利用EF中的弱引用。這裏的主要問題是,這會驅動人們瀏覽我的網站時看到的第一頁,因此它需要非常快速地加載。感謝您的回覆,我會爲DateDiff功能尋找NuGet包。 – randcd

+0

讓我們聽聽如何解決問題,加速明智。如果你不得不調整很多,也許會發佈一個自我回答。 –

+0

只是想補充一點,上面的查詢可以簡化爲ctx.Things.Select(x => x.ThingId).Take(10)因爲您沒有過濾,只是預測。包括也是無用的。 – SuperJMN