.NET——關(guān)于EF與延遲加載(LazyLoad)

  • 前一段獨(dú)立負(fù)責(zé)了一個(gè)web項(xiàng)目昂儒,由于自己一個(gè)人開(kāi)發(fā)辈末,技術(shù)選型也更加自由一些,因?yàn)轫?xiàng)目相對(duì)沒(méi)那么復(fù)雜孝偎,拋棄了公司一直沿用的IBatis.Net访敌,用了微軟自己的ORM框架——EF(Entity Framework),在使用的過(guò)程中遇到了些許問(wèn)題衣盾,發(fā)現(xiàn)了EF的延遲加載特性寺旺,在使用的過(guò)程中,深入研究了一下势决,正好結(jié)合一些基礎(chǔ)知識(shí)阻塑,做了些深入的學(xué)習(xí)。
  • 關(guān)鍵字:虛方法 多態(tài)

基本操作

由于項(xiàng)目使用的EF的Code First開(kāi)發(fā)方式果复,并且使用Fluent API配置實(shí)體類與表的映射陈莽,在OnModelCreating方法里通過(guò)modelBuilder.Configruations.AddFromAssembly(Assembly.GetExecutingAssembly())加載所有繼承自EntityTypeConfiguration<>的配置類為模型類進(jìn)行配置。

public class EFDbContext : DbContext
{
     public EFDbContext() : base("name = connstr"){}
     protected override void OnModelCreating(DbModelBuilder modelBuilder)
     {
        base.OnModelCreating(modelBuilder);
        modelBuilder.COnfigurations.AddFromAssembly(Assembly.GetExecutingAssembly());    
     }
}
public class Class
{
    public long Id { get; set; }
    public string Name { get; set; }
}
using System.Data.Entity.ModelConfiguration;
class ClassConfig: EntityTypeConfiguration<Class>
{
    public ClassConfig()
    {
        ToTable("T_Class");
    }
}
  • 當(dāng)多表之間需要配置主外鍵關(guān)系時(shí)虽抄,需要在多端進(jìn)行配置
public class Student
{
    public long Id { get; set; }
    public long ClassId { get; set; }
    public virtual Class Class { get; set; }
    public string Name { get; set; }
}
class StudentConfig : EntityTypeConfiguration<Student>
{
    public StudentConfig()
    {
        this.ToTable("T_Student");
        this.HasRequired(e => e.Class).WithMany().HasForeignKey(e => e.ClassId);
    }
}

調(diào)用情況如下

using (EFContext ctx = new EFContext())
{
    Student s = ctx.Students.First();
    Console.WriteLine(s.SName);
    Console.WriteLine(s.Class.ClassName);
}

打印一下sql調(diào)用過(guò)程和結(jié)果如下

EF執(zhí)行SQL:Opened connection at 2019/4/26 9:58:03 +08:00

EF執(zhí)行SQL:SELECT TOP (1)
    [c].[Id] AS [Id],
    [c].[ClassId] AS [ClassId],
    [c].[SName] AS [SName]
    FROM [dbo].[T_Student] AS [c]
EF執(zhí)行SQL:

EF執(zhí)行SQL:-- Executing at 2019/4/26 9:58:03 +08:00

EF執(zhí)行SQL:-- Completed in 0 ms with result: SqlDataReader

EF執(zhí)行SQL:

EF執(zhí)行SQL:Closed connection at 2019/4/26 9:58:03 +08:00

小王
EF執(zhí)行SQL:Opened connection at 2019/4/26 9:58:03 +08:00

EF執(zhí)行SQL:SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[ClassName] AS [ClassName]
    FROM [dbo].[T_Class] AS [Extent1]
    WHERE [Extent1].[Id] = @EntityKeyValue1
EF執(zhí)行SQL:

EF執(zhí)行SQL:-- EntityKeyValue1: '1' (Type = Int64, IsNullable = false)

EF執(zhí)行SQL:-- Executing at 2019/4/26 9:58:03 +08:00

EF執(zhí)行SQL:-- Completed in 1 ms with result: SqlDataReader

EF執(zhí)行SQL:

