天天看點

FreeSql.Repository (八)級聯加載

歡迎來到《FreeSql.Repository 倉儲模式》系列文檔,本系列文檔專注介紹 【倉儲+工作單元】 的使用方式。完整文檔請前往 wiki 中心:https://github.com/dotnetcore/FreeSql/wiki

前面說到,倉儲模式推薦使用導航屬性,聯表查詢、子表查詢都已得到解決,本文繼續配置導航屬性之後,多表查詢如何傳回資料子表、主表資料。

ManyToOne

ManyToOne 聯表查詢傳回資料上篇已經說到,這裡重溫一下:

var repo = fsql.GetRepository<Topic>();
var topics = repo.Select.Include(a => a.Type).ToList();
string typeName = topics[0].Type.Name;
           

執行的SQL:

select
topic.*, type.name
from topic
inner join type on type.id = topic.typeid
           

思考:ToList 預設傳回 topic.* 和 type.* 不對,因為當 Topic 下面的導航屬性有很多的時候,每次都傳回所有導航屬性?

于是:ToList 的時候隻會傳回 Include 過的,或者使用過的 N對1 導航屬性字段。

  • repo.Select.ToList(); 傳回 topic.*
  • repo.Select.Include(a => a.Type).ToList(); 傳回 topic.* 和 type.*
  • repo.Where(a => a.Type.name == "c#").ToList(); 傳回 topic.* 和 type.*,此時不需要顯式使用 Include(a => a.Type)
  • repo.Select.ToList(a => new { Topic = a, TypeName = a.Type.name }); 傳回 topic.* 和 type.name

OneToMany

提示:ManyToMany 多對多隻要配置好了,用法與 OneToMany 一樣

1對N,和 N對1 是反過來看

topic 相對于 type 是 N對1

type 相對于 topic 是 1對N

class Topic
{
    [Column(IsIdentity = true)]
    public int id { get; set; }

    public int typeid { get; set; }
    [Navigate(nameof(typeid))]
    public Type Type { get; set; }
}
class Type
{
    [Column(IsIdentity = true)]
    public int id { get; set; }
    public string name { get; set; }

    [Navigate(nameof(Topic.typeid))]
    public List<Topic> Topics { get; set; }
}
           

把 Type.name 為 c# java php,以及它們的 topic 查詢出來:

var repo = fsql.GetRepository<Type>();
var types = repo.Select
   .IncludeMany(a => a.Topics)
   .Where(a => new { "c#", "java", "php" }.Contains(a.name))
   .ToList();
           
[
{
  name : "c#",
  Topics: [ 文章清單 ]
}
...
]
           

這種方法是從 Type 方向查詢的,非常符合使用方的資料格式要求。

最終是分兩次 SQL 查詢資料回來的,大概是:

select * from type where name in ('c#', 'java', 'php')
select * from topics where typeid in (上一條SQL傳回的id)
           

舉一反三:

IncludeMany 級聯查詢,在實際開發中,還可以 IncludeMany(a => a.Topics, then => then.IncludeMany(b => b.Comments))

假設,還需要把 topic 對應的 comments 也查詢出來。最多會産生三條SQL查詢:

select * from type where name in ('c#', 'java', 'php')
select * from topic where typeid in (上一條SQL傳回的id)
select * from comment where topicid in (上一條SQL傳回的id)
           

思考:這樣級聯查詢其實是有缺點的,比如 c# 下面有1000篇文章,那不是都傳回了?

IncludeMany(a => a.Topics.Take(10))
           

這樣就能解決每個分類隻傳回 10 條資料了,這個功能 ef/efcore 目前做不到,直到 efcore 5.0 才支援,這可能是很多人忌諱 ef 導航屬性的原因之一吧。幾個月前我測試了 efcore 5.0 sqlite 該功能是報錯的,也許隻支援 sqlserver。而 FreeSql 沒有資料庫種類限制,還是那句話:都是親兒子!

關于 IncludeMany 還有更多功能請到 github wiki 文檔中了解。

系列文章導航

  • (一)什麼是倉儲
  • (二)如何使用倉儲
  • (三)實體特性
  • (四)工作單元
  • (五)狀态管理
  • (六)導航屬性
  • (七)多表查詢
  • (八)級聯加載
  • (九)級聯儲存
  • (十)動态實體類型
  • (十一)分表
  • (十二)如何擴充

繼續閱讀