源代碼下載地址在最後
知識要求:ef code first
ef 批量操做是最近遇到的一個新問題,ef這個orm爲咱們解決了大量的curd操做,可是,對於批量操做,其性能一直沒有很好的方案,無論是 foreach 方式,仍是 addorupdate(這個擴展內部實現原理仍是一個一個add),當數據量很大的時候,其性能簡直是不能容忍,差很少1萬多的數據,須要等半個小時左右!
因而開始着手尋找一個可使用ef進行批量操做的的類庫,開始用的是 zzz projects 的類庫,可是其免費版,只有更新和刪除,沒有插入,使用非免費版,若是時間過時,會致使程序出問題(血的教訓),因而有找啊找,皇天不負有心人,這裏須要吐槽下百度,在百度上,基本上搜索到的答案,均是 zzz projects 的類庫 或者 ef extend ,後者也沒有 批量插入。
不得已,咱們翻出去看看,不會FQ的程序員不是好的搬磚工,在google一搜,搜到了很漂亮,很好用的一個類庫:EntityFramework.Utilities,地址:https://github.com/MikaelEliasson/EntityFramework.Utilities, 關鍵這貨仍是免費開源的……
咱們的故事就從這裏開始!javascript
使用vs工具,建立一個控制檯程序,並引入nuget 包:php
實體類以下:java
using System.ComponentModel.DataAnnotations; public class TestEntity { [Key] public int Id { get; set; } public int RandomVlue { get; set; } }
上下文以下:git
public class BatchDemoContext : DbContext { public BatchDemoContext() : base("Default") { } public IDbSet<TestEntity> TestEntities { get; set; } }
有了以上代碼,咱們就可使用code first 命令建立數據庫,前提是,在配置文件裏,添加了數據庫鏈接字符串程序員
有了以上內容,接下來,咱們須要生產多條數據,好比,生產100000條數據,具體的代碼以下github
/// <summary> /// 產生須要生產的數據 /// </summary> /// <returns></returns> private static IEnumerable<TestEntity> GetInsertDatas() { // 線程安全的list ConcurrentBag<TestEntity> datas=new ConcurrentBag<TestEntity>(); Parallel.For(0, 100000, (index, state) => { Random rand = new Random(); var newData = new TestEntity { RandomVlue = rand.Next(1, 100) }; datas.Add(newData); }); return datas; }
爲了生產數據快,使用了Parallel,不懂的能夠自行 google,若是不能FQ你就bing一下,或者是看《 C# 高級編程》 關於異步 的這章,這裏,只須要知道,生產100000條數據,用parallel會很快的產生數據便可!sql
咱們獲取了數據源,那麼,如何對這些數據進行插入呢,看以下的測試代碼:數據庫
/// <summary> /// 批量插入 /// </summary> private static void BatchInster() { var datas = GetInsertDatas(); var testEntities = datas as IList<TestEntity> ?? datas.ToList(); Stopwatch watch =new Stopwatch(); Console.WriteLine("開始插入計時,總共數據:{0}條",testEntities.Count()); watch.Start(); using (var context=new BatchDemoContext()) { EFBatchOperation.For(context,context.TestEntities) .InsertAll(testEntities); } watch.Stop(); Console.WriteLine("結束插入計時,工用時:{0}ms",watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("數據庫總共數據:{0}條",count); var minId = context.TestEntities.Min(c => c.Id); // 隨機取十條數據進行驗證 for (int i = 1; i <= 10; i++) { Random rand = new Random(); var id = rand.Next(minId, minId+ 100000); var testdata = context.TestEntities.FirstOrDefault(c => c.Id == id); Console.WriteLine("插入的數據 id:{0} randomvalue:{1}",testdata.Id,testdata.RandomVlue); } } Console.WriteLine("-----------------華麗的分割線 插入-------------------------"); }
用 Stopwatch 監測 插入所執行的時間
經過使用代碼編程
using (var context=new BatchDemoContext()) { EFBatchOperation.For(context,context.TestEntities) .InsertAll(testEntities); }
便可實現批量插入,而後,針對這10調數據,隨機隨十條進行驗證,咱們在生產數據的時候,默認的隨機值不會超過100,若是這10條隨機值,都是小於100的,能夠認爲插入成功。
使用var minId = context.TestEntities.Min(c => c.Id);
是爲了保證,id條件最小,否則第二次運行程序,會出錯,由於id是自增的,刪除數據,id也不會從1開始!
插入的運行結果以下:安全
經過上圖能夠看出
須要插入的數據有 100000條,插入僅用了 3070ms,這比原生態的ef要快了不知道多少倍……
經過插入數據的id和randomvalue,能夠看出,咱們的數據,也的確是正確的插入到了數據庫!
經過上面的步驟,咱們數據庫裏已經有100000條數據裏,如今,咱們將數據庫裏的數據,randomvalue 所有設置爲 1000,因而咱們須要,獲取所有數據,而後並行運算,改randomvalue的值爲100000,在而後批量更新修改後的數據
代碼以下
/// <summary> /// 批量更新 /// </summary> private static void BatchUpdate() { IEnumerable<TestEntity> toUpdates=new List<TestEntity>(); // 獲取全部數據 using (var context = new BatchDemoContext()) { toUpdates = context.TestEntities.ToList(); } // 全部的值 都爲 1000 Parallel.ForEach(toUpdates, (entity, state) => { entity.RandomVlue = 1000; }); Stopwatch watch = new Stopwatch(); Console.WriteLine("開始更新計時,總共數據:{0}條", toUpdates.Count()); watch.Start(); using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities).UpdateAll(toUpdates, x => x.ColumnsToUpdate(c => c.RandomVlue)); } watch.Stop(); Console.WriteLine("結束更新計時,工用時:{0}ms", watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("數據庫總共數據:{0}條", count); var minId = context.TestEntities.Min(c => c.Id); // 隨機取十條數據進行驗證 for (int i = 1; i <= 10; i++) { Random Rand = new Random(); var id = Rand.Next(minId, minId+100000); var testdata = context.TestEntities.FirstOrDefault(c => c.Id == id); Console.WriteLine("更新的數據 id:{0} randomvalue:{1}", testdata.Id, testdata.RandomVlue); } } Console.WriteLine("-----------------華麗的分割線 更新-------------------------"); }
經過
using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities).UpdateAll(toUpdates, x => x.ColumnsToUpdate(c => c.RandomVlue)); }
這條語句,告訴ef,須要更新哪一個集合的randomvalue!
而後隨機取10條數據,發現,randomvalue的值所有都是 1000,說明咱們批量更新成功!
運行結果以下:
什麼是查詢更新呢?就是當咱們知足什麼條件的時候,對屬性進行什麼操做,相似於 update table set col=value where id=1 這樣的 sql 語句,和批量更新有什麼區別的?我這邊的批量更新,是指從數據庫中加載的多個實體到內存,在內存中改變了屬性值,在將這一批數據,更新到數據庫,而查詢更新,無需查詢到內存!
這裏,咱們將id大於等於 minid+10000的數據和 id 小於等於 minid+50000的數據進行改值,修改ran 的值爲 500,
代碼以下
/// <summary> /// 將id >= 1w 小於 5w 的隨機值等於 500 /// </summary> private static void BatchUpdateQuery() { Stopwatch watch = new Stopwatch(); Console.WriteLine("開始查詢更新計時"); watch.Start(); using (var context = new BatchDemoContext()) { var minId = context.TestEntities.Min(c => c.Id); EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>= minId+10000 && c.Id<= minId+50000) .Update(c=>c.RandomVlue,rv=>500); } watch.Stop(); Console.WriteLine("結束查詢更新計時,工用時:{0}ms", watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("數據庫總共數據:{0}條", count); var minId = context.TestEntities.Min(c => c.Id); // 隨機取十條數據進行驗證 for (int i = 1; i <= 10; i++) { Random rand = new Random(); var id = rand.Next(minId+10000, minId+ 50000); var testdata = context.TestEntities.FirstOrDefault(c => c.Id == id); Console.WriteLine("查詢更新的數據 id:{0} randomvalue:{1}", testdata.Id, testdata.RandomVlue); } } Console.WriteLine("-----------------華麗的分割線 查詢更新-------------------------"); }
經過代碼
var minId = context.TestEntities.Min(c => c.Id); EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>= minId+10000 && c.Id<= minId+50000) .Update(c=>c.RandomVlue,rv=>500);
進行查詢更新
而後取十條數據進行驗證,具體的運行結果以下:
經過結果,咱們能夠看出,查詢更新也執行成功了
相似的sql語句是 : delete from table where id=1
在ef裏,刪除只能是先獲取,在remove,咱們如何使用efUtilities進行批量刪除呢?
看代碼
/// <summary> /// 刪除全部數據 /// </summary> private static void BatchDelete() { Stopwatch watch = new Stopwatch(); Console.WriteLine("開始刪除計時"); watch.Start(); using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>=1).Delete(); } watch.Stop(); Console.WriteLine("結束刪除計時,工用時:{0}ms", watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("數據庫總共數據:{0}條", count); } Console.WriteLine("-----------------華麗的分割線 刪除-------------------------"); }
使用代碼
using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>=1).Delete(); }
進行批量刪除,當咱們刪除以後,數據庫數據應該爲空,即條目爲0,爲了驗證是否刪除,咱們只需獲取條目便可,運行結果以下
實踐證實,批量刪除是成功的
efUtilities地址:https://github.com/MikaelEliasson/EntityFramework.Utilities ,也能夠從這裏看到文檔
相比 zzz projects ,其提供的功能還算是很全的,批量插入,批量更新,查詢更新和批量刪除,可是, ef utilities 是 免費開源的,免費開源的,免費開源的,重要的事情說五遍,開源的代碼,咱們能夠學習甚至是改造,打造符合本身的代碼!
相比 ef extend , ef utilities 提供的功能全面,基本上是 extend有的,utilities 有,extend 沒有的,utilities 也有,(只針對批量操做,查詢方面,仍是extend 強大)
什麼狀況下,會用到批量操做?
我遇到的有:導入數據、錄入多條數據、批量計算而後保存每一條數據等等………………
qq:961823316
源代碼:https://git.oschina.net/zhaord/EfBatchDemo.git
轉載註明:http://www.jianshu.com/p/dff3c684a0e4