EF執(zhí)行SQL:Closed connection at 2019/4/26 9:58:03 +08:00

三年二班

通過(guò)以上操作可以發(fā)現(xiàn)走搁,當(dāng)分別打印Student和Class的字段時(shí),會(huì)兩次調(diào)用SQL語(yǔ)句迈窟,這就是EF的延遲加載私植。

深入延遲加載

延遲加載是EF的默認(rèn)特性,在平時(shí)調(diào)用時(shí)车酣,只有通過(guò)循環(huán)或者類似ToList()操作時(shí)才會(huì)真正操作SQL語(yǔ)句曲稼。而在Student實(shí)體類中索绪,當(dāng)需要添加Class作為外鍵時(shí),需要添加public virtual Class class{get;set;}躯肌,如果沒(méi)有添加virtual會(huì)怎么樣呢者春?

EF執(zhí)行SQL:Opened connection at 2019/4/26 10:20:48 +08:00

EF執(zhí)行SQL:SELECT TOP (1)
    [c].[Id] AS [Id],
    [c].[ClassId] AS [ClassId],
    [c].[SName] AS [SName]
    FROM [dbo].[T_Student] AS [c]
EF執(zhí)行SQL:

EF執(zhí)行SQL:-- Executing at 2019/4/26 10:20:48 +08:00

EF執(zhí)行SQL:-- Completed in 4 ms with result: SqlDataReader

EF執(zhí)行SQL:

EF執(zhí)行SQL:Closed connection at 2019/4/26 10:20:48 +08:00

小王

未經(jīng)處理的異常:  System.NullReferenceException: 未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例。
   在 ConsoleApp.Program.Main(String[] args) 位置 D:\Work\Code\ConsoleApp\ConsoleApp\Program.cs:行號(hào) 37

可以發(fā)現(xiàn)清女,當(dāng)SQL執(zhí)行完Student的訪問(wèn)后钱烟,去訪問(wèn)Student里面的Class,然后就報(bào)錯(cuò)了未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例嫡丙。

簡(jiǎn)單分析一下拴袭,就很容易發(fā)現(xiàn),在Student類中只是定義了一下Class類型的Class字段曙博,可是用沒(méi)有賦值拥刻,沒(méi)有初始化對(duì)象,直接調(diào)用父泳,肯定會(huì)報(bào)錯(cuò)般哼,可是為什么加上virtual就可以了呢?

  • 繼續(xù)深入
Student s = ctx.Students.First();
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
Console.WriteLine(s.GetType());
Console.WriteLine(s.Class.GetType());

控制臺(tái):
System.Data.Entity.DynamicProxies.Student_E7E50A6894411395155465B3AA4894D4DAE31B725BE1AD63AA27469E722AB9D3
ConsoleApp.Class

通過(guò)分別打印s和s.Class的GetType()可以發(fā)現(xiàn)惠窄,拿到的對(duì)象其實(shí)是Student子類的對(duì)象蒸眠,因此EF其實(shí)是動(dòng)態(tài)生成了實(shí)體類對(duì)象的子類,然后override了這些virtual的屬性杆融,在內(nèi)部進(jìn)行操作楞卡,將Student的Class屬性進(jìn)行賦值,這樣就解釋的通了脾歇。

virtual和override

EF的延遲加載其實(shí)用到了C#的多態(tài)性蒋腮,通過(guò)方法重寫(xiě)和虛方法的調(diào)用,實(shí)現(xiàn)了根據(jù)不同的需要藕各,分別加載數(shù)據(jù)池摧,之前閱讀過(guò)一本書(shū)《net 4.0面向?qū)ο缶幊搪劇罚渲杏幸粋€(gè)章節(jié)就非常形象的介紹了C#的這一特性激况。

class Parent
{
    public void HideF()
    {
        Console.WriteLine("Parent.HideF()");
    }
}

class Parent : Parent
{
    public void HideF()
    {
        Console.WriteLine("Child.HideF()");
    }
}
當(dāng)子類和父類都擁有了一個(gè)完全相同的方法HideF险绘,問(wèn)題發(fā)生了:
Child c = new Child();
c.HideF();      //輸出:Child.HideF()
修改一下代碼:
Parent p = new Parent();
p.HideF();     //輸出:Parent.HideF()

