接上一篇文章。現(xiàn)在寫程序,做項(xiàng)目不是說(shuō)功能做完就完事了,在平常的開發(fā)過(guò)程中對(duì)于性能的考慮也是極其重要的。
關(guān)于ef的那些事,今天就來(lái)說(shuō)說(shuō)吧。首先必須得知道.net ef在程序中的五種狀態(tài)變化過(guò)程與原理。
主要來(lái)說(shuō)說(shuō)查詢部分的性能優(yōu)化,在所有查詢中,客戶端查詢出來(lái)的數(shù)據(jù)一般來(lái)說(shuō)是不需要進(jìn)行跟蹤的。也就是說(shuō)查詢只是給用戶看,不做其他任何操作。對(duì)于基于B/S模式的項(xiàng)目網(wǎng)站開發(fā),應(yīng)該是無(wú)狀態(tài)的,也就是ef中的游離態(tài)(Unchanged)(個(gè)人理解)。如果是C/S可能還需要進(jìn)行其他連接的狀態(tài)。通俗的說(shuō),在開發(fā)過(guò)程不用去檢測(cè)某一個(gè)屬性或者類是否被修改或者刪除。
例如:
AsNoTracking:它的作用就是在查詢的過(guò)程中不被緩存,也就是不被保留,查出來(lái)就完事了,這樣的狀態(tài)就變成了Detached游離態(tài)
//AsNoTracking:它的性能比ToList快大約4.8倍,不被緩存的數(shù)據(jù)。 var
item=db.Book.AsNoTracking().First(m=>m.Id==1);
Console.WriteLine(db.Entry(item).State);//輸出Detached(游離態(tài))
去掉AsNoTracking
var item=db.Book.First(m=>m.Id==1); Console.WriteLine(db.Entry(item).State); //
輸出UnChanged(持久態(tài))
也就是加上AsNoTracking做出來(lái)的查詢操作,就和數(shù)據(jù)庫(kù)斷絕了關(guān)系(個(gè)人理解)這樣對(duì)性能也是一個(gè)極好的優(yōu)化。
然后來(lái)說(shuō)說(shuō)在項(xiàng)目中對(duì)ef整體框架優(yōu)化的使用。
C#是一門面向?qū)ο蟮恼Z(yǔ)言無(wú)外乎就是封裝、繼承、多態(tài)。。。類可以繼承類,同樣接口也可以繼承接口。面向?qū)ο蟮乃枷刖褪敲繌埍矶紤?yīng)該有一個(gè)父類,只寫一遍,其他類繼承就好了不用重復(fù)去寫。
建立一個(gè)空白的解決方案,添加一個(gè)名為BaseEntity的類,你可以把它看做是所有類的基礎(chǔ)類(父類)。
重點(diǎn)來(lái)說(shuō)說(shuō)這個(gè)BaseEntity為什么要作為基礎(chǔ)類,它的用意何在。
先看看里面的寫了些什么吧!
以前用int或者long作為主鍵自增長(zhǎng)的數(shù)據(jù)類型,這樣后期數(shù)據(jù)非常龐大的時(shí)候可能會(huì)無(wú)法預(yù)估難免可能會(huì)出現(xiàn)重復(fù)的數(shù)據(jù),這個(gè)時(shí)候?qū)τ谡麄€(gè)系統(tǒng)來(lái)說(shuō)就無(wú)法保障了。
Guid給我做出了一個(gè)計(jì)算,它是32位的數(shù)字加字母的組合,而且是特別長(zhǎng)的不會(huì)重復(fù)的自增數(shù),可以這樣去理解。
DateTime就是每條數(shù)據(jù)的創(chuàng)建時(shí)間,IsRemove就是作為數(shù)據(jù)邏輯刪除的標(biāo)識(shí),也就是說(shuō),每個(gè)類(表)都會(huì)存在這三個(gè)字段屬性。你可以通過(guò)DateTime對(duì)沒(méi)張表進(jìn)行排序的操作。
using System; namespace Book.Models { public class BaseEntity { public Guid Id
{get; set; }=Guid.NewGuid(); //計(jì)算32位,字母加數(shù)字,特別長(zhǎng)的,不重復(fù)的,自增數(shù) public DateTime
DateTime {get; set; }=DateTime.Now; //每條數(shù)據(jù)的創(chuàng)建時(shí)間 public bool IsRemove { get; set
; }//偽刪除的標(biāo)識(shí) } }
創(chuàng)建BookType類
using System.ComponentModel.DataAnnotations; namespace Book.Models { public
class BookType:BaseEntity { [StringLength(20)] public string Name { get; set; }
} }
創(chuàng)建Book類
using System.ComponentModel.DataAnnotations; namespace Book.Models { public
class Book:BaseEntity { [StringLength(50),Required] public string Name { get;
set; } public decimal Price { get; set; } } }
這兩個(gè)類都會(huì)繼承BaseEntity
在App.Config里面寫上你自己的數(shù)據(jù)庫(kù)連接字符串
這樣的目的就是去生成數(shù)據(jù)庫(kù),只做生成數(shù)據(jù)庫(kù)的操作。這里只是在Models層寫了一次,在后期加上表示層還有在寫一遍!
接下來(lái)就是去通過(guò)指令遷移到數(shù)據(jù)庫(kù),操作有三步:
* 啟動(dòng)遷移:enable-migrations
* 添加遷移:add-migration '參數(shù)'
* 更新數(shù)據(jù)庫(kù):update-database(如果你需要添加或者修改某個(gè)字段屬性,只需要進(jìn)行第二步和第三步的操作即可?。?
記住默認(rèn)項(xiàng)目要選擇你的Models,如果是多個(gè)項(xiàng)目(我這里是只有一個(gè))就必須要把Models設(shè)為啟動(dòng)項(xiàng)目,不然遷移指令可能不會(huì)起到作用
這樣就表示你的數(shù)據(jù)庫(kù)遷移的工作已經(jīng)完成了,你可以在數(shù)據(jù)庫(kù)進(jìn)行查看
?接下來(lái)的工作就是準(zhǔn)備分層的工作,什么是分層?怎么分?好處是什么?
我的理解:聽某一個(gè)大佬說(shuō),代碼是一層一層寫的不是一行一行寫的。個(gè)人覺得拿捏了。原諒我沒(méi)去百度。。。
一直覺得對(duì)于面向?qū)ο蟮乃枷?,一直覺得自己才疏學(xué)淺,學(xué)的只是皮毛,真是非常的慚愧。
剛好借這個(gè)機(jī)會(huì)來(lái)通過(guò)分層一起來(lái)學(xué)習(xí)學(xué)習(xí)。三層,在之前的學(xué)習(xí)中對(duì)于三層也只是找到表示層(UI)、業(yè)務(wù)邏輯層(BLL)、數(shù)據(jù)訪問(wèn)層(DAL)。
個(gè)人覺得,這樣確實(shí)是解耦了,但是,并沒(méi)有將業(yè)務(wù)邏輯層的“業(yè)務(wù)邏輯”
這一詞的功能發(fā)揮到極致,就只是簡(jiǎn)單的從UI層get一下BLL層,BLL層簡(jiǎn)單的get一下DAL層。。。(個(gè)人覺得并沒(méi)有解什么耦。。。來(lái)自菜鳥的思考)
那我們可不可以封一個(gè)接口層,通過(guò)面向?qū)ο蟮睦^承來(lái)去調(diào)整一下呢?
首先得明確接口是可以繼承接口的!
創(chuàng)建一個(gè)數(shù)據(jù)接口層IDAL,結(jié)構(gòu)如下:引用Models層
?為什么寫這個(gè)接口呢?原因很簡(jiǎn)單,就是將增刪改查等操作進(jìn)行一個(gè)封裝,并且這不是普通的增刪改查,看代碼:
你可以把它看做是增刪改查的基礎(chǔ)接口,這個(gè)接口是一個(gè)泛型(比如你的商品表,用戶表等等...),它必須得基礎(chǔ)BaseEntity,并且這個(gè)T必須繼承與BaseEntity,也就是必須是BaseEntity的派生類。
關(guān)于IQueryable的好處注釋以寫!
這四個(gè)方法就是增刪改查,就算你有幾百?gòu)埍?,?yīng)該也只要寫一次增刪改查的操作就ok(也是來(lái)自菜鳥理解)
using System; using System.Linq; using Book.Models; namespace IDAL { //
接口封裝增刪改查的方法它,是個(gè)泛型,并且要給一個(gè)約束,這個(gè)T必須是BaseEntity的派生類也就是子類,別的不行 public interface
IBaseService<T>where T:BaseEntity { void Add(T t); void Edit(T t); void
Remove(Guid id); T GetOne(Guid id);//查詢某一個(gè)對(duì)象 //
IQueryable得到的是一個(gè)集合,它不會(huì)立刻給你去生成Sql語(yǔ)句,直到你需要拿到指定的某個(gè)結(jié)果后,再去給你生成Sql語(yǔ)句,比如根據(jù)條件,分頁(yè),排序等操作獲得的集合數(shù)據(jù)
IQueryable<T> GetAll(); } }
既然有了接口,那么肯定是要實(shí)現(xiàn)這個(gè)接口才能調(diào)用里面的增刪改查的四個(gè)方法吧!既然如此,那么就再寫一個(gè)DAL類庫(kù),去實(shí)現(xiàn)這個(gè)接口層。
結(jié)構(gòu)如下:引用EF以及Models和IDAL層,并且寫一個(gè)BaseService類去實(shí)現(xiàn)IBaseService接口里面的方法
BaseService代碼如下:
using IDAL; using System; using System.Linq; using Book.Models; namespace DAL {
public class BaseService<T> : IBaseService<T> where T:BaseEntity //
指明這個(gè)T是誰(shuí),就是繼承BaseEntity的類 { public void Add(T t) { throw new
NotImplementedException(); }public void Edit(T t) { throw new
NotImplementedException(); }public IQueryable<T> GetAll() { throw new
NotImplementedException(); }public T GetOne(Guid id) { throw new
NotImplementedException(); }public void Remove(Guid id) { throw new
NotImplementedException(); } } }
前面的?IBaseService<T>就是你實(shí)現(xiàn)的是哪個(gè)接口,后面的Where就是告訴這個(gè)接口要實(shí)現(xiàn)的T(泛型已經(jīng)指定了是繼承BaseEntity的派生類)是誰(shuí)。。。
可能這里的Where作用還需要去理解一下,個(gè)人理解就是約束、條件之類的作用。
怎么寫方法?代碼如下:
using IDAL; using System; using System.Data.Entity; using System.Linq; using
Book.Models;namespace DAL { public class BaseService<T> : IBaseService<T> where
T:BaseEntity,new() //指明這個(gè)T是誰(shuí),就是繼承BaseEntity的類 { private BookContext _db=new
BookContext();public void Add(T t) { //db.Books.Add(t); _db.Set<T>().Add(t); //
Set<>就是返回一個(gè)DbSet實(shí)例,為什么是T ,作用就是動(dòng)態(tài)的訪問(wèn),無(wú)論是Book還是BookTypes,你給我什么我就用T接就好了
_db.SaveChanges(); }public void Edit(T t) { _db.Entry(t).State =
EntityState.Modified;//直接通過(guò)主題去作修改 _db.SaveChanges(); } public IQueryable<T>
GetAll() {return _db.Set<T>().Where(m => !m.IsRemove).AsNoTracking(); //
脫離持久態(tài),變?yōu)橛坞x態(tài),并且過(guò)濾掉了已被刪除的數(shù)據(jù) } public T GetOne(Guid id) { return GetAll().First(m
=> m.Id == id);//直接通過(guò)GetAll在通過(guò)id查到你想要的數(shù)據(jù) } public void Remove(Guid id) { var t=
new T() { Id = id }; //這里是偽刪除,T不能直接new,也是給個(gè)約束條件 new()即可,代表它有構(gòu)造函數(shù)
_db.Entry(t).State = EntityState.Unchanged;//也是根據(jù)狀態(tài)去刪除 t.IsRemove = true;
_db.SaveChanges(); } } }
這些增刪改查方法寫完后,就要去繼承了。原理實(shí)際上和BaseEntity差不多
既然增刪改查的方法都寫好了,那么怎么去才能調(diào)用到它呢?這個(gè)時(shí)候?yàn)槭裁催€需要去寫IBook和IBookType呢?。
首先我們?cè)谥笆菍懥薎BaseService這個(gè)接口并且寫了對(duì)應(yīng)的增刪改查方法,但是具體的實(shí)現(xiàn)是在DAL層所有,這兩個(gè)接口只需要繼承對(duì)應(yīng)的IBaseService就好了,具體實(shí)現(xiàn)也是在DAL層,至于DAL層怎么去寫,請(qǐng)看下面的內(nèi)容。
這個(gè)時(shí)候泛型 T就其作用了,我們可以去寫對(duì)應(yīng)的接口繼承IBaseService就好了。。
?代碼如下:
IBookService:
namespace IDAL { public interface IBook:IBaseService<Book.Models.Book> { } }
IBookTypeService:
using Book.Models; namespace IDAL { public interface
IBookTypeService:IBaseService<BookType> { } }
DAL層結(jié)構(gòu):
為什么還要繼承BaseService?首先BaseService具體實(shí)現(xiàn)了繼承IBaseService的方法,所以,我們只需要繼承一下就可以調(diào)用到BaseService的增刪改查的方法了!
BookService代碼:
using Book.Models; using IDAL; namespace DAL { public class
BookService:BaseService<Book.Models.Book>, IBookService { } }
BookTypeService代碼:
using Book.Models; using IDAL; namespace DAL { public class
BookTypeService:BaseService<BookType>,IBookTypeService { } }
以上操作完成后,我們只需要實(shí)例化DAL層的Service方法就可以調(diào)用在UI層增刪改查了。
寫到這里只是最最基礎(chǔ)的底層,如果還要完善需要進(jìn)一步的改善改善。。。
如果有錯(cuò)誤的地方請(qǐng)?zhí)岢鰜?lái),本人也是學(xué)生,大家都是抱著學(xué)習(xí)的心態(tài)去記錄和鞏固自己的知識(shí)點(diǎn)。。。。。
熱門工具 換一換
