实体类配置
每款 ORM 都会有自己一套实体类配置方法, 当项目的实体被多个 ORM 同时使用时将成为问题, 因为不可能做多套配置, FreeSql 提供了以下几种的方法, 免入侵式配置;
1, 如果你从数据库生成的实体, FreeSql 提供 IsConfigEntityFromDbFirst 参数, 可从数据库导入主键, 自键等配置信息;
- var ORM = new FreeSql.FreeSqlBuilder()
- .UseConnectionString(FreeSql.DataType.MySQL, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=10")
- .UseAutoSyncStructure(true)
- // 只需要在这里控制, 默认为关闭状态
- .UseConfigEntityFromDbFirst(true)
- .Build();
2, 如果你已经使用 EF 建好了实体模式, FreeSql 提供了从 EF 元数据导入;
- public static void ConfigEntity(this ICodeFirst codeFirst, IModel efmodel) {
- foreach (var type in efmodel.GetEntityTypes()) {
- codeFirst.ConfigEntity(type.ClrType, a => {
- // 表名
- var relationalTableName = type.FindAnnotation("Relational:TableName");
- if (relationalTableName != null) {
- a.Name(relationalTableName.Value?.ToString() ?? type.ClrType.Name);
- }
- foreach (var prop in type.GetProperties()) {
- var freeProp = a.Property(prop.Name);
- // 列名
- var relationalColumnName = prop.FindAnnotation("Relational:ColumnName");
- if (relationalColumnName != null) {
- freeProp.Name(relationalColumnName.Value?.ToString() ?? prop.Name);
- }
- // 主键
- freeProp.IsPrimary(prop.IsPrimaryKey());
- // 自增
- freeProp.IsIdentity(
- prop.ValueGenerated == ValueGenerated.Never ||
- prop.ValueGenerated == ValueGenerated.OnAdd ||
- prop.GetAnnotations().Where(z =>
- z.Name == "SqlServer:ValueGenerationStrategy" && z.Value.ToString().Contains("IdentityColumn") //sqlserver 自增
- || z.Value.ToString().Contains("IdentityColumn") // 其他数据库实现未经测试
- ).Any()
- );
- // 可空
- freeProp.IsNullable(prop.AfterSaveBehavior != PropertySaveBehavior.Throw);
- // 类型
- var relationalColumnType = prop.FindAnnotation("Relational:ColumnType");
- if (relationalColumnType != null) {
- var dbType = relationalColumnType.ToString();
- if (!string.IsNullOrEmpty(dbType)) {
- var maxLength = prop.FindAnnotation("MaxLength");
- if (maxLength != null)
- dbType += $"({maxLength})";
- freeProp.DbType(dbType);
- }
- }
- }
- });
- }
- }
3, 如果你使用了其他 ORM,FreeSql 提供 ConfigEntity, 使用类似 2 的做法来完成配置导入;
事务
FreeSql 提供了同线程事务, 对外开放事务.
同线程事务
假设用户购买了价值 100 元的商品:
第一步: 扣余额;
第二步: 扣库存;
第一步成功了, 到了第二步发现库存不足时, 事务可以回滚, 扣余额的数据将不生效.
- // 假设已经有了其他 wiki 页的 IFreeSql 声明
- ORM.Transaction(() => {
- var affrows = ORM.Update<User>().Set(a => a.Wealth - 100)
- .Where(a => a.Wealth>= 100)
- // 判断别让用户余额扣成负数
- .ExecuteAffrows();
- if (affrows <1) {
- throw new Exception("用户余额不足");
- // 抛出异常, 事务退出
- }
- affrows = ORM.Update<Goods>().Set(a => a.Stock - 1)
- .Where(a => a.Stock> 0)
- // 判断别让用库存扣成负数
- .ExecuteAffrows();
- if (affrows <1) {
- throw new Exception("商品库存不足");
- // 抛出异常, 回滚事务, 事务退出
- // 用户余额的扣除将不生效
- }
- // 程序执行在此处, 说明都扣成功了, 事务完成并提交
- });
注意与说明:
1, 数据库事务在线程挂载, 每个线程只可开启一个事务连接, 重复开启会获取线程已开启的事务;
2, 在事务代码过程中, 不可使用异步方法, 包括 FreeSql 提供的数据库异步方法, 否则线程将会切换事务不生效;
3,ORM.Transaction 有防止死锁机制, 60 秒事务未结束的, 将会被其他线程强行提交(不是回滚), 可能造成不完整的事务, 但仔细一想 60 秒还没完成的事务是什么原因呢? 如果嫌 60 秒太少了可以在重载方法的参数中设置;
指定事务对象
除了上面提供的同线程事务外, FreeSql 还提供了指定事务对象的方法, 将事务对象暴露给外部;
- ORM.Update<xxx>().WithTransaction(指定事务)
- .Set(a => a.Clicks + 1).ExecuteAffrows();
ISelect,IInsert,IUpdate,IDelete, 都支持 WithTransaction 方法.
仓储 Repository
dotnet add package FreeSql.Repository
1,IFreeSql 的扩展方法;
- var curd1 = ORM.GetRepository<Song, int>();
- var curd2 = ORM.GetGuidRepository<Song>();
2, 继承现实;
- public class SongRepository : BaseRepository<Song, int> {
- public SongRepository(IFreeSql ORM) : base(ORM) {}
- // 在这里增加 CURD 以外的方法
- }
3,Autofac 注入;
- public IServiceProvider ConfigureServices(IServiceCollection services) {
- services.AddSingleton<IFreeSql>(ORM);
- services.AddMvc();
- var builder = new ContainerBuilder();
- // 示范全局过滤的仓储类注入, 如果实体中不存在 Title 属性, 则条件不生效
- builder.RegisterFreeRepositoryAddFilter<Song>(() => a => a.Title == DateTime.Now.ToString() + System.Threading.Thread.CurrentThread.ManagedThreadId);
- builder.Populate(services);
- var container = builder.Build();
- return new AutofacServiceProvider(container);
- }
- // 在控制器使用
- public SongsController(GuidRepository<Song> repos1, GuidRepository<xxxx> repos2) {
- }
Autofac 注入方式实现了全局[过滤与验证] 的设定, 方便租户功能的设计;
表达式函数
In 查询
- var t1 = ORM.Select<xxx>().Where(a => new[] {
- 1, 2, 3
- }.Contains(a.testFieldInt)).ToSql();
- //SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime`
- //FROM `tb_topic` a
- //WHERE (a.`Id` in (1,2,3))
查找今天创建的数据
var t2 = ORM.Select<xxx>().Where(a => a.CreateTime.Date == DateTime.Now.Date).ToSql();
不提供 SqlFunc 之类的伪函数, 所支持的类型基本都可以使用对应的表达式函数, 例如 日期, 字符串, IN 查询, 数组 (PostgreSQL 的数组), 字典(PostgreSQL HStore) 等等.
安全性
1, 避免死锁的事务, 超时自动提交;
2, 未设置条件的删除, 更新不生效;
3, 仓储提供 filter 验证数据, 确保数据的安全性;
......
更多特性可前往 wiki 中心查看
- GitHub: https://github.com/2881099/FreeSql
- wiki: https://github.com/2881099/FreeSql/wiki/
来源: https://www.cnblogs.com/kellynic/p/10512734.html