由此得出結(jié)論:當(dāng)分別位于父類和子類兩個(gè)方法完全一樣時(shí),調(diào)用哪個(gè)方法由對(duì)象變量的類型決定誉碴。

繼續(xù)測(cè)試:
Parent p = new Child();
p.HideF();     //輸出:Parent.HideF()

繼續(xù)得出結(jié)論:當(dāng)分別位于父類和子類的兩個(gè)方法完全一樣時(shí)宦棺,調(diào)用哪個(gè)方法由對(duì)象變量的編譯時(shí)類型決定。
如果確實(shí)希望調(diào)用的是子類的方法黔帕,應(yīng)先進(jìn)行強(qiáng)制類型轉(zhuǎn)換:
((Child)p).HideF();    //輸出:Child.HideF()

所以代咸,如果父類和子類方法重名,應(yīng)該子類同名方法前加上new關(guān)鍵字public new void HideF(){}成黄,
如果要調(diào)用父類的同名方法呐芥,可以使用base.HideF();

由于子類隱藏了父類的通過(guò)名方法逻杖,如果不進(jìn)行強(qiáng)制轉(zhuǎn)換,就無(wú)法通過(guò)父類變量直接調(diào)用子類的同名方法思瘟,哪怕父類變量引用的是子類對(duì)象荸百。

為了達(dá)到這個(gè)目的,需要在父類同名方法前加關(guān)鍵字virtual滨攻,表明這是一個(gè)虛方法够话,子類可以重寫(xiě)此方法。與此同時(shí)光绕,需要在子類同名方法錢(qián)加關(guān)鍵字override女嘲,表明對(duì)父類同名方法進(jìn)行重寫(xiě)。

  • 修改之后的結(jié)果
Child c = new child();
Parent p = c;
p.HideF();    //輸出:Child.HideF()

這一示例表明:將父類方法定義為虛方法诞帐、子類重寫(xiě)同名方法后欣尼,通過(guò)父類變量調(diào)用此方法,到底調(diào)用父類還是子類的方法停蕉,由父類變量引用的真實(shí)對(duì)象類型決定愕鼓,而與父類變量無(wú)關(guān)。

可見(jiàn)慧起,根據(jù)C#的虛方法調(diào)用特性菇晃,可以在同樣的語(yǔ)句下,通過(guò)引用不同的類型子類對(duì)象完慧,就可以實(shí)現(xiàn)不同的功能谋旦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剩失,一起剝皮案震驚了整個(gè)濱河市屈尼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拴孤,老刑警劉巖脾歧,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異演熟,居然都是意外死亡鞭执,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)芒粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)兄纺,“玉大人,你說(shuō)我怎么就攤上這事化漆」来啵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵座云,是天一觀的道長(zhǎng)疙赠。 經(jīng)常有香客問(wèn)我付材,道長(zhǎng),這世上最難降的妖魔是什么圃阳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任厌衔,我火速辦了婚禮,結(jié)果婚禮上捍岳,老公的妹妹穿的比我還像新娘富寿。我一直安慰自己,他們只是感情好祟同,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布作喘。 她就那樣靜靜地躺著,像睡著了一般晕城。 火紅的嫁衣襯著肌膚如雪泞坦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天砖顷,我揣著相機(jī)與錄音贰锁,去河邊找鬼。 笑死滤蝠,一個(gè)胖子當(dāng)著我的面吹牛豌熄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播物咳,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼锣险,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了览闰?” 一聲冷哼從身側(cè)響起芯肤,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎压鉴,沒(méi)想到半個(gè)月后崖咨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡油吭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年击蹲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婉宰。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歌豺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出心包,到底是詐尸還是另有隱情类咧,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站轮听,受9級(jí)特大地震影響骗露,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜血巍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一萧锉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧述寡,春花似錦柿隙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至螟炫,卻和暖如春波附,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昼钻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工掸屡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人然评。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓仅财,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親碗淌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盏求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容