FreeSql 開源釋出快一年了,目前主倉庫代碼量 64118 行,用 git 指令統計的指令如下:
find . "(" -name "*.cs" ")" -print | xargs wc -l
加上其他幾個擴充包的代碼,大約有 70000 行源碼。
倉庫位址:https://github.com/2881099/FreeSql
在金九銀十的日子,釋出了兩大重要支援更新,分别是 .NetFramework4.0 和 ODBC。
随着不斷的疊代更新,越來越穩定,也越來越強大。預計在一周年的時候(2020年1月1日)釋出 1.0 正式版本。
由于篇幅原因,太短則上不了首頁,同時也對不起觀衆,下面介紹一下最近更新的幾個較實用的功能:
ISelect.ToDelete/ToUpdate
IFreeSql 之 CRUD 方法,分别對應 IInsert、ISelect、IUpdate、IDelete。
IDelete 預設不支援導航對象,多表關聯等。ISelect.ToDelete 可将查詢對象轉為删除對象,以便支援導航對象或其他查詢功能删除資料,如下:
fsql.Select<T1>().Where(a => a.Options.xxx == 1).ToDelete().ExecuteAffrows();
注意:此方法不是将資料查詢到記憶體循環删除,上面的代碼産生如下 SQL 執行:
DELETE FROM `T1` WHERE id in (select a.id from T1 a left join Options b on b.t1id = a.id where b.xxx = 1)
複雜删除使用該方案的好處:
- 删除前可預覽測試資料,防止錯誤删除操作,實作所查、即所删;
- 支援更加複雜的删除操作(IDelete 預設隻支援簡單的操作),甚至在 ISelect 上使用 Limit(10) 将隻删除附合條件的前 10條記錄;
ToUpdate 功能大概相同。
GlobalFilter 全局過濾
FreeSql 基礎層實作了 Select/Update/Delete 可設定的全局過濾器功能。
public static AsyncLocal<Guid> TenantId { get; set; } = new AsyncLocal<Guid>();
fsql.GlobalFilter
.Apply<TestAddEnum>("test1", a => a.Id == TenantId.Value)
.Apply<AuthorTest>("test2", a => a.Id == 111)
.Apply<AuthorTest>("test3", a => a.Name == "11");
Apply 泛型參數可以設定為任何類型,當使用 Select/Update/Delete 方法時會進行過濾器比對嘗試(try catch):
- 比對成功的,将附加 where 條件;
- 比對失敗的,标記下次不再比對,避免性能損耗;
如何禁用?
fsql.Select<TestAddEnum>().ToList(); //所有生效
fsql.Select<TestAddEnum>().DisableGlobalFilter("test1").ToList(); //禁用 test1
fsql.Select<TestAddEnum>().DisableGlobalFilter().ToList(); //禁用所有
fsql.Update/Delete 方法效果同上。
倉儲過濾器(舊功能)
這是一個原先就支援了的功能。FreeSql.Repository 也同樣實作了過濾器功能,它不僅是查詢時過濾,連删除/修改/插入時都會進行驗證,避免資料安全問題。
注意:倉儲的過濾器與 IFreeSql.GlobalFilter 不是一個功能,可以同時生效
每個倉儲執行個體都有 IDataFilter 屬性,可利用其完成過濾器管理,它是獨立的修改後不影響全局。
public interface IDataFilter<TEntity> where TEntity : class {
IDataFilter<TEntity> Apply(string filterName, Expression<Func<TEntity, bool>> filterAndValidateExp);
IDisposable Enable(params string[] filterName);
IDisposable EnableAll();
IDisposable Disable(params string[] filterName);
IDisposable DisableAll();
bool IsEnabled(string filterName);
}
臨時禁用
using (repo1.DataFilter.Disable("test")) {
//在這段中,repo1 之 test 過濾器失效
}
//repo1 之 test 過濾器重新生效
過濾與驗證
假設我們有User(使用者)、Topic(主題)兩個實體,在領域類中定義了兩個倉儲:
var userRepository = fsql.GetGuidRepository<User>();
var topicRepository = fsql.GetGuidRepository<Topic>();
在開發過程中,總是擔心 topicRepository 的資料安全問題,即有可能查詢或操作到其他使用者的主題。是以我們在v0.0.7版本進行了改進,增加了 filter lambda 表達式參數。
var userRepository = fsql.GetGuidRepository<User>(a => a.Id == 1);
var topicRepository = fsql.GetGuidRepository<Topic>(a => a.UserId == 1);
- 在查詢/修改/删除時附加此條件,進而達到不會修改其他使用者的資料;
- 在添加時,使用表達式驗證資料的合法性,若不合法則抛出異常;
實體變化通知
該功能依附在 FreeSql.Repository 上實作的,對實體的變化進行統一轉發,以便實作全局或局部類似日志的功能。
全局設定:
fsql.SetDbContextOptions(opt => {
opt.OnEntityChange = report => {
Console.WriteLine(report);
};
});
單獨設定 DbContext 或者 UnitOfWork:
var ctx = fsql.CreateDbContext();
ctx.Options.OnEntityChange = report => {
Console.WriteLine(report);
};
var uow = fsql.CreateUnitOfWork();
uow.OnEntityChange = report => {
Console.WriteLine(report);
};
參數 report 是一個 List 集合,集合元素的類型定義如下:
public class EntityChangeInfo
{
public object Object { get; set; }
public EntityChangeType Type { get; set; }
}
public enum EntityChangeType { Insert, Update, Delete, SqlRaw }
變化類型- | 說明 |
---|---|
Insert | 實體對象被插入 |
Update | 實體對象被更新 |
Delete | 實體對象被删除 |
SqlRaw | 執行了SQL語句 |
SqlRaw 目前有兩處地方比較特殊:
- 多對多聯級更新導航屬性的時候,對中間表的全部删除操作;
- 通用倉儲類 BaseRepository 有一個 Delete 方法,參數為表達式,而并非實體;
int Delete(Expression<Func<TEntity, bool>> predicate);
DbContext.SaveChanges,或者 Repository 對實體的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都會最多觸發一次該事件。
更多功能
請移步更新日志:https://github.com/2881099/FreeSql/wiki/更新日志