FreeSql 過濾器使用介紹
FreeSql.Repository 實現了過濾器,它不僅是查詢時過濾,連刪除/修改/插入時都會進行驗證,避免資料安全問題。
過濾器
目前過濾器依附在倉儲層實現,每個倉儲例項都有 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 (repos1.DataFilter.Disable("test")) { //在這段中,repos1 之 test 過濾器失效 } //repos1 之 test 過濾器重新生效
如何使用倉儲
dotnet add package FreeSql.Repository
var fsql = new FreeSql.FreeSqlBuilder() .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10") .UseLogger(loggerFactory.CreateLogger<IFreeSql>()) .UseAutoSyncStructure(true) //自動遷移實體的結構到資料庫 .Build(); public class Song { [Column(IsIdentity = true)] public int Id { get; set; } public string Title { get; set; } }
1、IFreeSql 的擴充套件方法;
var curd1 = fsql.GetRepository<Song, int>(); var curd2 = fsql.GetGuidRepository<Song>();
2、繼承現實;
public class SongRepository : BaseRepository<Song, int> { public SongRepository(IFreeSql fsql) : base(fsql) {} //在這裡增加 CURD 以外的方法 }
3、Autofac 注入,使用方法繼續往下看【全域性過濾器】;
過濾與驗證
假設我們有User(使用者)、Topic(主題)兩個實體,在領域類中定義了兩個倉儲:
var userRepository = fsql.GetGuidRepository<User>(); var topicRepository = fsql.GetGuidRepository<Topic>();
在開發過程中,總是擔心 topicRepository 的資料安全問題,即有可能查詢或操作到其他使用者的主題。因此我們在v0.0.7版本進行了改進,增加了 filter lambad 表示式引數。
var userRepository = fsql.GetGuidRepository<User>(a => a.Id == 1); var topicRepository = fsql.GetGuidRepository<Topic>(a => a.UserId == 1);
- 在查詢/修改/刪除時附加此條件,從而達到不會修改其他使用者的資料;
- 在新增時,使用表示式驗證資料的合法性,若不合法則丟擲異常;
全域性過濾器
全域性過濾器,可幫助實現“軟刪除”、“租戶”等設計,目前使用 Autofac 注入的方式實現的全域性過濾器。
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddSingleton<IFreeSql>(fsql); services.AddMvc(); var builder = new ContainerBuilder(); //示範全域性過濾的倉儲類注入,如果實體中不存在 Title 屬性,則條件不生效 builder.RegisterFreeRepository(filter => filter.Apply<Song>("test", a => a.Title == DateTime.Now.ToString() + Thread.CurrentThread.ManagedThreadId) ); builder.Populate(services); var container = builder.Build(); return new AutofacServiceProvider(container); } public class xxxx { public int Id { get; set; } } public class Song { [Column(IsIdentity = true)] public int Id { get; set; } public string Title { get; set; } } //在控制器使用 public SongsController(GuidRepository<Song> repos1, GuidRepository<xxxx> repos2) { //在此打斷點,除錯 }
第一次請求:
repos1.Select.ToSql()
"SELECT a."Id", a."Title" \r\nFROM "Song" a \r\nWHERE (a."Title" = strftime('%Y-%m-%d %H:%M.%f',datetime(current_timestamp,'localtime')) || 21)"
repos2.Select.ToSql()
"SELECT a."Id" \r\nFROM "xxxx" a"
第二次請求:
repos1.Select.ToSql()
"SELECT a."Id", a."Title" \r\nFROM "Song" a \r\nWHERE (a."Title" = strftime('%Y-%m-%d %H:%M.%f',datetime(current_timestamp,'localtime')) || 4)"
repos2.Select.ToSql()
"SELECT a."Id" \r\nFROM "xxxx" a"
//禁用過濾器
repos1.DataFilter.Disable("test")
repos1.Select.ToSql()
"SELECT a."Id", a."Title" \r\nFROM "Song" a"
測試總結:
1、注入的變數值在使用時有了動態變化,每次獲取時都是新的(Thread.CurrentThread.ManagedThreadId);
2、設定的全域性過濾,若某實體不存在表示式函式中的欄位時,不會生效(如上xxxx不存在Title);
3、使用 DataFilter.Disable("test") 可臨時關閉過濾器的效果,使用 DataFilter.Enable("test") 可重新開啟;
4、倉儲物件建立時,從全域性過濾器copy進來,然後自己管理自己。修改後不影響其他或全域性設定。
github 原始碼:https://github.com/2881099/FreeSql