FreeSql 开源发布快一年了, 目前主仓库代码量 64118 行, 用 Git 命令统计的命令如下:
find . "(" -name "*.cs" ")" -print | xargs wc -l
加上其他几个扩展包的代码, 大约有 70000 行源码.
仓库地址: https://github.com/2881099/FreeSql
在金九银十的日子, 发布了两大重要支持更新, 分别是 .NetFramework4.0 和 ODBC.
由于篇幅原因, 太短则上不了首页, 同时也对不起观众, 下面介绍一下最近更新的几个较实用的功能:
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://www.cnblogs.com/kellynic/p/11768147.html