C# 數(shù)據(jù)操作系列 - 8. EF Core的增刪改查

0.前言

到目前為止,我們看了一下如何聲明EF Core的初步使用,也整體的看了下EF Core的映射關(guān)系配置以及導(dǎo)航屬性的配置。

這一篇,我?guī)Т蠹曳窒硪幌?,我在工作中需要的EF Core的用法。

1. 初始化

在實(shí)際開(kāi)發(fā)中,一般都是先設(shè)計(jì)好數(shù)據(jù)表再進(jìn)行開(kāi)發(fā),所以很少用到EF Core的數(shù)據(jù)遷移功能。所以EF Core的初始化,一般也指的是EF Core上下文初始化。

1.1 連接字符串

我們通過(guò)前面的文章知道,EF Core在上下文初始化的時(shí)候,都需要一個(gè)鏈接字符串。如果在不考慮后續(xù)變更或者上下文的復(fù)用性,可以直接在自定義Context里重寫(xiě)OnConfiguring方法中定義。

如果需要后續(xù)變更,那么就需要在創(chuàng)建自定義EF Core 上下文類(lèi)的時(shí)候,為之添加一個(gè)連接字符串的屬性或者字段,以方便初始化的時(shí)候指定。實(shí)例:

public class DefaultContext : DbContext
{
    private string Connection { get; set; } = "Data Source=./blogging1.db";
    public DefaultContext(string connection)
    {
        Connection = connection;
    }
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite(Connection);
}

這樣一來(lái),我們?cè)诤罄m(xù)使用的時(shí)候,就可以指定連接字符串了。當(dāng)然了,如果有小伙伴有更好的方法也可以分享出來(lái)呀。

1.2 配置文件的加載或者實(shí)體對(duì)象的托管

如果我們不使用配置文件的話,就必須在EF Core的上下文類(lèi)里添加一個(gè)類(lèi)型是DbSet<T>的屬性。繼續(xù)延續(xù)上面的實(shí)例:

public class SingleModel
{
    public int Id { get; set; }
    public int TargetId { get; set; }
    public SingleTargetModel SingleTarget { get; set; }
}

public class SingleTargetModel
{
    public int Id { get; set; }
    public SingleModel Single { get; set; }
}
public class DefaultContext : DbContext
{
    // 其余代碼參見(jiàn) 1.1  DefaultContext
    public DbSet<SingleModel> Singles { get; set; }
    public DbSet<SingleTargetModel> Targets { get; set; }
}

以上也就是這一小節(jié)標(biāo)題中的實(shí)體對(duì)象的托管。我沒(méi)找到EF Core官方文檔中對(duì)于這種方式的稱呼,所以我就悄悄的搶注了一下為托管。

如果我們使用Config類(lèi)(也就是 《C# 數(shù)據(jù)操作系列 - 7. EF Core 導(dǎo)航屬性配置》中介紹的配置類(lèi))的話,需要在EF Core中應(yīng)用配置,具體是:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfiguration(new SingleModelConfig());
    modelBuilder.ApplyConfiguration(new SingleTargeModelConfig());
}

在使用的時(shí)候,可以直接:

var context = new DefaultContext("Data Source=demo.db");
var t1 = context.Set<SingleTargetModel>().First();

即使用DbContext.Set<T>,可以獲取到一個(gè)數(shù)據(jù)加載集,當(dāng)然也可以結(jié)合實(shí)體類(lèi)的托管來(lái)一起使用。

那么為什么,我推薦使用配置類(lèi)加載嗎?

因?yàn)樵趯?shí)際開(kāi)發(fā)中,一個(gè)完整的程序或者網(wǎng)站實(shí)體類(lèi)都會(huì)大于10,而這些如果使用屬性的形式會(huì)非常多,不利于實(shí)際開(kāi)發(fā)。而且,EF Core可以通過(guò) Assembly 方式整體加載配置文件。再者,為了保證ORM中的O不受其他因素的影響。也就是說(shuō),如果使用注解形式配置映射關(guān)系,那么勢(shì)必會(huì)造成影響。

當(dāng)然了,使用配置文件必然會(huì)導(dǎo)致項(xiàng)目的類(lèi)增多,而且大量的重復(fù)類(lèi)可能會(huì)出現(xiàn)。當(dāng)然了,如果考慮到這個(gè)問(wèn)題的話,可以試試寫(xiě)一個(gè)項(xiàng)目代碼生成器哦,專門(mén)用來(lái)處理這些差不多的類(lèi)。

咳咳,總而言之,使用配置文件利大于弊,所以我推薦使用配置文件對(duì)關(guān)系進(jìn)行配置。

2. 數(shù)據(jù)變化

換句話說(shuō),嗯,也就是增刪改。在數(shù)據(jù)增刪這兩方面,EF Core沒(méi)有太多需要注意的地方。不過(guò)如果有導(dǎo)航屬性的話,在新增的時(shí)候,EF Core會(huì)自動(dòng)檢索導(dǎo)航屬性的另一端是否需要新增到數(shù)據(jù)庫(kù)中,如果需要新增的話,EF Core會(huì)自動(dòng)標(biāo)記為新增的。

而刪除,如果在配置導(dǎo)航屬性時(shí),沒(méi)有設(shè)置級(jí)聯(lián)刪除,刪除當(dāng)前元素,如果另一端的外鍵是可空類(lèi)型的,并不會(huì)刪除導(dǎo)航屬性另一端的元素只會(huì)設(shè)置外鍵指向?yàn)镹ULL,如果另一端外鍵是不可空的,那么就會(huì)同時(shí)刪除。如果需要修改,可以使用以下方法修改,在配置導(dǎo)航屬性的時(shí)候:

OnDelete(DeleteBehavior.Cascade);

