[开源] .Net orm FreeSql 1.5.0 最新版本(番号:好久不见)
廢話(huà)開(kāi)頭
這篇文章是我有史以來(lái)編輯最長(zhǎng)時(shí)間的,歷時(shí) 4小時(shí)!!!原本我可以利用這 4小時(shí)編寫(xiě)一堆膠水代碼,真心希望善良的您點(diǎn)個(gè)贊,謝謝了!!
很久很久沒(méi)有寫(xiě)文章了,上一次還是在元旦發(fā)布 1.0 版本的時(shí)候,今年版本規(guī)劃是每月底發(fā)布小版本(年底發(fā)布 2.0),全年的開(kāi)源工作主要是收集用戶(hù)需求增加功能,完善測(cè)試,修復(fù) bug。FreeSql 1.0 -> 1.5 相隔半年有哪些新功能?只能說(shuō)每個(gè)功能都能讓我興奮,并且能感受到使用者也一樣興奮(妄想癥)。
迫不及待的人會(huì)問(wèn),這更新速度也太快了吧,升級(jí)會(huì)不會(huì)有問(wèn)題?
不了解版本的更新日志,直接升級(jí)不是好的習(xí)慣,建議關(guān)注我們的更新日志(github 上有專(zhuān)門(mén)的文檔);
我們的版本開(kāi)發(fā)原則:在盡量保證兼容的情況下,增加新功能,砍掉少量不合理的功能;
我們的單元測(cè)試數(shù)量:4000+,這是我們引以自豪,發(fā)布版本的保障;
2|0入戲準(zhǔn)備
FreeSql 是 .Net ORM,能支持 .NetFramework4.0+、.NetCore、Xamarin、XAUI、Blazor、以及還有說(shuō)不出來(lái)的運(yùn)行平臺(tái),因?yàn)榇a綠色無(wú)依賴(lài),支持新平臺(tái)非常簡(jiǎn)單。目前單元測(cè)試數(shù)量:4000+,Nuget下載數(shù)量:123K+,源碼幾乎每天都有提交。值得高興的是 FreeSql 加入了 ncc 開(kāi)源社區(qū):https://github.com/dotnetcore/FreeSql,加入組織之后社區(qū)責(zé)任感更大,需要更努力做好品質(zhì),為開(kāi)源社區(qū)出一份力。QQ開(kāi)發(fā)群:4336577
為什么要重復(fù)造輪子?
FreeSql 主要優(yōu)勢(shì)在于易用性上,基本是開(kāi)箱即用,在不同數(shù)據(jù)庫(kù)之間切換兼容性比較好。作者花了大量的時(shí)間精力在這個(gè)項(xiàng)目,肯請(qǐng)您花半小時(shí)了解下項(xiàng)目,謝謝。
FreeSql 整體的功能特性如下:
支持 CodeFirst 對(duì)比結(jié)構(gòu)變化遷移;
支持 DbFirst 從數(shù)據(jù)庫(kù)導(dǎo)入實(shí)體類(lèi);
支持 豐富的表達(dá)式函數(shù),自定義解析;
支持 批量添加、批量更新、BulkCopy;
支持 導(dǎo)航屬性,貪婪加載、延時(shí)加載、級(jí)聯(lián)保存;
支持 讀寫(xiě)分離、分表分庫(kù),租戶(hù)設(shè)計(jì);
支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/達(dá)夢(mèng)/MsAccess;
1.0 -> 1.5 更新的重要功能如下:
一、UnitOfWorkManager 工作單元管理器,可實(shí)現(xiàn) Spring 事務(wù)設(shè)計(jì);
二、IFreeSql.InsertOrUpdate 實(shí)現(xiàn)批量保存,執(zhí)行時(shí)根據(jù)數(shù)據(jù)庫(kù)自動(dòng)適配執(zhí)行 merge into 或者 on duplicate key update;
三、ISelect.WhereDynamicFilter 方法實(shí)現(xiàn)動(dòng)態(tài)過(guò)濾條件(與前端交互);
四、自動(dòng)適配表達(dá)式解析 yyyyMMdd 常用 c# 日期格式化;
五、IUpdate.SetSourceIgnore 方法實(shí)現(xiàn)忽略屬性值為 null 的字段;
六、FreeSql.Provider.Dameng 基于 DmProvider Ado.net 訪(fǎng)問(wèn)達(dá)夢(mèng)數(shù)據(jù)庫(kù);
七、自動(dòng)識(shí)別 EFCore 常用的實(shí)體特性,FreeSql.DbContext 擁有和 EFCore 高相似度的語(yǔ)法,并且支持 90% 相似的 FluentApi;
八、ISelect.ToTreeList 擴(kuò)展方法查詢(xún)數(shù)據(jù),把配置父子導(dǎo)航屬性的實(shí)體加工為樹(shù)型 List;
九、BulkCopy 相關(guān)方法提升大批量數(shù)據(jù)插入性能;
十、Sqlite :memrory: 內(nèi)存模式;
FreeSql 使用非常簡(jiǎn)單,只需要定義一個(gè) IFreeSql 對(duì)象即可:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, connectionString).UseAutoSyncStructure(true) //自動(dòng)同步實(shí)體結(jié)構(gòu)到數(shù)據(jù)庫(kù).Build(); //請(qǐng)務(wù)必定義成 Singleton 單例模式?
3|0UnitOfWorkManager 工作單元管理器
public class SongService {BaseRepository<Song> _repo;public SongService(BaseRepository<Song> repo){_repo = repo;}[Transactional]public virtual void Test1(){_repo.Insert(new Song { Title = "卡農(nóng)1" }); //事務(wù)1this.Test2();}[Transactional(Propagation = Propagation.Nested)] //嵌套事務(wù),新的(不使用 Test1 的事務(wù))public virtual void Test2(){_repo.Insert(new Song { Title = "卡農(nóng)2" });} }BaseRepository 是 FreeSql.BaseRepository 包實(shí)現(xiàn)的通用倉(cāng)儲(chǔ)類(lèi),實(shí)際項(xiàng)目中可以繼承它再使用。
Propagation 的模式參考了 Spring 事務(wù),在以下幾種模式:
Requierd:如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中,默認(rèn)的選擇。
Supports:支持當(dāng)前事務(wù),如果沒(méi)有當(dāng)前事務(wù),就以非事務(wù)方法執(zhí)行。
Mandatory:使用當(dāng)前事務(wù),如果沒(méi)有當(dāng)前事務(wù),就拋出異常。
NotSupported:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
Never:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前事務(wù)存在則拋出異常。
Nested:以嵌套事務(wù)方式執(zhí)行。(上面的例子使用的這個(gè))
UnitOfWorkManager 正是干這件事的。避免了每次對(duì)數(shù)據(jù)操作都要現(xiàn)獲得 Session 實(shí)例來(lái)啟動(dòng)事務(wù)/提交/回滾事務(wù)還有繁瑣的Try/Catch操作。這些也是 AOP(面向切面編程)機(jī)制很好的應(yīng)用。一方面使開(kāi)發(fā)業(yè)務(wù)邏輯更清晰、專(zhuān)業(yè)分工更加容易進(jìn)行。另一方面就是應(yīng)用 AOP 隔離降低了程序的耦合性使我們可以在不同的應(yīng)用中將各個(gè)切面結(jié)合起來(lái)使用大大提高了代碼重用度。
使用前準(zhǔn)備第一步:配置 Startup.cs 注入
//Startup.cs public void ConfigureServices(IServiceCollection services) {services.AddSingleton<IFreeSql>(fsql);services.AddScoped<UnitOfWorkManager>();services.AddFreeRepository(null, typeof(Startup).Assembly); }| IUnitOfWork Current | 返回當(dāng)前的工作單元 |
| void Binding(repository) | 將倉(cāng)儲(chǔ)的事務(wù)交給它管理 |
| IUnitOfWork Begin(propagation, isolationLevel) | 創(chuàng)建工作單元 |
使用前準(zhǔn)備第二步:定義事務(wù)特性
[AttributeUsage(AttributeTargets.Method)] public class TransactionalAttribute : Attribute {/// <summary>/// 事務(wù)傳播方式/// </summary>public Propagation Propagation { get; set; } = Propagation.Requierd;/// <summary>/// 事務(wù)隔離級(jí)別/// </summary>public IsolationLevel? IsolationLevel { get; set; } }使用前準(zhǔn)備第三步:引入動(dòng)態(tài)代理庫(kù)
在 Before 從容器中獲取 UnitOfWorkManager,調(diào)用它的 var uow = uowManager.Begin(attr.Propagation, attr.IsolationLevel) 方法
在 After 調(diào)用 Before 中的 uow.Commit 或者 Rollback 方法,最后調(diào)用 uow.Dispose
自問(wèn)自答:是不是進(jìn)方法就開(kāi)事務(wù)呢?
不一定是真實(shí)事務(wù),有可能是虛的,就是一個(gè)假的 unitofwork(不帶事務(wù)),也有可能是延用上一次的事務(wù),也有可能是新開(kāi)事務(wù),具體要看傳播模式。
4|0IFreeSql.InsertOrUpdate 批量插入或更新
IFreeSql 定義了 InsertOrUpdate 方法實(shí)現(xiàn)批量插入或更新的功能,利用的是數(shù)據(jù)庫(kù)特性進(jìn)行保存,執(zhí)行時(shí)根據(jù)數(shù)據(jù)庫(kù)自動(dòng)適配:
| MySql | on duplicate key update |
| PostgreSQL | on conflict do update |
| SqlServer | merge into |
| Oracle | merge into |
| Sqlite | replace into |
| Dameng | merge into |
由于我們前面定義 fsql 變量的類(lèi)型是 MySql,所以執(zhí)行的語(yǔ)句大概是這樣的:
INSERT INTO `T`(`id`, `name`) VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)當(dāng)實(shí)體類(lèi)有自增屬性時(shí),批量 InsertOrUpdate 最多可被拆成兩次執(zhí)行,內(nèi)部計(jì)算出未設(shè)置自增值、和有設(shè)置自增值的數(shù)據(jù),分別執(zhí)行 insert into 和 上面講到的 merge into 兩種命令(采用事務(wù)執(zhí)行)。
5|0WhereDynamicFilter 動(dòng)態(tài)過(guò)濾
是否見(jiàn)過(guò)這樣的高級(jí)查詢(xún)功能,WhereDynamicFilter 在后端可以輕松完成這件事情,前端根據(jù) UI 組裝好對(duì)應(yīng)的 json 字符串傳給后端就行,如下:
DynamicFilterInfo dyfilter = JsonConvert.DeserializeObject<DynamicFilterInfo>(@" {""Logic"" : ""Or"",""Filters"" :[{""Field"" : ""Code"",""Operator"" : ""NotContains"",""Value"" : ""val1"",""Filters"" :[{""Field"" : ""Name"",""Operator"" : ""NotStartsWith"",""Value"" : ""val2"",}]},{""Field"" : ""Parent.Code"",""Operator"" : ""Eq"",""Value"" : ""val11"",""Filters"" :[{""Field"" : ""Parent.Name"",""Operator"" : ""Contains"",""Value"" : ""val22"",}]}] } "); fsql.Select<VM_District_Parent>().WhereDynamicFilter(dyfilter).ToList(); //SELECT a.""Code"", a.""Name"", a.""ParentCode"", a__Parent.""Code"" as4, a__Parent.""Name"" as5, a__Parent.""ParentCode"" as6 //FROM ""D_District"" a //LEFT JOIN ""D_District"" a__Parent ON a__Parent.""Code"" = a.""ParentCode"" //WHERE?(not((a.""Code"")?LIKE?'%val1%')?AND?not((a.""Name"")?LIKE?'val2%')?OR?a__Parent.""Code""?=?'val11'?AND?(a__Parent.""Name"")?LIKE?'%val22%')支持的操作符:Contains/StartsWith/EndsWith/NotContains/NotStartsWith/NotEndsWith、Equals/Eq/NotEqual、GreaterThan/GreaterThanOrEqual、LessThan/LessThanOrEqual
6|0表達(dá)式解析 yyyyMMdd c# 常用日期格式化
不知道大家有沒(méi)有這個(gè)困擾,在 ORM 表達(dá)式使用 DateTime.Now.ToString("yyyyMM") 是件很難轉(zhuǎn)換的事,在我適配的這些數(shù)據(jù)庫(kù)中,只有 MsAccess 可以直接翻譯成對(duì)應(yīng)的 SQL 執(zhí)行。
這個(gè)想法來(lái)自另一個(gè) ORM issues,我時(shí)不時(shí)會(huì)去了解其他 ORM 優(yōu)點(diǎn)和缺陷,以便給 FreeSql 做補(bǔ)充。
想法出來(lái)之后當(dāng)于,也就是昨天 2020/5/24 奮戰(zhàn)一宿完成的,除了每個(gè)數(shù)據(jù)庫(kù)進(jìn)行編碼適配外,更多的時(shí)間耗在了單元測(cè)試上,目前已全部通過(guò)(4000+單元測(cè)試不是吹的)。
僅以此功能讓大家感受一下 FreeSql 的認(rèn)真,他不是一些人口中所說(shuō)的個(gè)人項(xiàng)目,謝謝。
var dtn = DateTime.Parse("2020-1-1 0:0:0"); var dts = Enumerable.Range(1, 12).Select(a => dtn.AddMonths(a)).Concat(Enumerable.Range(1, 31).Select(a => dtn.AddDays(a))).Concat(Enumerable.Range(1, 24).Select(a => dtn.AddHours(a))).Concat(Enumerable.Range(1, 60).Select(a => dtn.AddMinutes(a))).Concat(Enumerable.Range(1, 60).Select(a => dtn.AddSeconds(a))); foreach (var dt in dts) {Assert.Equal(dt.ToString("yyyy-MM-dd HH:mm:ss.fff"), fsql.Select<T>().First(a => dt.ToString()));Assert.Equal(dt.ToString("yyyy-MM-dd HH:mm:ss"), fsql.Select<T>().First(a => dt.ToString("yyyy-MM-dd HH:mm:ss")));Assert.Equal(dt.ToString("yyyy-MM-dd HH:mm"), fsql.Select<T>().First(a => dt.ToString("yyyy-MM-dd HH:mm")));Assert.Equal(dt.ToString("yyyy-MM-dd HH"), fsql.Select<T>().First(a => dt.ToString("yyyy-MM-dd HH")));Assert.Equal(dt.ToString("yyyy-MM-dd"), fsql.Select<T>().First(a => dt.ToString("yyyy-MM-dd")));Assert.Equal(dt.ToString("yyyy-MM"), fsql.Select<T>().First(a => dt.ToString("yyyy-MM")));Assert.Equal(dt.ToString("yyyyMMddHHmmss"), fsql.Select<T>().First(a => dt.ToString("yyyyMMddHHmmss")));Assert.Equal(dt.ToString("yyyyMMddHHmm"), fsql.Select<T>().First(a => dt.ToString("yyyyMMddHHmm")));Assert.Equal(dt.ToString("yyyyMMddHH"), fsql.Select<T>().First(a => dt.ToString("yyyyMMddHH")));Assert.Equal(dt.ToString("yyyyMMdd"), fsql.Select<T>().First(a => dt.ToString("yyyyMMdd")));Assert.Equal(dt.ToString("yyyyMM"), fsql.Select<T>().First(a => dt.ToString("yyyyMM")));Assert.Equal(dt.ToString("yyyy"), fsql.Select<T>().First(a => dt.ToString("yyyy")));Assert.Equal(dt.ToString("HH:mm:ss"), fsql.Select<T>().First(a => dt.ToString("HH:mm:ss")));Assert.Equal(dt.ToString("yyyy MM dd HH mm ss yy M d H hh h"), fsql.Select<T>().First(a => dt.ToString("yyyy MM dd HH mm ss yy M d H hh h")));Assert.Equal(dt.ToString("yyyy MM dd HH mm ss yy M d H hh h m s tt t").Replace("上午", "AM").Replace("下午", "PM").Replace("上", "A").Replace("下", "P"), fsql.Select<T>().First(a => dt.ToString("yyyy MM dd HH mm ss yy M d H hh h m s tt t"))); }支持常用 c# 日期格式化,yyyy MM dd HH mm ss yy M d H hh h m s tt t
tt t 為 AM PM
AM PM 這兩個(gè)轉(zhuǎn)換不完美,勉強(qiáng)能使用。
7|0IUpdate.SetSourceIgnore 不更新 null 字段
這個(gè)功能被用戶(hù)提了幾次,每一次都認(rèn)為 FreeSql.Repository 的狀態(tài)對(duì)比可以完成這件事。
這一次作者心疼他們了,為什么一定要用某個(gè)功能限制住使用者?大家是否經(jīng)常聽(tīng)誰(shuí)說(shuō) EF框架、MVC框架,框架的定義其實(shí)是約束+規(guī)范。
作者不想做這樣的約束,作者更希望盡量提供多一些實(shí)用功能讓用戶(hù)自己選擇,把項(xiàng)目定義為:功能組件。
fsql.Update<Song>().SetSourceIgnore(item, col => col == null).ExecuteAffrows();第二個(gè)參數(shù)是 Func<object, bool> 類(lèi)型,col 相當(dāng)于屬性的值,上面的代碼更新實(shí)體 item 的時(shí)候會(huì)忽略 == null 的屬性。
8|0Ado.net 訪(fǎng)問(wèn)達(dá)夢(mèng)數(shù)據(jù)庫(kù)
武漢達(dá)夢(mèng)數(shù)據(jù)庫(kù)有限公司成立于2000年,為中國(guó)電子信息產(chǎn)業(yè)集團(tuán)(CEC)旗下基礎(chǔ)軟件企業(yè),專(zhuān)業(yè)從事數(shù)據(jù)庫(kù)管理系統(tǒng)的研發(fā)、銷(xiāo)售與服務(wù),同時(shí)可為用戶(hù)提供大數(shù)據(jù)平臺(tái)架構(gòu)咨詢(xún)、數(shù)據(jù)技術(shù)方案規(guī)劃、產(chǎn)品部署與實(shí)施等服務(wù)。多年來(lái),達(dá)夢(mèng)公司始終堅(jiān)持原始創(chuàng)新、獨(dú)立研發(fā),目前已掌握數(shù)據(jù)管理與數(shù)據(jù)分析領(lǐng)域的核心前沿技術(shù),擁有全部源代碼,具有完全自主知識(shí)產(chǎn)權(quán)。
不知道大家沒(méi)有聽(tīng)說(shuō)過(guò)相關(guān)政策,政府推動(dòng)國(guó)產(chǎn)化以后是趨勢(shì),雖然 .NET 不是國(guó)產(chǎn),但是目前無(wú)法限制編程語(yǔ)言,當(dāng)下正在對(duì)操作系統(tǒng)、數(shù)據(jù)庫(kù)強(qiáng)制推進(jìn)。
我們知道 EFCore for oracle 問(wèn)題多,并且現(xiàn)在還沒(méi)更新到 3.x,在這樣的背景下,一個(gè)國(guó)產(chǎn)數(shù)據(jù)庫(kù)更不能指望誰(shuí)實(shí)現(xiàn)好用的 EFCore。目前看來(lái)除了 EFCore for sqlserver 我們沒(méi)把握完全占優(yōu)勢(shì),起碼在其他數(shù)據(jù)庫(kù)肯定是我們更接地氣。
言歸正傳,達(dá)夢(mèng)數(shù)據(jù)庫(kù)其實(shí)蠻早就支持了,之前是以 Odbc 的方式實(shí)現(xiàn)的,后面根據(jù)使用者的反饋 Odbc 環(huán)境問(wèn)題比較麻煩,經(jīng)研究決定支持 ado.net 適配,讓使用者更加方便。使用 ado.net 方式連接達(dá)夢(mèng)只需要修改 IFreeSql 創(chuàng)建時(shí)候的類(lèi)型即可,如下:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.Dameng, connectionString).UseAutoSyncStructure(true) //自動(dòng)同步實(shí)體結(jié)構(gòu)到數(shù)據(jù)庫(kù).Build(); //請(qǐng)務(wù)必定義成 Singleton 單例模式9|0兼容 EFCore 實(shí)體特性、FluentApi
EFCore 目前用戶(hù)量最多,為了方便一些項(xiàng)目過(guò)渡到 FreeSql,我們做了一些 “AI”:
自動(dòng)識(shí)別 EFCore 實(shí)體特性:Key/Required/NotMapped/Table/Column
與 EFCore 90% 相似的 FluentApi
fsql.CodeFirst.Entity<Song>(eb => {eb.ToTable("tb_song");eb.Ignore(a => a.Field1);eb.Property(a => a.Title).HasColumnType("varchar(50)").IsRequired();eb.Property(a => a.Url).HasMaxLength(100);eb.Property(a => a.RowVersion).IsRowVersion();eb.Property(a => a.CreateTime).HasDefaultValueSql("current_timestamp");eb.HasKey(a => a.Id);eb.HasIndex(a => new { a.Id, a.Title }).IsUnique().HasName("idx_xxx11");//一對(duì)多、多對(duì)一eb.HasOne(a => a.Type).HasForeignKey(a => a.TypeId).WithMany(a => a.Songs);//多對(duì)多eb.HasMany(a => a.Tags).WithMany(a => a.Songs, typeof(Song_tag)); });fsql.CodeFirst.Entity<SongType>(eb => {eb.HasMany(a => a.Songs).WithOne(a => a.Type).HasForeignKey(a => a.TypeId);eb.HasData(new[]{new SongType{Id = 1,Name = "流行",Songs = new List<Song>(new[]{new Song{ Title = "真的愛(ài)你" },new Song{ Title = "愛(ài)你一萬(wàn)年" },})},new SongType{Id = 2,Name = "鄉(xiāng)村",Songs = new List<Song>(new[]{new Song{ Title = "鄉(xiāng)里鄉(xiāng)親" },})},}); });public class SongType {public int Id { get; set; }public string Name { get; set; }public List<Song> Songs { get; set; } } public class Song {[Column(IsIdentity = true)]public int Id { get; set; }public string Title { get; set; }public string Url { get; set; }public DateTime CreateTime { get; set; }public int TypeId { get; set; }public SongType Type { get; set; }public int Field1 { get; set; }public long RowVersion { get; set; } }10|0ISelect.ToTreeList 查詢(xún)樹(shù)型數(shù)據(jù) List
這是幾個(gè)意思?有做過(guò)父子關(guān)系的表應(yīng)該知道的,把數(shù)據(jù)查回來(lái)了是平面的,需要再用遞歸轉(zhuǎn)化為樹(shù)型。考慮到這個(gè)功能實(shí)用性比較高,所以就集成了進(jìn)來(lái)。來(lái)自單元測(cè)試的一段代碼:
var repo = fsql.GetRepository<VM_District_Child>(); repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; repo.DbContextOptions.NoneParameter = true; repo.Insert(new VM_District_Child {Code = "100000",Name = "中國(guó)",Childs = new List<VM_District_Child>(new[] {new VM_District_Child{Code = "110000",Name = "北京市",Childs = new List<VM_District_Child>(new[] {new VM_District_Child{ Code="110100", Name = "北京市" },new VM_District_Child{ Code="110101", Name = "東城區(qū)" },})}}) }); var t3 = fsql.Select<VM_District_Child>().ToTreeList(); Assert.Single(t3); Assert.Equal("100000", t3[0].Code); Assert.Single(t3[0].Childs); Assert.Equal("110000", t3[0].Childs[0].Code); Assert.Equal(2, t3[0].Childs[0].Childs.Count); Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code); Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code);注意:實(shí)體需要配置父子導(dǎo)航屬性
11|0BulkCopy 大批量數(shù)據(jù)
原先 FreeSql 對(duì)批量數(shù)據(jù)操作就做得還可以,例如批量數(shù)據(jù)超過(guò)數(shù)據(jù)庫(kù)某些限制的,會(huì)拆分執(zhí)行,性能其實(shí)也還行。
本需求也是來(lái)自用戶(hù),然后就實(shí)現(xiàn)了,實(shí)現(xiàn)完了我還專(zhuān)門(mén)做了性能測(cè)試對(duì)比,sqlserver bulkcopy 收益比較大,mysql 收益非常小。
測(cè)試結(jié)果(52個(gè)字段,18W-50行數(shù)據(jù),單位ms):
測(cè)試結(jié)果,是在相同操作系統(tǒng)下進(jìn)行的,并且都有預(yù)熱
ExecuteMySqlBulkCopy 方法在 FreeSql.Provider.MySqlConnector 中實(shí)現(xiàn)的
12|0Sqlite :memory: 內(nèi)存模式
了解 EFCore 應(yīng)該知道有一個(gè) inMemory 實(shí)現(xiàn),Sqlite 其實(shí)也有內(nèi)存模式,所以在非常棒(忍不住)的 FreeSql.Provider.Sqlite 稍加適配就可以實(shí)現(xiàn) inMemory 模式了。
使用 inMemory 模式非常簡(jiǎn)單,只需要修改 IFreeSql 創(chuàng)建的類(lèi)型,以及連接字符串即可:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.Sqlite, "Data Source=:memory:").UseAutoSyncStructure(true) //自動(dòng)同步實(shí)體結(jié)構(gòu)到數(shù)據(jù)庫(kù).Build(); //請(qǐng)務(wù)必定義成 Singleton 單例模式內(nèi)存模式 + FreeSql CodeFirst 功能,用起來(lái)體驗(yàn)還是不錯(cuò)的。因?yàn)槊看味家w移結(jié)構(gòu),fsql 釋放數(shù)據(jù)就沒(méi)了。
13|0終于寫(xiě)完了
終于寫(xiě)完了,這篇文章是我有史以來(lái)編輯最長(zhǎng)時(shí)間的,歷時(shí) 4小時(shí)!!!原本我可以利用這 4小時(shí)編寫(xiě)一堆膠水代碼,卻非要寫(xiě)推廣的文章,真心希望正在使用的、善良的您能動(dòng)一動(dòng)小手指,把文章轉(zhuǎn)發(fā)一下,讓更多人知道 .NET 有這樣一個(gè)好用的 ORM 存在。謝謝了!!
FreeSql 開(kāi)源協(xié)議 MIT https://github.com/dotnetcore/FreeSql,可以商用,文檔齊全。QQ開(kāi)發(fā)群:4336577
CSRedisCore 說(shuō):FreeSql 的待遇也好太多了。
如果你有好的 ORM 實(shí)現(xiàn)想法,歡迎給作者留言討論,謝謝觀(guān)看!
總結(jié)
以上是生活随笔為你收集整理的[开源] .Net orm FreeSql 1.5.0 最新版本(番号:好久不见)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .NET 程序下锐浪报表 (Grid++
- 下一篇: 调试实战 —— dll 加载失败之全局变