1. 基礎查詢
常見錯誤
- EF 不支持Last或LastOrDefault
- 量詞方法不支持實體類型的查詢陈哑,僅支持基本數(shù)據(jù)類型
量詞方法包括:Contains距潘、Any磕谅、Exists/Not Exists等查詢方法点楼。
- Cast只能對基本類型進行映射或EDM原始類型糖儡;OfType可以對實體類型進行映射,但是不支持基本類型映射
不管哪種映射贷币,需要映射的類型需要保持一種繼承關系击胜。也就是在EF中主要用于繼承映射。
下面例子中:ManCustomers 繼承自 Customer役纹,但是映射失敗了偶摔。這是由于cast不支持實體類型映射。
將映射類型改為基本類型:
將映射類型改為EDM類型:
OfType函數(shù)的轉(zhuǎn)換更加友好促脉,滿足條件才能進行轉(zhuǎn)換辰斋,起到過濾的作用,而Cast則是全盤轉(zhuǎn)換瘸味,若類型不匹配宫仗,則拋出異常。
- 分頁查詢常見問題
總結(jié):在使用EF進行分頁查詢時旁仿,未進行Order By排序的話锰什,會出現(xiàn)異常!EF不會自動生成Order By子句丁逝,并且上面的查詢會引發(fā)Not SupportedException異常汁胆。Order By 方法必須在Skip方法之前調(diào)用!
當調(diào)用Take且沒有調(diào)用skip時霜幼,將被翻譯成top語句
- Select 投影查詢嫩码,EF中不支持轉(zhuǎn)換為實體類型,僅支持轉(zhuǎn)換為原始類型罪既、匿名類型铸题。
2. 加載關聯(lián)數(shù)據(jù)
1. Lazy Loading(延遲加載)
顧名思義,延遲加載就是當我們需要時才加載琢感,當?shù)谝淮卧L問指向?qū)嶓w/實體屬性時丢间,實體或?qū)嶓w集合將自動從數(shù)據(jù)庫加載。訪問導航屬性時驹针,相關對象/子對象不會自動加載烘挫。當使用POCO(Plain Old CLR Objects )實體類型時,通過創(chuàng)建派生代理類型的實例柬甥,然后覆蓋virtual 的導航屬性從而實現(xiàn)延遲加載饮六。
默認情況下,Entity Framework 啟用延遲加載苛蒲,但是我們可以在EF上下文派生類中手動關閉延遲加載卤橄。
public EfDbContext() : base("server=.;database=createDbContext;uid=sa;pwd=123123")
{
Database.SetInitializer(new CreateDatabaseIfNotExists<EfDbContext>());
Configuration.LazyLoadingEnabled = false;//關閉延遲加載
}
延遲加載的數(shù)據(jù),只有真正使用的時候才會去查詢數(shù)據(jù)庫進行加載顯示臂外。
延遲加載需要注意問題:
- 循環(huán)引用
當A表中有B表的導航屬性窟扑,并且B表中由于A表的導航屬性喇颁,那么在進行延遲加載的時候就會出現(xiàn)序列化循環(huán)引用問題。
解決方法: 通過投影來解決序列化循環(huán)引用問題嚎货。
延遲加載的本質(zhì):只有當調(diào)用者使用時才會計算結(jié)果橘霎。
2. Eager Loading(饑餓加載)
查詢時提示,加載相關實體/子實體作為查詢的一部分厂抖,加載父對象時同時加載子對象茎毁,在EF中,我們可使用DbSet<T>.Include()方法來實現(xiàn)饑餓加載忱辅,和延遲加載不同的是七蜘,饑餓加載時,無論我們用火不用墙懂,利用Include()方法都會對子對象進行加載橡卤。
var customer=db.Customers.Include("Orders").FirstOrDefault();
其中Include()方法中的字符串參數(shù)是其關聯(lián)的導航屬性。
3. Explicitly Loading(顯示加載)
延遲執(zhí)行有延遲加載和顯示加載兩種方式损搬,即使我們僅用了延遲加載碧库,仍然可以通過顯示加載來延遲加載相關實體。通過DbEntityEntry<T>.Reference("").Load()加載實體巧勤,通過DbEntityEntry<T>.Collection("").Load()加載集合嵌灰。
顯示加載的本質(zhì):即使金庸延遲加載依然可以延遲執(zhí)行。
3. 原始查詢
EF底層還是Ado.net,對于一些復雜的查詢颅悉,我們還是需要利用原始查詢或者存儲過程來完成沽瞭。在EF中,我們可以通過SqlQuery方法來進行原始查詢剩瓶,SqlQuery方法有如下2中形式:
efDbContext.Database.SqlQuery<TElement>(string sql, params object[] parameters);
efDbContext.Customers.SqlQuery<TElement>(string sql, params object[] parameters);
-
在實體上執(zhí)行原始查詢
原始查詢只有當結(jié)果全部枚舉玩(也就是只有ToList后)才會與數(shù)據(jù)庫進行交互驹溃,否則將不會執(zhí)行查詢。
var c1=db.Database.SqlQuery<Customer>("select * from Customers").ToList();
c1.FirstOrDefault().Name = "c1";
db.SaveChanges();
var c2=db.Customers.SqlQuery("select * from Customers").ToList();
c2.Last().Name = "c2";
db.SaveChanges();
分析:
當我們在上下文構(gòu)造函數(shù)中關閉變更追蹤時延曙,第一種形式將不會更新到數(shù)據(jù)庫豌鹤,所以只是執(zhí)行查詢時,直接使用第一種SqlQuery實現(xiàn)即可枝缔。這是由于SqlQuery方法實體查詢是在數(shù)據(jù)庫Database上布疙,實體不會被上下文跟蹤。魂仍;第二種方法不但執(zhí)行了查詢而且還執(zhí)行了修改操作拐辽。這是由于SqlQuery方法實體查詢在上下文中的實體集合DbSet上,實體被上下文跟蹤擦酌。
利用SqlQuery查詢實體時必須返回所有列,否則將拋出異常菠劝,這是利用SqlQuery進行查詢的最大缺點赊舶。
-
返回自定義類型執(zhí)行原始查詢
-
在非實體上執(zhí)行原始查詢
ctx.Database.SqlQuery<TEntity>()
:SqlQuery方法支持返回單條記錄。
ctx.Set<TEntity>().SqlQuery()
:SqlQuery方法不支持返回單條記錄
-
傳遞參數(shù)執(zhí)行原始查詢
字符串拼接:
var id = 2;
var customers = db.Database.SqlQuery<Customer>($"select * from customers where id={id}").ToList();
參數(shù)化:
var name = "張三";
var parameters = new SqlParameter[]
{
new SqlParameter(){ParameterName="@id",SqlDbType=System.Data.SqlDbType.Int,Value=id}
};
var c = db.Database.SqlQuery<Customer>("select * from customers where id=@id",parameters).ToList();
-
利用原始查詢執(zhí)行存儲過程
var c = db.Database.SqlQuery<Customer>("GetCustomers");
-
利用原始查詢執(zhí)行存儲過程傳遞參數(shù)
var parameters = new SqlParameter("@id", 2);
var c = db.Database.SqlQuery<Customer>("GetCustomers",parameters);
注意:調(diào)用存儲過程時,如果數(shù)據(jù)庫時SqlServer2005笼平,就需要在存儲過程前加上Exec园骆,否則會拋出異常。
-
執(zhí)行增刪改操作
當需要執(zhí)行增刪改操作時寓调,我們需要使用Database對象上的ExecuteSqlCommand或者ExecuterSqlCommandAsync方法锌唾。和SqlQuery查詢一樣,ExecuteSqlCommand或者ExecuterSqlCommandAsync也有2個參數(shù)夺英。
string sql = @"INSERT INTO [dbo].[Customers]
([Name]
,[Email]
,[CreateTime]
,[ModifiedTime])
VALUES
(@name
,@email
,@CreateTime
,@ModifiedTime)";
var parameters = new SqlParameter[] {
new SqlParameter("@name","養(yǎng)老四"),
new SqlParameter("@email","324@21.com"),
new SqlParameter("@CreateTime","2019-04-06 10:42:29.243"),
new SqlParameter("@ModifiedTime","2019-04-06 10:42:29.243"),
};
db.Database.ExecuteSqlCommand(sql, parameters);