EntityFramework使用及優化
1、 簡介
- ORM框架:Object Relation Mapping,用操作物件的方式來運算元據庫
- 其它框架:
Dapper
、NHibernate
,首推EF,微軟官方的。 - EF底層還是
ADO.NET
實現的。 - EF支援
SqlServer
、MySQL
、Oracle
等主流資料庫 - 使用EF開發資料庫有兩種形式:先建資料庫or先建模型類
- 三種模式:
DataBase First
資料庫優先;先建資料庫表結構,生成EDM檔案
Model First
模型優先;沒啥用
Code First
:程式碼優先;開發者自己寫模型生成資料庫,沒有EDM檔案DataBase First:簡單、方便,但如果是大專案後期會非常痛苦,會出現諸如:修改了資料庫在程式中更新EF不起作用等這類問題。
Code First:門檻高,但適合大專案,微軟主推;資料庫由EF幫助生成,當修改模型後,EF使用
DB Miguration
自動幫助修改資料庫,但也可以禁用Miguration
,手動建立(推薦)- EF是採用約定大於配置的框架原則的,能遵守約定的就不要去配置
2、EF的安裝
①程式中右鍵新建項
②通過NuGet
程式集:Install-Package EntityFramework
會自動在App.config
中增加兩個entityFramework配置段,如果是MySql
資料庫還需要新增相關的MySql
的驅動。
在Web.config
中配置連線字串
<connectionStrings>
<add name="connStr" connectionString="Data source=.;initial catalog=School;user id=sa;password=***;" providerName="System.Data.SqlClient" />
</connectionStrings>
3、EF簡單實體配置DataAnnotations
資料庫建表T_Persons
程式中實體類Person
類
[Table("T_Persons")]//表名與類名不一樣,所以需要特性標註
public class Person{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateDateTime { get; set; }
}
備註:因為EF約定主鍵欄位時Id,所以不用再特殊指定Id是主鍵;主鍵必須是Id,可以配置但不推薦
常用特性:必填欄位[Required]
、限制欄位長度[MaxLength(5)]
、欄位在資料庫中有預設值[DatabaseGenerated]
、可空欄位int?
用問號修飾、欄位需要用public
//建立實體類
public class TestDbContext:DbContext{
public TestDbContext()
: base("name=connStr")
{
}
//通過對Persons操作就可以完成對T_Persons表的操作
public DbSet<Person> Persons { get; set; }
}
是否using的爭議
不using也沒問題,但其實using更好。推薦using
異常處理:
InnerException
4、EF模型的兩種配置方式
①DataAnnotaions
;方便,偶爾度高,上邊那種
②FluentAPI
;微軟推薦使用的
5、FluentAPI
使用
①sql中建表
②C#建模型類
③建立PersonConfig
類
using System.Data.Entity.ModelConfiguration;
public class PersonConfig:EntityTypeConfiguration<Person>{
public PersonConfig() {
this.ToTable("T_Persons");
}
}
④建立DbContext
類
public class MyContext:DbContext{
public MyContext() : base("name=connStr") {
}
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder){
base.OnModelCreating(modelBuilder);
//當前程式碼所在程式集,載入所有的繼承自EntityTypeConfiguration為模型配置類 modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
}
}
當存在多表時,只要新增多個實體類與DbSet
就可以了
6.、EF的原理及sql監控
EF會自動把where
、OrderBy
、Select
等這些編譯成“表示式樹(Expression Tree
)”,然後會把表示式樹翻譯成SQL
語句執行。因此不是把資料都取到記憶體中,然後使用集合的方式進行資料過濾,因此效能不會低。但如果這個操作不能被翻譯成sql語句,則會報錯,或者被放在記憶體中操作,效能就會很低。
檢視真正執行的sql是什麼樣的
MyContext ctx = new MyContext();
ctx.Database.Log = (sql) =>{
Console.WriteLine("=============Log============" + sql);
};
EF的延遲執行
只有遍歷結果集的時候才執行select查詢,ToList()內部也是遍歷結果集形成List
各種語句編譯後的結果:
Person p=ctx.Persons.First();
p.Name = "hello word";
ctx.SaveChanges();
Log:
EF很智慧,知道我只是更新Name
欄位,而且主動的幫我找到ID;
值得一提的是如果你重複執行語句,那麼EF也很聰明的不會執行該update語句…
ctx.Persons.Where(o => o.Name.StartsWith("baidu")).ToList();
var result = ctx.Persons.Where(p => p.Name.Contains("com"));
則是%com%
var result = ctx.Persons.Where(p => p.Name.Length > 5).ToList();
該語句生成的sql相對複雜些,而且欄位上涉及到了資料型別轉換(將得到的欄位值轉換成int型別再比較),它的執行效率就比較低。
var result = ctx.Persons.Where(p => p.CreateDateTime > DateTime.Now).ToList();
long[] ids = { 2, 5, 6 };
var result = ctx.Persons.Where(p => ids.Contains(p.Id)).ToList();
EF很強大,自動幫助拼接了字串
IEnumerable<Person> p = ctx.Persons.Where(o => o.Id > 3);
p=p.Where(o => o.Name.Length > 3);
p.ToList();
第一次獲得IEnumerable<Person> p
時,已經把資料載入入記憶體了,又在記憶體中操作效率就很低。
IQueryable<Person> p = ctx.Persons.Where(o => o.Id > 3);
p=p.Where(o => o.Name.Length > 3);
p.ToList();
使用IQueryable<Person>
物件時,所有的操作都是在資料庫中執行的,最後返回給程式
如果使用IEnumerable<Person>
時,是把所有id>3
的都載入到記憶體中又Linq to Object
查詢了一次這樣非常不好。
EF 是跨資料庫的,如果遷移到 MYSQL
上, 就會翻譯成 MYSQL
的語法。要配置對應資料庫的Entity Framework Provider
細節:
每次開始執行的__MigrationHistory
等這些 SQL 語句是什麼?
是 DBMigration
用的,也就是由 EF 幫我們建資料庫,現在用不到,
通過下面的程式碼禁用:
Database.SetInitializer<XXXDbContext>(null);
XXXDbContext
就是專案 DbContext
的類名。一般建議放到 XXXDbContext
建構函式中。
注意這裡的 Database
是 System.Data.Entity
下的類,不是 DbContext
的 Database
屬性。如果寫到 DbContext
中,最好用上全名,防止出錯。
7、EF執行sql
在一些特殊場合,需要執行原生 SQL。比如Sqlserver
有一些特有的函式,EF就無法使用語句生成,因為EF是垮資料庫的,不能支援所有的特性。
執行非查詢語句,呼叫 DbContext
的 Database
屬性的ExecuteSqlCommand
方法,可以通過佔位符的方式傳遞引數:
ctx.Database.ExecuteSqlCommand("update T_Persons set Name={0},CreateDateTime=GetDate()",
"baidu.com");
佔位符的方式不是字串拼接,經過觀察生成的 SQL 語句,發現仍然是引數化查詢,因此不會有 SQL 注入漏洞。
執行查詢:
var q1 = ctx.Database.SqlQuery<Item1>("select Name,Count(*) Count from T_Persons where Id>{0} and
CreateDateTime<={1} group by Name",2, DateTime.Now); //返回值是 DbRawSqlQuery<T> 型別,也是實現了 IEnumerable 介面
類似於 ExecuteScalar 的操作比較麻煩:
int c = ctx.Database.SqlQuery<int>("select count(*) from T_Persons").SingleOrDefault();
8、EF中不是所有lambda寫法都能被支援
var result = ctx.Persons.Where(p => Convert.ToString(p.Id)=="3");
出現“System.NotSupportedException
”異常一般就說明你的寫法無法翻譯成 SQL 語句。
想獲取建立日期早於當前時間一小時以上的資料:
var result = ctx.Persons.Where(p => (DateTime.Now - p.CreateDateTime).TotalHours>1);
同樣也可能會報System.ArgumentException
型別的未經處理的異常
也就是說在EF使用lambda
表示式中不能發生,計算or型別轉換操作
EF中提供了一個SqlServer
專用的類,SqlFunctions
,這個方法只對Sqlserver
資料庫支援,對於在EF不支援的函式提供支援;
比如:
var result = ctx.Persons.Where(p =>
SqlFunctions.DateDiff("hour",p.CreateDateTime,DateTime.Now)>1);
9、EF物件狀態管理
為什麼查詢出來的物件 Remove()
、再 SaveChanges()
就會把資料刪除。 而自己 new
一個Person()
物件,然後 Remove()
不行?
為什麼查詢出來的物件修改屬性值後、再 SaveChanges()
就會把資料庫中的資料修改。
因為 EF
會跟蹤物件狀態的改變。
EF
中物件有五個狀態:
Detached
(遊離態,脫離態) 、
Unchanged
(未改變) 、Added
(新增) 、Deleted
(刪除) 、 Modified
(被修改)
狀態的裝換:
Add()
、 Remove()
修改物件的狀態。 所有狀態之間幾乎都可以通過: Entry(p).State=xxx
的方式進行強制狀態轉換。狀態改變都是依賴於 Id 的( Added
除外)
應用:
當 SavaChanged()
方法執行期間,會檢視當前物件的 EntityState
的值,決定是去新增( Added
)、修改( Modified
)、刪除( Deleted
)或者什麼也不做( UnChanged
)。
10、EF優化的一個技巧
如果查詢出來的物件只是供顯示使用,不會修改、刪除後儲存,那麼可以使用
AsNoTracking()
來使得查詢出來的物件是 Detached
狀態,這樣對物件的修改也還是 Detached
狀態, EF
不再跟蹤這個物件狀態的改變,能夠提升效能。
var p1 = ctx.Persons.Where(p => p.Name == "baidu.com").FirstOrDefault();
Console.WriteLine(ctx.Entry(p1).State);
//改成:
var p1 = ctx.Persons.AsNoTracking().Where(p => p.Name == "baidu.com").FirstOrDefault();
Console.WriteLine(ctx.Entry(p1).State);
因為 AsNoTracking()
是 DbQuery
類( DbSet
的父類)的方法,所以要先在 DbSet
後呼叫AsNoTracking()
如果確實還想再更新,ctx.Entry().State=System.Data.Entity.EntityState.Unchanged;
後在更新即可。
相關文章
- EntityFramework 優化建議Framework優化
- EntityFramework優化:查詢WITH(NOLOCK)Framework優化
- EntityFramework優化:查詢效能Framework優化
- EntityFramework優化:第一次啟動優化Framework優化
- MySQL 索引使用策略及優化MySql索引優化
- Hive使用Calcite CBO優化流程及SQL優化實戰Hive優化SQL
- 你真的瞭解EF嗎?關於EntityFramework的高階優化Framework優化
- 使用Android Profile做效能分析及優化Android優化
- 2-VI–ListView的基本使用及優化View優化
- Web 效能優化:理解及使用 JavaScript 快取Web優化JavaScript快取
- 【譯】Web 效能優化:理解及使用 JavaScript 快取Web優化JavaScript快取
- 快速排序及優化排序優化
- Nginx 優化及原理Nginx優化
- Tomcat部署及優化Tomcat優化
- EntityFramework Core不得不注意的效能優化意外收穫,你會用錯?Framework優化
- Entityframework MigrationsFramework
- MySQL 規範及優化MySql優化
- mysql索引原理及優化MySql索引優化
- 氣泡排序及優化排序優化
- 【前端優化】js圖片懶載入及優化前端優化JS
- 效能優化之關於畫素管道及優化(二)優化
- Oracle優化案例-使用with as優化Subquery Unnesting(七)Oracle優化
- SQL優化案例-使用with as優化Subquery Unnesting(七)SQL優化
- TiDB 查詢優化及調優系列(一)TiDB 優化器簡介TiDB優化
- nginx部署及簡單優化Nginx優化
- MySQL函式索引及優化MySql函式索引優化
- Mysql慢SQL分析及優化MySql優化
- Tomcat部署解析及配置優化Tomcat優化
- 十六.Apache的管理及優化Apache優化
- Web 效能優化:Preload與Prefetch的使用及在 Chrome 中的優先順序Web優化Chrome
- 優化動畫卡頓:卡頓原因分析及優化方案優化動畫
- 《MySQL慢查詢優化》之SQL語句及索引優化MySql優化索引
- TiDB 查詢優化及調優系列(四)查詢執行計劃的調整及優化原理TiDB優化
- EntityFramework Core筆記:表結構及資料基本操作(2)Framework筆記
- # Kotlin使用優化(四)Kotlin優化
- consistent hash 原理,優化及實現優化
- JNI記憶體管理及優化記憶體優化
- synchronized實現原理及鎖優化synchronized優化