對(duì)于可為NULL的外鍵來(lái)說(shuō),枚舉DeleteBehavior的值起以下作用:

行為名稱 對(duì)內(nèi)存中的依賴項(xiàng)/子項(xiàng)的影響 對(duì)數(shù)據(jù)庫(kù)中的依賴項(xiàng)/子項(xiàng)的影響
Cascade 刪除實(shí)體 刪除實(shí)體
ClientSetNull(默認(rèn)) 外鍵屬性設(shè)置為 null None
SetNull 外鍵屬性設(shè)置為 null 外鍵屬性設(shè)置為 null
Restrict None None

而對(duì)于不可為NULL的外鍵來(lái)說(shuō),枚舉DeleteBehavior的值起以下作用:

行為名稱 對(duì)內(nèi)存中的依賴項(xiàng)/子項(xiàng)的影響 對(duì)數(shù)據(jù)庫(kù)中的依賴項(xiàng)/子項(xiàng)的影響
Cascade(默認(rèn)) 刪除實(shí)體 刪除實(shí)體
ClientSetNull SaveChanges 引發(fā)異常 None
SetNull 引發(fā) SaveChanges SaveChanges 引發(fā)異常
Restrict None None

而對(duì)于數(shù)據(jù)的修改,EF Core的做法是通過(guò)監(jiān)控實(shí)體的ChangeTracker來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)實(shí)體的狀態(tài)更新。也就是說(shuō),如果你從EF Core的上下文獲取了一個(gè)實(shí)體對(duì)象,對(duì)這個(gè)對(duì)象的某些值進(jìn)行了修改。這時(shí)候EF Core其實(shí)已經(jīng)記錄了這個(gè)對(duì)象的修改。不需要我們額外的調(diào)用修改方法(因?yàn)楦緵](méi)有Update方法)。

EF Core在我們調(diào)用 SaveChanges 會(huì)把緩存的所有更改(增、刪、改)都推送給數(shù)據(jù)庫(kù)。如果有一條數(shù)據(jù)變更因?yàn)閿?shù)據(jù)庫(kù)校驗(yàn)或者其他約束沒(méi)有通過(guò),就會(huì)報(bào)錯(cuò),同時(shí)撤銷(xiāo)所有已推送的變更并取消后續(xù)變更的推送。

從數(shù)據(jù)庫(kù)的角度來(lái)看,EF Core在SaveChanges的過(guò)程中是以事務(wù)的形式推送給數(shù)據(jù)庫(kù)的。如果出錯(cuò),那么事務(wù)就會(huì)回滾。

所以一般情況下,EF不需要開(kāi)啟事務(wù)。

3.花樣查詢

EF Core 支持Linq查詢,所以在查詢的時(shí)候可以使用Linq進(jìn)行。簡(jiǎn)單示例如下:

var context = new DefaultContext("Data Source=demo.db");
var results = from t in context.Set<SingleTargetModel>()
             select t;

當(dāng)然,也可以使用方法鏈的形式傳入一個(gè)Expression<Func<T,bool>> 類(lèi)型的表達(dá)式。

var results = context.Set<SingleTargetModel>().Where(t=>true);

看到這里了,可能會(huì)有疑問(wèn)了,這明明很簡(jiǎn)單呀。是的,如果只是查詢,自然簡(jiǎn)單。

那么,結(jié)合排序、分頁(yè)之后呢?先來(lái)看看排序是怎么實(shí)現(xiàn)的吧。

在查詢表達(dá)式寫(xiě)法中,排序應(yīng)該這樣的寫(xiě)的:

var results = from t in context.Set<SingleTargetModel>()
            orderby t.Id //descending 如果降序則取消注釋
            select t ;

方法鏈的形式是:

var results = context.Set<SingleTargetModel>().Where(t=>true).OrderBy(t=>t.Id);

分頁(yè)只能通過(guò)方法鏈的形式進(jìn)行分頁(yè),這里提供一個(gè)分頁(yè)的工具方法:

public static IQueryable<T> Paging<T>(IQueryable<T> source,int pageIndex,int pageSize)
{
    return source.Skip(pageSize * (pageIndex - 1)).Take(pageSize);
}

這里用到的是 Skip(int count) 表示忽略數(shù)據(jù)集的前count條記錄,Take(int count)取得數(shù)據(jù)集的前count條記錄。

EF Core在調(diào)用 ToList的時(shí)候,會(huì)將已調(diào)用的方法和Linq轉(zhuǎn)換成SQL語(yǔ)句,并正式向數(shù)據(jù)庫(kù)發(fā)起查詢。如果出現(xiàn)了在Linq中調(diào)用三方方法或者自己寫(xiě)的工具方法的話,可能會(huì)提示不受支持。

如果使用的Linq表達(dá)式,則沒(méi)關(guān)系,EF Core在遇到這種情況的時(shí)候,會(huì)把數(shù)據(jù)庫(kù)里所有數(shù)據(jù)都加載到上下文中,再執(zhí)行后續(xù)的查詢等操作。

所以,為了高效的查詢,在執(zhí)行查詢的時(shí)候,最好使用簡(jiǎn)單的查詢條件。

4. 后續(xù)

EF Core整體使用已經(jīng)介紹完了,當(dāng)然照例是普通工程級(jí)的內(nèi)容。下一篇我給大家介紹一下EF Core剩下一些邊角料,嗯。如果要深挖代碼的話,得以后有機(jī)會(huì)了。

數(shù)據(jù)訪問(wèn)系列,EF Core 篇即將到一段落。待EF Core篇完成后,將帶領(lǐng)一起去探索 Nhibernate和Dapper,SqlSugar這三個(gè)ORM框架。

更多內(nèi)容煩請(qǐng)關(guān)注我的博客《高先生小屋》

file
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容