From 157dbc283e22475994024d00dc0b1e396c7a484f Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Sat, 29 Feb 2020 13:05:48 +0800 Subject: [PATCH] update readme --- Docs/1. quickstart.md | 97 --------- Docs/2 入门.md | 28 --- Docs/2.1 安装FreeSql | 0 Docs/codefirst.md | 193 ----------------- Docs/dbfirst.md | 109 ---------- Docs/delete.md | 82 -------- Docs/expression.md | 177 ---------------- Docs/generator.md | 103 --------- Docs/insert.md | 79 ------- Docs/select.md | 259 ----------------------- Docs/update.md | 129 ------------ FreeSql.DbContext/readme.md | 396 +++++++++++++++-------------------- FreeSql.Repository/readme.md | 288 +++++++++++++++++-------- readme.md | 227 +++++++++----------- 14 files changed, 455 insertions(+), 1712 deletions(-) delete mode 100644 Docs/1. quickstart.md delete mode 100644 Docs/2 入门.md delete mode 100644 Docs/2.1 安装FreeSql delete mode 100644 Docs/codefirst.md delete mode 100644 Docs/dbfirst.md delete mode 100644 Docs/delete.md delete mode 100644 Docs/expression.md delete mode 100644 Docs/generator.md delete mode 100644 Docs/insert.md delete mode 100644 Docs/select.md delete mode 100644 Docs/update.md diff --git a/Docs/1. quickstart.md b/Docs/1. quickstart.md deleted file mode 100644 index c9c021af..00000000 --- a/Docs/1. quickstart.md +++ /dev/null @@ -1,97 +0,0 @@ -# FreeSql 简介 - -FreeSql 是轻量化、可扩展和跨平台版的 .NETStandard 数据访问技术实现。 - -FreeSql 可用作对象关系映射程序 (O/RM),以便于开发人员能够使用 .NETStandard 对象来处理数据库,不必经常编写大部分数据访问代码。 - -FreeSql 支持 MySql/SqlServer/PostgreSQL 数据库技术实现。 - -## 模型 - -FreeSql 使用模型执行数据访问,模型由实体类表示数据库表或视图,用于查询和保存数据。 有关详细信息,请参阅创建模型。 - -可从现有数据库生成实体模型,提供 IDbFirst 生成实体模型。 - -或者手动创建模型,基于模型创建或修改数据库,提供 ICodeFirst 同步结构的 API(甚至可以做到开发阶段自动同步)。 - -```csharp -using FreeSql.DataAnnotations; -using System; - -public class Blog -{ - [Column(IsIdentity = true, IsPrimary = true)] - public int BlogId { get; set; } - public string Url { get; set; } - public int Rating { get; set; } -} - -public class Post -{ - public int PostId { get; set; } - public string Title { get; set; } - public string Content { get; set; } - - public int BlogId { get; set; } - public Blog Blog { get; set; } -} -``` - -## 声明 - -```csharp -var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;" + - "Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=10"; - -IFreeSql fsql = new FreeSql.FreeSqlBuilder() - .UseConnectionString(FreeSql.DataType.MySql, connstr) - .UseSlave("connectionString1", "connectionString2") //使用从数据库,支持多个 - - .UseLogger(null) //使用日志,不指定默认输出控制台 ILogger - .UseCache(null) //使用缓存,不指定默认使用内存 IDistributedCache - - .UseAutoSyncStructure(true) //自动同步实体结构到数据库 - .UseSyncStructureToLower(true) //转小写同步结构 - .Build(); -``` - -注意: IFreeSql 在项目中应以单例声明,而不是在每次使用的时候创建。 - -## 查询 - -```csharp -var blogs = fsql.Select - .Where(b => b.Rating > 3) - .OrderBy(b => b.Url) - .ToList(); -``` - -## 插入 - -```csharp -var blog = new Blog { Url = "http://sample.com" }; -blog.BlogId = (int)fsql.Insert() - .AppendData(blog) - .ExecuteIdentity(); -``` - -## 更新 - -```csharp -fsql.Update() - .Set(b => b.Url, "http://sample2222.com") - .Where(b => b.Url == "http://sample.com") - .ExecuteAffrows(); -``` - -## 删除 - -```csharp -fsql.Delete() - .Where(b => b.Url == "http://sample.com") - .ExecuteAffrows(); -``` - -## 后续步骤 - -有关介绍性教程,请参阅 [FreeSql 入门]()。 diff --git a/Docs/2 入门.md b/Docs/2 入门.md deleted file mode 100644 index 00857ee9..00000000 --- a/Docs/2 入门.md +++ /dev/null @@ -1,28 +0,0 @@ -# FreeSql 入门 - -## 安装 - -FreeSql 是一个 .NET Standard 2.0 库,支持 .NET Framework 4.6.1 或 .NET Core 或更高版本的应用程序。 - -```shell -dotnet add package FreeSql -``` - -或者 - -```shell -Install-Package FreeSql -``` - -## 入门教程 - -FreeSql 可基于现有数据库创建模型,也可基于模型创建数据库。 提供的教程演示了这两种方法。 - -* .NET Core 控制台应用 - -- * 新建数据库 - -* ASP.NET Core 应用 - -- * 新建数据库 -- * 现有数据库 \ No newline at end of file diff --git a/Docs/2.1 安装FreeSql b/Docs/2.1 安装FreeSql deleted file mode 100644 index e69de29b..00000000 diff --git a/Docs/codefirst.md b/Docs/codefirst.md deleted file mode 100644 index 34d25cee..00000000 --- a/Docs/codefirst.md +++ /dev/null @@ -1,193 +0,0 @@ -# CodeFirst - -## 类型映射 - -| csharp | MySql | SqlServer | PostgreSQL | oracle | -| - | - | - | - | - | -| bool \| bool? | bit(1) | bit | bool | number(1) | -| sbyte \| sbyte? | tinyint(3) | smallint | int2 | number(4) | -| short \| short? | smallint(6) | smallint | int2 | number(6) | -| int \| int? | int(11) | int | int4 | number(11) | -| long \| long? | bigint(20) | bigint | int8 | number(21) | -| byte \| byte? | tinyint(3) unsigned | tinyint | int2 | number(3) | -| ushort \| ushort? | smallint(5) unsigned | int | int4 | number(5) | -| uint \| uint? | int(10) unsigned | bigint | int8 | number(10) | -| ulong \| ulong? | bigint(20) unsigned | decimal(20,0) | numeric(20,0) | number(20) | -| double \| double? | double | float | float8 | float(126) | -| float \| float? | float | real | float4 | float(63) | -| decimal \| decimal? | decimal(10,2) | decimal(10,2) | numeric(10,2) | number(10,2) | -| Guid \| Guid? | char(36) | uniqueidentifier | uuid | char(36 CHAR) | -| TimeSpan \| TimeSpan? | time | time | time | interval day(2) to second(6) | -| DateTime \| DateTime? | datetime | datetime | timestamp | timestamp(6) | -| DateTimeOffset \| DateTimeOffset? | - | - | datetimeoffset | timestamp(6) with local time zone | -| Enum \| Enum? | enum | int | int4 | number(16) | -| FlagsEnum \| FlagsEnum? | set | bigint | int8 | number(32) | -| byte[] | varbinary(255) | varbinary(255) | bytea | blob | -| string | varchar(255) | nvarchar(255) | varchar(255) | nvarchar2(255) | -| MygisPoint | point | - | - | - | -| MygisLineString | linestring | - | - | - | -| MygisPolygon | polygon | - | - | - | -| MygisMultiPoint | multipoint | - | - | - | -| MygisMultiLineString | multilinestring | - | - | - | -| MygisMultiPolygon | multipolygon | - | - | - | -| BitArray | - | - | varbit(64) | - | -| NpgsqlPoint \| NpgsqlPoint? | - | - | point | - | -| NpgsqlLine \| NpgsqlLine? | - | - | line | - | -| NpgsqlLSeg \| NpgsqlLSeg? | - | - | lseg | - | -| NpgsqlBox \| NpgsqlBox? | - | - | box | - | -| NpgsqlPath \| NpgsqlPath? | - | - | path | - | -| NpgsqlPolygon \| NpgsqlPolygon? | - | - | polygon | - | -| NpgsqlCircle \| NpgsqlCircle? | - | - | circle | - | -| (IPAddress Address, int Subnet) \| (IPAddress Address, int Subnet)? | - | - | cidr | - | -| IPAddress | - | - | inet | - | -| PhysicalAddress | - | - | macaddr | - | -| NpgsqlRange\ \| NpgsqlRange\? | - | - | int4range | - | -| NpgsqlRange\ \| NpgsqlRange\? | - | - | int8range | - | -| NpgsqlRange\ \| NpgsqlRange\? | - | - | numrange | - | -| NpgsqlRange\ \| NpgsqlRange\? | - | - | tsrange | - | -| PostgisPoint | - | - | geometry | - | -| PostgisLineString | - | - | geometry | - | -| PostgisPolygon | - | - | geometry | - | -| PostgisMultiPoint | - | - | geometry | - | -| PostgisMultiLineString | - | - | geometry | - | -| PostgisMultiPolygon | - | - | geometry | - | -| PostgisGeometry | - | - | geometry | - | -| PostgisGeometryCollection | - | - | geometry | - | -| Dictionary | - | - | hstore | - | -| JToken | - | - | jsonb | - | -| JObject | - | - | jsonb | - | -| JArray | - | - | jsonb | - | -| 数组 | - | - | 以上所有类型都支持 | - | - -> 以上类型和长度是默认值,可手工设置,如 string 属性可指定 [Column(DbType = "varchar(max)")] - -```csharp -IFreeSql fsql = 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) - - .UseMonitorCommand( - cmd => { - Console.WriteLine(cmd.CommandText); - }, //监听SQL命令对象,在执行前 - (cmd, traceLog) => { - Console.WriteLine(traceLog); - }) //监听SQL命令对象,在执行后 - .Build(); -``` - -### 自动同步实体结构【开发环境必备】 - -自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改 - -```csharp -fsql.CodeFirst.IsAutoSyncDataStructure = true; -``` - -> 此功能默认为开启状态,发布正式环境后,请修改此设置 - -> 虽然【自动同步实体结构】功能开发非常好用,但是有个坏处,就是数据库后面会很乱,没用的字段一大堆 - -### 手工同步实体结构 - -| 实体&表对比 | 添加 | 改名 | 删除 | -| - | - | - | - | -| | √ | √ | X | - -| 实体属性&字段对比 | 添加 | 修改可空 | 修改自增 | 修改类型 | 改名 | 删除 | -| - | - | - | - | - | - | - | -| | √ | √ | √ | √ | √ | X | - -> 为了保证安全,不提供删除字段 - - -1、提供方法对比实体,与数据库中的变化部分 - -```csharp -var t1 = mysql.CodeFirst.GetComparisonDDLStatements(); - -class Topic { - [Column(IsIdentity = true, IsPrimary = true)] - public int Id { get; set; } - public int Clicks { get; set; } - public TestTypeInfo Type { get; set; } - public string Title { get; set; } - public DateTime CreateTime { get; set; } - public ushort fusho { get; set; } -} -``` -```sql -CREATE TABLE IF NOT EXISTS `cccddd`.`Topic` ( - `Id` INT(11) NOT NULL AUTO_INCREMENT, - `Clicks` INT(11) NOT NULL, - `Title` VARCHAR(255), - `CreateTime` DATETIME NOT NULL, - `fusho` SMALLINT(5) UNSIGNED NOT NULL, - PRIMARY KEY (`Id`) -) Engine=InnoDB CHARACTER SET utf8; -``` - -2、指定实体的表名 - -指定 Name 后,实体类名变化不影响数据库对应的表 -```csharp -[Table(Name = "tb_topic111")] -class Topic { - //... -} -``` - -3、无指定实体的表名,修改实体类名 - -指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】 - -```csharp -[Table(OldName = "Topic")] -class Topic2 { - //... -} -``` -```sql -ALTER TABLE `cccddd`.`Topic` RENAME TO `cccddd`.`Topic2`; -``` - -4、修改属性的类型 - -把 Id 类型改为 uint 后 -```sql -ALTER TABLE `cccddd`.`Topic2` MODIFY `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT; -``` -```csharp -[Column(DbType = "varchar(128)")] -public string Title { get; set; } -``` -```sql -ALTER TABLE `cccddd`.`Topic2` MODIFY `Title2` VARCHAR(128); -``` - -5、指定属性的字段名 - -这样指定后,修改实体的属性名不影响数据库对应的列 -```csharp -[Column(Name = "titl2")] -public string Title { get; set; } -``` - -6、无指定属性的字段名,修改属性名 - -指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】 - -```csharp -[Column(OldName = "Title2")] -public string Title { get; set; } -``` -```sql -ALTER TABLE `cccddd`.`Topic2` CHANGE COLUMN `Title2` `Title` VARCHAR(255); -``` - -7、提供方法同步结构 - -```csharp -var t2 = fsql.CodeFirst.SyncStructure(); -//同步实体类型到数据库 -``` diff --git a/Docs/dbfirst.md b/Docs/dbfirst.md deleted file mode 100644 index 0c9f119a..00000000 --- a/Docs/dbfirst.md +++ /dev/null @@ -1,109 +0,0 @@ -# DbFirst - -```csharp -IFreeSql fsql = 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") - .Build(); -``` - -### 获取所有数据库 - -```csharp -var t1 = fsql.DbFirst.GetDatabases(); -//返回字符串数组, ["cccddd", "test"] -``` - -### 获取指定数据库的表信息 - -```csharp -var t2 = fsql.DbFirst.GetTablesByDatabase(fsql.DbFirst.GetDatabases()[0]); -//返回包括表、列详情、主键、唯一键、索引、外键 -``` - -# 生成器 - -生成器是基于 dbfirst 开发的辅助工具,适用老项目一键生成实体。生成器采用模板的方式,作者实现了三种生成模板: - -| 模板名称 | 路径 | 类型映射 | 外键导航属性 | 缓存管理 | 失血 | 贫血 | 充血 | -| ------------- | - | - |- | - |- | - |- | -| simple-entity | ../Templates/MySql/simple-entity | √ | X | X | √ | X | X | -| simple-entity-navigation-object | ../Templates/MySql/simple-entity-navigation-object | √ | √ | X | √ | X | X | -| rich-entity-navigation-object | ../Templates/MySql/rich-entity-navigation-object | √ | √ | √ | X | √ | X | - -> 更多模板逐步开发中。。。 - -```csharp -//创建模板生成类实现 -var gen = new FreeSql.Generator.TemplateGenerator(); -gen.Build(fsql.DbFirst, - @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity", //模板目录(事先下载) - @"C:\Users\28810\Desktop\新建文件夹 (9)", //生成后保存的目录 - "cccddd" //数据库 -); -``` - -## 模板语法 - -```html - - -{#title} - - - - -{#表达式} -{##表达式} 当表达式可能发生runtime错误时使用,性能没有上面的高 - - -{include ../header.html} -
-

aaa

-

bbb {#i}

-

ccc {#i}

-
- - -{module module_name1 parms1, 2, 3...} -{/module} -{module module_name2 parms1, 2, 3...} -{/module} - - -{import ../module.html as myname} -{#myname.module_name(parms1, 2, 3...)} - - -{extends ../inc/layout.html} -{block body}{/block} - - -{% -for (var a = 0; a < 100; a++) - print(a); -%} - - -{if i === 50} -{elseif i > 60} -{else} -{/if} - - -{for i 1,101} 可自定义名 {for index2 表达式1 in 表达式2} - -{for item,index in items} 可选参数称 index - 可自定义名 {for item2, index99 in 数组表达式} - -{for key,item,index on json} 可选参数 item, index, - 可自定义名 {for key2, item2, index99 in 对象表达式} -{/for} - - -{miss} -此块内容不被bmw.js解析 -{/miss} - - - -``` \ No newline at end of file diff --git a/Docs/delete.md b/Docs/delete.md deleted file mode 100644 index 31c40d99..00000000 --- a/Docs/delete.md +++ /dev/null @@ -1,82 +0,0 @@ -# 删除数据 - -| 方法 | 返回值 | 参数 | 描述 | -| - | - | - | - | -| Where | \ | Lambda | 表达式条件,仅支持实体基础成员(不包含导航对象) | -| Where | \ | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) | -| Where | \ | T1 \| IEnumerable | 传入实体或集合,将其主键作为条件 | -| WhereExists | \ | ISelect | 子查询是否存在 | -| ToSql | string | | 返回即将执行的SQL语句 | -| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 | -| ExecuteDeleted | List\ | | 执行SQL语句,返回被删除的记录 | - -### 测试代码 - -```csharp -IFreeSql fsql = 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") - .Build(); -IDelete delete => fsql.Delete(); - -[Table(Name = "tb_topic")] -class Topic { - [Column(IsIdentity = true, IsPrimary = true)] - public int Id { get; set; } - public int Clicks { get; set; } - public TestTypeInfo Type { get; set; } - public string Title { get; set; } - public DateTime CreateTime { get; set; } -} -``` - -### 动态条件 -```csharp -Delete(object dywhere) -``` -dywhere 支持 - -* 主键值 -* new[] { 主键值1, 主键值2 } -* Topic对象 -* new[] { Topic对象1, Topic对象2 } -* new { id = 1 } - -```csharp -var t1 = fsql.Delete(new[] { 1, 2 }).ToSql(); -//DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2) - -var t2 = fsql.Delete(new Topic { Id = 1, Title = "test" }).ToSql(); -//DELETE FROM `tb_topic` WHERE (`Id` = 1) - -var t3 = fsql.Delete(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).ToSql(); -//DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2) - -var t4 = fsql.Delete(new { id = 1 }).ToSql(); -//DELETE FROM `tb_topic` WHERE (`Id` = 1) -``` - -### 删除条件 - -```csharp -var t5 = delete.Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); -//DELETE FROM `tb_topic` WHERE (`Id` = 1) - -var t6 = delete.Where("id = ?id", new { id = 1 }).ToSql().Replace("\r\n", ""); -//DELETE FROM `tb_topic` WHERE (id = ?id) - -var item = new Topic { Id = 1, Title = "newtitle" }; -var t7 = delete.Where(item).ToSql().Replace("\r\n", ""); -//DELETE FROM `tb_topic` WHERE (`Id` = 1) - -var items = new List(); -for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); -var t8 = delete.Where(items).ToSql().Replace("\r\n", ""); -//DELETE FROM `tb_topic` WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10)) -``` - -### 执行命令 - -| 方法 | 返回值 | 参数 | 描述 | -| - | - | - | - | -| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 | -| ExecuteDeleted | List\ | | 执行SQL语句,返回被删除的记录 | \ No newline at end of file diff --git a/Docs/expression.md b/Docs/expression.md deleted file mode 100644 index 9566aa3f..00000000 --- a/Docs/expression.md +++ /dev/null @@ -1,177 +0,0 @@ -# 表达式函数 -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | 功能说明 | -| - | - | - | - | - | - | -| a ? b : c | case when a then b else c end | case when a then b else c end | case when a then b else c end | case when a then b else c end | a成立时取b值,否则取c值 | -| a ?? b | ifnull(a, b) | isnull(a, b) | coalesce(a, b) | nvl(a, b) | 当a为null时,取b值 | -| 数字 + 数字 | a + b | a + b | a + b | a + b | 数字相加 | -| 数字 + 字符串 | concat(a, b) | cast(a as varchar) + cast(b as varchar) | case(a as varchar)\|\| b | a\|\| b | 字符串相加,a或b任意一个为字符串时 | -| a - b | a - b | a - b | a - b | a - b | 减 -| a * b | a * b | a * b | a * b | a * b | 乘 -| a / b | a / b | a / b | a / b | a / b | 除 -| a % b | a % b | a % b | a % b | mod(a,b) | 模 - -> 等等... - -### 数组 -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | 功能说明 | -| - | - | - | - | - | - | -| a.Length | - | - | case when a is null then 0 else array_length(a,1) end | - | 数组长度 | -| 常量数组.Length | - | - | array_length(array[常量数组元素逗号分割],1) | - | 数组长度 | -| a.Any() | - | - | case when a is null then 0 else array_length(a,1) end > 0 | - | 数组是否为空 | -| 常量数组.Contains(b) | b in (常量数组元素逗号分割) | b in (常量数组元素逗号分割) | b in (常量数组元素逗号分割) | b in (常量数组元素逗号分割) | IN查询 | -| a.Contains(b) | - | - | a @> array[b] | - | a数组是否包含b元素 | -| a.Concat(b) | - | - | a \|\| b | - | 数组相连 | -| a.Count() | - | - | 同 Length | - | 数组长度 | - -### 字典 Dictionary -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | 功能说明 | -| - | - | - | - | - | - | -| a.Count | - | - | case when a is null then 0 else array_length(akeys(a),1) end | - | 字典长度 | -| a.Keys | - | - | akeys(a) | - | 返回字典所有key数组 | -| a.Values | - | - | avals(a) | - | 返回字典所有value数组 | -| a.Contains(b) | - | - | a @> b | - | 字典是否包含b -| a.ContainsKey(b) | - | - | a? b | - | 字典是否包含key -| a.Concat(b) | - | - | a \|\| b | - | 字典相连 | -| a.Count() | - | - | 同 Count | - | 字典长度 | - -### JSON JToken/JObject/JArray -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | 功能说明 | -| - | - | - | - | - | - | -| a.Count | - | - | jsonb_array_length(coalesce(a, '[])) | - | json数组类型的长度 | -| a.Any() | - | - | jsonb_array_length(coalesce(a, '[])) > 0 | - | json数组类型,是否为空 | -| a.Contains(b) | - | - | coalesce(a, '{}') @> b::jsonb | - | json中是否包含b | -| a.ContainsKey(b) | - | - | coalesce(a, '{}') ? b | - | json中是否包含键b | -| a.Concat(b) | - | - | coalesce(a, '{}') || b::jsonb | - | 连接两个json | -| Parse(a) | - | - | a::jsonb | - | 转化字符串为json类型 | - -### 字符串对象 -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | 功能说明 | -| - | - | - | - | - | - | -| string.Empty | '' | '' | '' | 空字符串表示 | -| string.IsNullOrEmpty(a) | (a is null or a = '') | (a is null or a = '') | (a is null or a = '') | (a is null or a = '') | 空字符串表示 | -| a.CompareTo(b) | strcmp(a, b) | - | case when a = b then 0 when a > b then 1 else -1 end | case when a = b then 0 when a > b then 1 else -1 end | 比较a和b大小 | -| a.Contains('b') | a like '%b%' | a like '%b%' | a ilike'%b%' | a like '%b%' | a是否包含b | -| a.EndsWith('b') | a like '%b' | a like '%b' | a ilike'%b' | a like '%b' | a尾部是否包含b | -| a.IndexOf(b) | locate(a, b) - 1 | locate(a, b) - 1 | strpos(a, b) - 1 | instr(a, b, 1, 1) - 1 | 查找a中出现b的位置 | -| a.Length | char_length(a) | len(a) | char_length(a) | length(a) | 返回a的字符串长度 | -| a.PadLeft(b, c) | lpad(a, b, c) | - | lpad(a, b, c) | lpad(a, b, c) | 在a的左侧充字符c,直到字符串长度大于b | -| a.PadRight(b, c) | rpad(a, b, c) | - | rpad(a, b, c) | rpad(a, b, c) | 在a的右侧充字符c,直到字符串长度大于b | -| a.Replace(b, c) | replace(a, b, c) | replace(a, b, c) | replace(a, b, c) | replace(a, b, c) | 将a中字符串b,替换成c | -| a.StartsWith('b') | a like 'b%' | a like 'b%' | a ilike'b%' | a like 'b%' | a头部是否包含b | -| a.Substring(b, c) | substr(a, b, c + 1) | substring(a, b, c + 1) | substr(a, b, c + 1) | substr(a, b, c + 1) | 截取a中位置b到c的内容 | -| a.ToLower | lower(a) | lower(a) | lower(a) | lower(a) | 转小写 | -| a.ToUpper | upper(a) | upper(a) | upper(a) | upper(a) | 转大写 | -| a.Trim | trim(a) | trim(a) | trim(a) | trim(a) | 移除两边字符 | -| a.TrimEnd | rtrim(a) | rtrim(a) | rtrim(a) | rtrim(a) | 移除左侧指定字符 | -| a.TrimStart | ltrim(a) | ltrim(a) | ltrim(a) | ltrim(a) | 移除右侧指定字符 | - -### 日期对象 -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | -| - | - | - | - | - | -| DateTime.Now | now() | getdate() | current_timestamp | systimestamp | -| DateTime.UtcNow | utc_timestamp() | getutcdate() | (current_timestamp at time zone 'UTC') | sys_extract_utc(systimestamp) | -| DateTime.Today | curdate | convert(char(10),getdate(),120) | current_date | trunc(systimestamp) | -| DateTime.MaxValue | cast('9999/12/31 23:59:59' as datetime) | '9999/12/31 23:59:59' | '9999/12/31 23:59:59'::timestamp | to_timestamp('9999-12-31 23:59:59','YYYY-MM-DD HH24:MI:SS.FF6') | -| DateTime.MinValue | cast('0001/1/1 0:00:00' as datetime) | '1753/1/1 0:00:00' | '0001/1/1 0:00:00'::timestamp | to_timestamp('0001-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS.FF6') | -| DateTime.Compare(a, b) | a - b | a - b | extract(epoch from a::timestamp-b::timestamp) | extract(day from (a-b)) | -| DateTime.DaysInMonth(a, b) | dayofmonth(last_day(concat(a, '-', b, '-1'))) | datepart(day, dateadd(day, -1, dateadd(month, 1, cast(a as varchar) + '-' + cast(b as varchar) + '-1'))) | extract(day from (a || '-' || b || '-01')::timestamp+'1 month'::interval-'1 day'::interval) | cast(to_char(last_day(a||'-'||b||'-01'),'DD') as number) | -| DateTime.Equals(a, b) | a = b | a = b | a = b | a = b | -| DateTime.IsLeapYear(a) | a%4=0 and a%100<>0 or a%400=0 | a%4=0 and a%100<>0 or a%400=0 | a%4=0 and a%100<>0 or a%400=0 | mod(a,4)=0 AND mod(a,100)<>0 OR mod(a,400)=0 | -| DateTime.Parse(a) | cast(a as datetime) | cast(a as datetime) | a::timestamp | to_timestamp(a,'YYYY-MM-DD HH24:MI:SS.FF6') | -| a.Add(b) | date_add(a, interval b microsecond) | dateadd(millisecond, b / 1000, a) | a::timestamp+(b||' microseconds')::interval | 增加TimeSpan值 | a + b | -| a.AddDays(b) | date_add(a, interval b day) | dateadd(day, b, a) | a::timestamp+(b||' day')::interval | a + b | -| a.AddHours(b) | date_add(a, interval b hour) | dateadd(hour, b, a) | a::timestamp+(b||' hour')::interval | a + b/24 | -| a.AddMilliseconds(b) | date_add(a, interval b*1000 microsecond) | dateadd(millisecond, b, a) | a::timestamp+(b||' milliseconds')::interval | a + b/86400000 | -| a.AddMinutes(b) | date_add(a, interval b minute) | dateadd(minute, b, a) | a::timestamp+(b||' minute')::interval | a + b/1440 | -| a.AddMonths(b) | date_add(a, interval b month) | dateadd(month, b, a) | a::timestamp+(b||' month')::interval | add_months(a,b) | -| a.AddSeconds(b) | date_add(a, interval b second) | dateadd(second, b, a) | a::timestamp+(b||' second')::interval | a + b/86400 | -| a.AddTicks(b) | date_add(a, interval b/10 microsecond) | dateadd(millisecond, b / 10000, a) | a::timestamp+(b||' microseconds')::interval | a + b/86400000000 | -| a.AddYears(b) | date_add(a, interval b year) | dateadd(year, b, a) | a::timestamp+(b||' year')::interval | add_months(a,b*12) | -| a.Date | cast(date_format(a, '%Y-%m-%d') as datetime) | convert(char(10),a,120) | a::date | trunc(a) | -| a.Day | dayofmonth(a) | datepart(day, a) | extract(day from a::timestamp) | cast(to_char(a,'DD') as number) | -| a.DayOfWeek | dayofweek(a) | datepart(weekday, a) - 1 | extract(dow from a::timestamp) | case when to_char(a)='7' then 0 else cast(to_char(a) as number) end | -| a.DayOfYear | dayofyear(a) | datepart(dayofyear, a) | extract(doy from a::timestamp) | cast(to_char(a,'DDD') as number) | -| a.Hour | hour(a) | datepart(hour, a) | extract(hour from a::timestamp) | cast(to_char(a,'HH24') as number) | -| a.Millisecond | floor(microsecond(a) / 1000) | datepart(millisecond, a) | extract(milliseconds from a::timestamp)-extract(second from a::timestamp)*1000 | cast(to_char(a,'FF3') as number) | -| a.Minute | minute(a) | datepart(minute, a) | extract(minute from a::timestamp) | cast(to_char(a,'MI') as number) | -| a.Month | month(a) | datepart(month, a) | extract(month from a::timestamp) | cast(to_char(a,'FF3') as number) | -| a.Second | second(a) | datepart(second, a) | extract(second from a::timestamp) | cast(to_char(a,'SS') as number) | -| a.Subtract(b) | timestampdiff(microsecond, b, a) | datediff(millisecond, b, a) * 1000 | (extract(epoch from a::timestamp-b::timestamp)*1000000) | a - b | -| a.Ticks | timestampdiff(microsecond, '0001-1-1', a) * 10 | datediff(millisecond, '1970-1-1', a) * 10000 + 621355968000000000 | extract(epoch from a::timestamp)*10000000+621355968000000000 | cast(to_char(a,'FF7') as number) | -| a.TimeOfDay | timestampdiff(microsecond, date_format(a, '%Y-%m-%d'), a) | '1970-1-1 ' + convert(varchar, a, 14) | extract(epoch from a::time)*1000000 | a - trunc(a) | -| a.Year | year(a) | datepart(year, a) | extract(year from a::timestamp) | 年 | cast(to_char(a,'YYYY') as number) | -| a.Equals(b) | a = b | a = b | a = b | a = b | -| a.CompareTo(b) | a - b | a - b | a - b | a - b | -| a.ToString() | date_format(a, '%Y-%m-%d %H:%i:%s.%f') | convert(varchar, a, 121) | to_char(a, 'YYYY-MM-DD HH24:MI:SS.US') | to_char(a,'YYYY-MM-DD HH24:MI:SS.FF6') | - -### 时间对象 -| 表达式 | MySql(微秒) | SqlServer(秒) | PostgreSQL(微秒) | Oracle(Interval day(9) to second(7)) | -| - | - | - | - | - | -| TimeSpan.Zero | 0 | 0 | - | 0微秒 | numtodsinterval(0,'second') | -| TimeSpan.MaxValue | 922337203685477580 | 922337203685477580 | - | numtodsinterval(233720368.5477580,'second') | -| TimeSpan.MinValue | -922337203685477580 | -922337203685477580 | - | numtodsinterval(-233720368.5477580,'second') | -| TimeSpan.Compare(a, b) | a - b | a - b | - | extract(day from (a-b)) | -| TimeSpan.Equals(a, b) | a = b | a = b | - | a = b | -| TimeSpan.FromDays(a) | a * 1000000 * 60 * 60 * 24 | a * 1000000 * 60 * 60 * 24 | - | numtodsinterval(a*86400,'second') | -| TimeSpan.FromHours(a) | a * 1000000 * 60 * 60 | a * 1000000 * 60 * 60 | - | numtodsinterval(a*3600,'second') | -| TimeSpan.FromMilliseconds(a) | a * 1000 | a * 1000 | - | numtodsinterval(a/1000,'second') | -| TimeSpan.FromMinutes(a) | a * 1000000 * 60 | a * 1000000 * 60 | - | numtodsinterval(a*60,'second') | -| TimeSpan.FromSeconds(a) | a * 1000000 | a * 1000000 | - | numtodsinterval(a,'second') | -| TimeSpan.FromTicks(a) | a / 10 | a / 10 | - | numtodsinterval(a/10000000,'second') | -| a.Add(b) | a + b | a + b | - | a + b | -| a.Subtract(b) | a - b | a - b | - | a - b | -| a.CompareTo(b) | a - b | a - b | - | extract(day from (a-b)) | -| a.Days | a div (1000000 * 60 * 60 * 24) | a div (1000000 * 60 * 60 * 24) | - | extract(day from a) | -| a.Hours | a div (1000000 * 60 * 60) mod 24 | a div (1000000 * 60 * 60) mod 24 | - | extract(hour from a) | -| a.Milliseconds | a div 1000 mod 1000 | a div 1000 mod 1000 | - | cast(substr(extract(second from a)-floor(extract(second from a)),2,3) as number) | -| a.Seconds | a div 1000000 mod 60 | a div 1000000 mod 60 | - | extract(second from a) | -| a.Ticks | a * 10 | a * 10 | - | (extract(day from a)*86400+extract(hour from a)*3600+extract(minute from a)*60+extract(second from a))*10000000 | -| a.TotalDays | a / (1000000 * 60 * 60 * 24) | a / (1000000 * 60 * 60 * 24) | - | extract(day from a) | -| a.TotalHours | a / (1000000 * 60 * 60) | a / (1000000 * 60 * 60) | - | (extract(day from a)*24+extract(hour from a)) | -| a.TotalMilliseconds | a / 1000 | a / 1000 | - | (extract(day from a)*86400+extract(hour from a)*3600+extract(minute from a)*60+extract(second from a))*1000 | -| a.TotalMinutes | a / (1000000 * 60) | a / (1000000 * 60) | - | | (extract(day from a)*1440+extract(hour from a)*60+extract(minute from a)) | -| a.TotalSeconds | a / 1000000 | a / 1000000 | - | (extract(day from a)*86400+extract(hour from a)*3600+extract(minute from a)*60+extract(second from a)) | -| a.Equals(b) | a = b | a = b | - | a = b | -| a.ToString() | cast(a as varchar) | cast(a as varchar) | - | to_char(a) | - -### 数学函数 -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | -| - | - | - | - | - | -| Math.Abs(a) | abs(a) | abs(a) | abs(a) | -| Math.Acos(a) | acos(a) | acos(a) | acos(a) | acos(a) | -| Math.Asin(a) | asin(a) | asin(a) | asin(a) | asin(a) | -| Math.Atan(a) | atan(a) | atan(a) | atan(a) | atan(a) | -| Math.Atan2(a, b) | atan2(a, b) | atan2(a, b) | atan2(a, b) | - | -| Math.Ceiling(a) | ceiling(a) | ceiling(a) | ceiling(a) | ceil(a) | -| Math.Cos(a) | cos(a) | cos(a) | cos(a) | cos(a) | -| Math.Exp(a) | exp(a) | exp(a) | exp(a) | exp(a) | -| Math.Floor(a) | floor(a) | floor(a) | floor(a) | floor(a) | -| Math.Log(a) | log(a) | log(a) | log(a) | log(e,a) | -| Math.Log10(a) | log10(a) | log10(a) | log10(a) | log(10,a) | -| Math.PI(a) | 3.1415926535897931 | 3.1415926535897931 | 3.1415926535897931 | 3.1415926535897931 | -| Math.Pow(a, b) | pow(a, b) | power(a, b) | pow(a, b) | power(a, b) | -| Math.Round(a, b) | round(a, b) | round(a, b) | round(a, b) | round(a, b) | -| Math.Sign(a) | sign(a) | sign(a) | sign(a) | sign(a) | -| Math.Sin(a) | sin(a) | sin(a) | sin(a) | sin(a) | -| Math.Sqrt(a) | sqrt(a) | sqrt(a) | sqrt(a) | sqrt(a) | -| Math.Tan(a) | tan(a) | tan(a) | tan(a) | tan(a) | -| Math.Truncate(a) | truncate(a, 0) | floor(a) | trunc(a, 0) | trunc(a, 0) | - -### 类型转换 -| 表达式 | MySql | SqlServer | PostgreSQL | Oracle | -| - | - | - | - | - | -| Convert.ToBoolean(a) | a not in ('0','false') | a not in ('0','false') | a::varchar not in ('0','false','f','no') | - | -| Convert.ToByte(a) | cast(a as unsigned) | cast(a as tinyint) | a::int2 | cast(a as number) | -| Convert.ToChar(a) | substr(cast(a as char),1,1) | substring(cast(a as nvarchar),1,1) | substr(a::char,1,1) | substr(to_char(a),1,1) | -| Convert.ToDateTime(a) | cast(a as datetime) | cast(a as datetime) | a::timestamp | to_timestamp(a,'YYYY-MM-DD HH24:MI:SS.FF6') | -| Convert.ToDecimal(a) | cast(a as decimal(36,18)) | cast(a as decimal(36,19)) | a::numeric | cast(a as number) | -| Convert.ToDouble(a) | cast(a as decimal(32,16)) | cast(a as decimal(32,16)) | a::float8 | cast(a as number) | -| Convert.ToInt16(a) | cast(a as signed) | cast(a as smallint) | a::int2 | cast(a as number) | -| Convert.ToInt32(a) | cast(a as signed) | cast(a as int) | a::int4 | cast(a as number) | -| Convert.ToInt64(a) | cast(a as signed) | cast(a as bigint) | a::int8 | cast(a as number) | -| Convert.ToSByte(a) | cast(a as signed) | cast(a as tinyint) | a::int2 | cast(a as number) | -| Convert.ToString(a) | cast(a as decimal(14,7)) | cast(a as decimal(14,7)) | a::float4 | to_char(a) | -| Convert.ToSingle(a) | cast(a as char) | cast(a as nvarchar) | a::varchar | cast(a as number) | -| Convert.ToUInt16(a) | cast(a as unsigned) | cast(a as smallint) | a::int2 | cast(a as number) | -| Convert.ToUInt32(a) | cast(a as unsigned) | cast(a as int) | a::int4 | cast(a as number) | -| Convert.ToUInt64(a) | cast(a as unsigned) | cast(a as bigint) | a::int8 | cast(a as number) | diff --git a/Docs/generator.md b/Docs/generator.md deleted file mode 100644 index 66b01ceb..00000000 --- a/Docs/generator.md +++ /dev/null @@ -1,103 +0,0 @@ -# 生成器 - -生成器是基于 dbfirst 开发的辅助工具,适用老项目一键生成实体。生成器采用模板的方式,作者实现了三种生成模板: - -| 模板名称 | 类型映射 | 外键导航属性 | 缓存管理 | 失血 | 贫血 | 充血 | -| ------------- | - | - |- | - |- | - | -| simple-entity | √ | X | X | √ | X | X | -| simple-entity-navigation-object | √ | √ | X | √ | X | X | -| rich-entity-navigation-object | √ | √ | √ | X | √ | X | - -模板在项目目录:/Templates/MySql - -> 更多模板逐步开发中。。。 - -```csharp -//定义 mysql FreeSql -IFreeSql fsql = 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) - - .UseMonitorCommand( - cmd => { - Console.WriteLine(cmd.CommandText); - }, //监听SQL命令对象,在执行前 - (cmd, traceLog) => { - Console.WriteLine(traceLog); - }) //监听SQL命令对象,在执行后 - .Build(); - -//创建模板生成类实现 -var gen = new FreeSql.Generator.TemplateGenerator(); -gen.Build(mysql.DbFirst, - @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity", //模板目录(事先下载) - @"C:\Users\28810\Desktop\新建文件夹 (9)", //生成后保存的目录 - "cccddd" //数据库 -); -``` - -## 模板语法 - -```html - - -{#title} - - - - -{#表达式} -{##表达式} 当表达式可能发生runtime错误时使用,性能没有上面的高 - - -{include ../header.html} -
-

aaa

-

bbb {#i}

-

ccc {#i}

-
- - -{module module_name1 parms1, 2, 3...} -{/module} -{module module_name2 parms1, 2, 3...} -{/module} - - -{import ../module.html as myname} -{#myname.module_name(parms1, 2, 3...)} - - -{extends ../inc/layout.html} -{block body}{/block} - - -{% -for (var a = 0; a < 100; a++) - print(a); -%} - - -{if i === 50} -{elseif i > 60} -{else} -{/if} - - -{for i 1,101} 可自定义名 {for index2 表达式1 in 表达式2} - -{for item,index in items} 可选参数称 index - 可自定义名 {for item2, index99 in 数组表达式} - -{for key,item,index on json} 可选参数 item, index, - 可自定义名 {for key2, item2, index99 in 对象表达式} -{/for} - - -{miss} -此块内容不被bmw.js解析 -{/miss} - - - -``` \ No newline at end of file diff --git a/Docs/insert.md b/Docs/insert.md deleted file mode 100644 index c84c5e02..00000000 --- a/Docs/insert.md +++ /dev/null @@ -1,79 +0,0 @@ -# 插入数据 - -| 方法 | 返回值 | 参数 | 描述 | -| - | - | - | - | -| AppendData | \ | T1 \| IEnumerable | 追加准备插入的实体 | -| InsertColumns | \ | Lambda | 只插入的列 | -| IgnoreColumns | \ | Lambda | 忽略的列 | -| ToSql | string | | 返回即将执行的SQL语句 | -| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 | -| ExecuteIdentity | long | | 执行SQL语句,返回自增值 | -| ExecuteInserted | List\ | | 执行SQL语句,返回插入后的记录 | - -### 列优先级 - -> 全部列 < 指定列(InsertColumns) < 忽略列(IgnoreColumns) - -### 测试代码 - -```csharp -IFreeSql fsql = 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") - .Build(); -IInsert insert => fsql.Insert(); - -[Table(Name = "tb_topic")] -class Topic { - [Column(IsIdentity = true, IsPrimary = true)] - public int Id { get; set; } - public int Clicks { get; set; } - public TestTypeInfo Type { get; set; } - public string Title { get; set; } - public DateTime CreateTime { get; set; } -} - -var items = new List(); -for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); -``` - -### 插入 - -```csharp -var t1 = insert.AppendData(items.First()).ToSql(); -//INSERT INTO `tb_topic`(`Clicks`, `Title`, `CreateTime`) VALUES(?Clicks0, ?Title0, ?CreateTime0) -``` - -### 批量插入 - -```csharp -var t2 = insert.AppendData(items).ToSql(); -//INSERT INTO `tb_topic`(`Clicks`, `Title`, `CreateTime`) VALUES(?Clicks0, ?Title0, ?CreateTime0), (?Clicks1, ?Title1, ?CreateTime1), (?Clicks2, ?Title2, ?CreateTime2), (?Clicks3, ?Title3, ?CreateTime3), (?Clicks4, ?Title4, ?CreateTime4), (?Clicks5, ?Title5, ?CreateTime5), (?Clicks6, ?Title6, ?CreateTime6), (?Clicks7, ?Title7, ?CreateTime7), (?Clicks8, ?Title8, ?CreateTime8), (?Clicks9, ?Title9, ?CreateTime9) -``` - -### 只想插入指定的列 - -```csharp -var t3 = insert.AppendData(items).InsertColumns(a => a.Title).ToSql(); -//INSERT INTO `tb_topic`(`Title`) VALUES(?Title0), (?Title1), (?Title2), (?Title3), (?Title4), (?Title5), (?Title6), (?Title7), (?Title8), (?Title9) - -var t4 = insert.AppendData(items).InsertColumns(a =>new { a.Title, a.Clicks }).ToSql(); -//INSERT INTO `tb_topic`(`Clicks`, `Title`) VALUES(?Clicks0, ?Title0), (?Clicks1, ?Title1), (?Clicks2, ?Title2), (?Clicks3, ?Title3), (?Clicks4, ?Title4), (?Clicks5, ?Title5), (?Clicks6, ?Title6), (?Clicks7, ?Title7), (?Clicks8, ?Title8), (?Clicks9, ?Title9) -``` - -### 忽略列 - -```csharp -var t5 = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql(); -//INSERT INTO `tb_topic`(`Clicks`, `Title`) VALUES(?Clicks0, ?Title0), (?Clicks1, ?Title1), (?Clicks2, ?Title2), (?Clicks3, ?Title3), (?Clicks4, ?Title4), (?Clicks5, ?Title5), (?Clicks6, ?Title6), (?Clicks7, ?Title7), (?Clicks8, ?Title8), (?Clicks9, ?Title9) - -var t6 = insert.AppendData(items).IgnoreColumns(a => new { a.Title, a.CreateTime }).ToSql(); -///INSERT INTO `tb_topic`(`Clicks`) VALUES(?Clicks0), (?Clicks1), (?Clicks2), (?Clicks3), (?Clicks4), (?Clicks5), (?Clicks6), (?Clicks7), (?Clicks8), (?Clicks9) -``` - -### 执行命令 - -| 方法 | 返回值 | 描述 | -| - | - | - | -| ExecuteAffrows | long | 执行SQL语句,返回影响的行数 | -| ExecuteIdentity | long | 执行SQL语句,返回自增值 | -| ExecuteInserted | List\ | 执行SQL语句,返回插入后的记录 | diff --git a/Docs/select.md b/Docs/select.md deleted file mode 100644 index ccf443c0..00000000 --- a/Docs/select.md +++ /dev/null @@ -1,259 +0,0 @@ -# 查询数据 - -## 测试代码 - -```csharp -IFreeSql fsql = 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") - .Build(); -ISelect select => fsql.Select(); - -[Table(Name = "tb_topic")] -class Topic { - [Column(IsIdentity = true, IsPrimary = true)] - public int Id { get; set; } - public int Clicks { get; set; } - public int TestTypeInfoGuid { get; set; } - public TestTypeInfo Type { get; set; } - public string Title { get; set; } - public DateTime CreateTime { get; set; } -} -class TestTypeInfo { - public int Guid { get; set; } - public int ParentId { get; set; } - public TestTypeParentInfo Parent { get; set; } - public string Name { get; set; } -} -class TestTypeParentInfo { - public int Id { get; set; } - public string Name { get; set; } - - public List Types { get; set; } -} -``` - -# Where - -### 单表 -```csharp -var sql = select.Where(a => a.Id == 10).ToSql(); -///SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a WHERE (a.`Id` = 10) - -sql = select.Where(a => a.Id == 10 && a.Id > 10 || a.Clicks > 100).ToSql(); -///SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a WHERE (a.`Id` = 10 AND a.`Id` > 10 OR a.`Clicks` > 100) - -sql = select.Where(a => new []{1,2,3}.Contains(a.Id)).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a WHERE (a.`Id` in (1,2,3)) -``` -> [《Expression 表达式函数文档》](Docs/expression.md) - -### 多表,使用导航属性 -```csharp -sql = select.Where(a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TestTypeInfoGuid).ToSql(); -///SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a__Type.`Guid`, a__Type.`ParentId`, a__Type.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeInfo` a__Type WHERE (a__Type.`Name` = 'typeTitle' AND a__Type.`Guid` = a.`TestTypeInfoGuid`) - -sql = select.Where(a => a.Type.Parent.Name == "tparent").ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a__Type.`Guid`, a__Type.`ParentId`, a__Type.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeInfo` a__Type, `TestTypeParentInfo` a__Type__Parent WHERE (a__Type__Parent.`Name` = 'tparent') -``` - -### 多表,没有导航属性 -```csharp -sql = select.Where((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "typeTitle").ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, b.`Guid`, b.`ParentId`, b.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeInfo` b WHERE (b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'typeTitle') - -sql = select.Where((a, b, c) => c.Name == "tparent").ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeParentInfo` c WHERE (c.`Name` = 'tparent') -``` - -### 多表,任意查 -```csharp -sql = select.From((s, b, c) => s - .Where(a => a.Id == 10 && c.Name == "xxx") - .Where(a => b.ParentId == 20)).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, b.`Guid`, b.`ParentId`, b.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a, `TestTypeParentInfo` c, `TestTypeInfo` b WHERE (a.`Id` = 10 AND c.`Name` = 'xxx') AND (b.`ParentId` = 20) -``` - -### 子表 Exists 查询 -```csharp -var sql2222 = select.Where(a => select.Where(b => b.Id == a.Id).Any()).ToList(); -// SELECT a.`Id`, a.`TypeGuid`, a.`Title`, a.`CreateTime` -// FROM `xxx` a -// WHERE (exists(SELECT 1 -// FROM `xxx` b -// WHERE (b.`Id` = a.`Id`))) - -//两级相同的子表查询 -sql2222 = select.Where(a => - select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) - .Offset(a.Id) - .Any() - ).Any() -).ToList(); -// SELECT a.`Id`, a.`TypeGuid`, a.`Title`, a.`CreateTime` -// FROM `xxx` a -// WHERE (exists(SELECT 1 -// FROM `xxx` b -// WHERE (b.`Id` = a.`Id` AND exists(SELECT 1 -// FROM `xxx` c -// WHERE (c.`Id` = b.`Id`) AND (c.`Id` = a.`Id`) AND (c.`Id` = b.`Id`) -// limit 0,1)) -// limit 0,1)) -``` - -### 原生SQL -```csharp -sql = select.Where("a.clicks > 100 && a.id = ?id", new { id = 10 }).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a WHERE (a.clicks > 100 && a.id = ?id) -``` - -> 以上条件查询,支持 WhereIf - -# 联表 - -### 使用导航属性联表 -```csharp -sql = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a__Type.`Guid`, a__Type.`ParentId`, a__Type.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` - -sql = select - .LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid) - .LeftJoin(a => a.Type.Parent.Id == a.Type.ParentId).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a__Type.`Guid`, a__Type.`ParentId`, a__Type.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` LEFT JOIN `TestTypeParentInfo` a__Type__Parent ON a__Type__Parent.`Id` = a__Type.`ParentId` -``` - -### 没有导航属性联表 -```csharp -sql = select.LeftJoin((a, b) => b.Guid == a.TestTypeInfoGuid).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, b.`Guid`, b.`ParentId`, b.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` - -sql = select - .LeftJoin((a, b) => b.Guid == a.TestTypeInfoGuid) - .LeftJoin((a, c) => c.Id == a.Type.ParentId).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, b.`Guid`, b.`ParentId`, b.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` LEFT JOIN `TestTypeParentInfo` c ON c.`Id` = b.`ParentId` -``` - -### 联表任意查 -```csharp -sql = select.From((s, b, c) => s - .LeftJoin(a => a.TestTypeInfoGuid == b.Guid) - .LeftJoin(a => b.ParentId == c.Id)).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, b.`Guid`, b.`ParentId`, b.`Name`, a.`Title`, a.`CreateTime` FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON a.`TestTypeInfoGuid` = b.`Guid` LEFT JOIN `TestTypeParentInfo` c ON b.`ParentId` = c.`Id` -``` - -### 原生SQL联表 -```csharp -sql = select.LeftJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", new { bname = "xxx" }).ToSql(); -//SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a.`Title`, a.`CreateTime` FROM `tb_topic` a LEFT JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname -``` - -# 查询数据 - -### 返回 List -```csharp -List t1 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList(); -``` - -### 返回 List + 导航属性的数据 -```csharp -List t2 = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid).ToList(); -//此时会返回普通字段 + 导航对象 Type 的数据 -``` - -### 指定字段返回 -```csharp -//返回一个字段 -List t3 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList(a => a.Id); - -//返回匿名类 -List<匿名类> t4 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList(a => new { a.Id, a.Title }); - -//返回元组 -List<(int, string)> t5 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList<(int, string)>("id, title"); - -//返回SQL字段 -List<匿名类> t4 = select.Where(a => a.Id > 0).Skip(100).Limit(200) - .ToList(a => new { - a.Id, - a.Title, - cstitle = "substr(a.title, 0, 2)", //将 substr(a.title, 0, 2) 作为查询字段 - csnow = Convert.ToDateTime("now()"), //将 now() 作为查询字段 - //奇思妙想:怎么查询开窗函数的结果 - }); -``` - -### 执行SQL返回数据 -```csharp -class xxx { - public int Id { get; set; } - public string Path { get; set; } - public string Title2 { get; set; } -} - -List t6 = fsql.Ado.Query("select * from song"); -List<(int, string ,string)> t7 = fsql.Ado.Query<(int, string, string)>("select * from song"); -List t8 = fsql.Ado.Query("select * from song"); -``` - -### 分组聚合 -```csharp -var groupby = fsql.Select() - .GroupBy(a => new { tt2 = a.Title.Substring(0, 2), mod4 = a.Id % 4 }) - .Having(a => a.Count() > 0 && a.Avg(a.Key.mod4) > 0 && a.Max(a.Key.mod4) > 0) - .Having(a => a.Count() < 300 || a.Avg(a.Key.mod4) < 100) - .OrderBy(a => a.Key.tt2) - .OrderByDescending(a => a.Count()) - .ToList(a => new { a.Key.tt2, cou1 = a.Count(), arg1 = a.Avg(a.Key.mod4) }); -//SELECT substr(a.`Title`, 1, 2) as1, count(1) as2, avg((a.`Id` % 4)) as3 -//FROM `xxx` a -//GROUP BY substr(a.`Title`, 1, 2), (a.`Id` % 4) -//HAVING (count(1) > 0 AND avg((a.`Id` % 4)) > 0 AND max((a.`Id` % 4)) > 0) AND (count(1) < 300 OR avg((a.`Id` % 4)) < 100) -//ORDER BY substr(a.`Title`, 1, 2), count(1) DESC -``` - -# 更多文档整理中。。。 - -| 方法 | 返回值 | 参数 | 描述 | -| ------------- | - | - | - | -| ToSql | string | | 返回即将执行的SQL语句 | -| ToList | List | | 执行SQL查询,返回 T1 实体所有字段的记录,若存在导航属性则一起查询返回,记录不存在时返回 Count 为 0 的列表 | -| ToList\ | List\ | Lambda | 执行SQL查询,返回指定字段的记录,记录不存在时返回 Count 为 0 的列表 | -| ToList\ | List\ | string field | 执行SQL查询,返回 field 指定字段的记录,并以元组或基础类型(int,string,long)接收,记录不存在时返回 Count 为 0 的列表 | -| ToOne | T1 | | 执行SQL查询,返回 T1 实体所有字段的第一条记录,记录不存在时返回 null | -| Any | bool | | 执行SQL查询,是否有记录 | -| Sum | T | Lambda | 指定一个列求和 | -| Min | T | Lambda | 指定一个列求最小值 | -| Max | T | Lambda | 指定一个列求最大值 | -| Avg | T | Lambda | 指定一个列求平均值 | -| 【分页】 | -| Count | long | | 查询的记录数量 | -| Count | \ | out long | 查询的记录数量,以参数out形式返回 | -| Skip | \ | int offset | 查询向后偏移行数 | -| Offset | \ | int offset | 查询向后偏移行数 | -| Limit | \ | int limit | 查询多少条数据 | -| Take | \ | int limit | 查询多少条数据 | -| Page | \ | int pageIndex, int pageSize | 分页 | -| 【条件】 | -| Where | \ | Lambda | 支持多表查询表达式 | -| WhereIf | \ | bool, Lambda | 支持多表查询表达式 | -| Where | \ | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) | -| WhereIf | \ | bool, string, parms | 原生sql语法条件,WhereIf(true, "id = ?id", new { id = 1 }) | -| 【分组】 | -| GroupBy | \ | Lambda | 按选择的列分组,GroupBy(a => a.Name) | GroupBy(a => new{a.Name,a.Time}) | -| GroupBy | \ | string, parms | 按原生sql语法分组,GroupBy("concat(name, ?cc)", new { cc = 1 }) | -| Having | \ | string, parms | 按原生sql语法聚合条件过滤,Having("count(name) = ?cc", new { cc = 1 }) | -| 【排序】 | -| OrderBy | \ | Lambda | 按列排序,OrderBy(a => a.Time) | -| OrderByDescending | \ | Lambda | 按列倒向排序,OrderByDescending(a => a.Time) | -| OrderBy | \ | string, parms | 按原生sql语法排序,OrderBy("count(name) + ?cc", new { cc = 1 }) | -| 【联表】 | -| LeftJoin | \ | Lambda | 左联查询,可使用导航属性,或指定关联的实体类型 | -| InnerJoin | \ | Lambda | 联接查询,可使用导航属性,或指定关联的实体类型 | -| RightJoin | \ | Lambda | 右联查询,可使用导航属性,或指定关联的实体类型 | -| LeftJoin | \ | string, parms | 左联查询,使用原生sql语法,LeftJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) | -| InnerJoin | \ | string, parms | 联接查询,使用原生sql语法,InnerJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) | -| RightJoin | \ | string, parms | 右联查询,使用原生sql语法,RightJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) | -| From | \ | Lambda | 多表查询,3个表以上使用非常方便,目前设计最大支持10个表 | -| 【其他】 | -| As | \ | string alias = "a" | 指定别名 | -| Master | \ | | 指定从主库查询(默认查询从库) | -| Caching | \ | int seconds, string key = null | 缓存查询结果 | \ No newline at end of file diff --git a/Docs/update.md b/Docs/update.md deleted file mode 100644 index dfe3bf0a..00000000 --- a/Docs/update.md +++ /dev/null @@ -1,129 +0,0 @@ -# 更新数据 - -| 方法 | 返回值 | 参数 | 描述 | -| - | - | - | - | -| SetSource | \ | T1 \| IEnumerable | 更新数据,设置更新的实体 | -| IgnoreColumns | \ | Lambda | 忽略的列 | -| Set | \ | Lambda, value | 设置列的新值,Set(a => a.Name, "newvalue") | -| Set | \ | Lambda | 设置列的的新值为基础上增加,Set(a => a.Clicks + 1),相当于 clicks=clicks+1; | -| SetRaw | \ | string, parms | 设置值,自定义SQL语法,SetRaw("title = ?title", new { title = "newtitle" }) | -| Where | \ | Lambda | 表达式条件,仅支持实体基础成员(不包含导航对象) | -| Where | \ | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) | -| Where | \ | T1 \| IEnumerable | 传入实体或集合,将其主键作为条件 | -| WhereExists | \ | ISelect | 子查询是否存在 | -| ToSql | string | | 返回即将执行的SQL语句 | -| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 | -| ExecuteUpdated | List\ | | 执行SQL语句,返回更新后的记录 | - -### 列优先级 - -> 全部列 < 指定列(Set/SetRaw) < 忽略列(IgnoreColumns) - -### 测试代码 - -```csharp -[Table(Name = "tb_topic")] -class Topic { - [Column(IsIdentity = true, IsPrimary = true)] - public int Id { get; set; } - public int Clicks { get; set; } - public TestTypeInfo Type { get; set; } - public string Title { get; set; } - public DateTime CreateTime { get; set; } -} - -IFreeSql fsql = 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") - .Build(); -IUpdate update => fsql.Update(); -``` - -### 动态条件 -```csharp -Update(object dywhere) -``` -dywhere 支持 - -* 主键值 -* new[] { 主键值1, 主键值2 } -* Topic对象 -* new[] { Topic对象1, Topic对象2 } -* new { id = 1 } - -### 更新指定列 -```csharp -var t1 = fsql.Update(1).Set(a => a.CreateTime, DateTime.Now).ToSql(); -//UPDATE `tb_topic` SET `CreateTime` = '2018-12-08 00:04:59' WHERE (`Id` = 1) -``` - -### 更新指定列,累加 -```csharp -var t2 = fsql.Update(1).Set(a => a.Clicks + 1).ToSql(); -//UPDATE `tb_topic` SET `Clicks` = ifnull(`Clicks`,0) + 1 WHERE (`Id` = 1) -``` - -### 保存实体 -```csharp -var item = new Topic { Id = 1, Title = "newtitle" }; -var t3 = update.SetSource(item).ToSql(); -//UPDATE `tb_topic` SET `Clicks` = ?p_0, `Title` = ?p_1, `CreateTime` = ?p_2 WHERE (`Id` = 1) -``` - -### 保存实体,忽略一些列 -```csharp -var t4 = update.SetSource(item).IgnoreColumns(a => a.Clicks).ToSql(); -//UPDATE `tb_topic` SET `Title` = ?p_0, `CreateTime` = ?p_1 WHERE (`Id` = 1) -var t5 = update.SetSource(item).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql(); -//UPDATE `tb_topic` SET `Title` = ?p_0 WHERE (`Id` = 1) -``` - -### 批量保存 -```csharp -var items = new List(); -for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); - -var t6 = update.SetSource(items).ToSql(); -//UPDATE `tb_topic` SET `Clicks` = CASE `Id` WHEN 1 THEN ?p_0 WHEN 2 THEN ?p_1 WHEN 3 THEN ?p_2 WHEN 4 THEN ?p_3 WHEN 5 THEN ?p_4 WHEN 6 THEN ?p_5 WHEN 7 THEN ?p_6 WHEN 8 THEN ?p_7 WHEN 9 THEN ?p_8 WHEN 10 THEN ?p_9 END, `Title` = CASE `Id` WHEN 1 THEN ?p_10 WHEN 2 THEN ?p_11 WHEN 3 THEN ?p_12 WHEN 4 THEN ?p_13 WHEN 5 THEN ?p_14 WHEN 6 THEN ?p_15 WHEN 7 THEN ?p_16 WHEN 8 THEN ?p_17 WHEN 9 THEN ?p_18 WHEN 10 THEN ?p_19 END, `CreateTime` = CASE `Id` WHEN 1 THEN ?p_20 WHEN 2 THEN ?p_21 WHEN 3 THEN ?p_22 WHEN 4 THEN ?p_23 WHEN 5 THEN ?p_24 WHEN 6 THEN ?p_25 WHEN 7 THEN ?p_26 WHEN 8 THEN ?p_27 WHEN 9 THEN ?p_28 WHEN 10 THEN ?p_29 END WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10)) -``` - -> 批量保存的场景,先查询20条记录,根据本地很复杂的规则把集合的值改完后 - -> 传统做法是循环20次保存,用 case when 只要一次就行 - -### 批量保存,忽略一些列 -```csharp -var t7 = update.SetSource(items).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql(); -//UPDATE `tb_topic` SET `Title` = CASE `Id` WHEN 1 THEN ?p_0 WHEN 2 THEN ?p_1 WHEN 3 THEN ?p_2 WHEN 4 THEN ?p_3 WHEN 5 THEN ?p_4 WHEN 6 THEN ?p_5 WHEN 7 THEN ?p_6 WHEN 8 THEN ?p_7 WHEN 9 THEN ?p_8 WHEN 10 THEN ?p_9 END WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10)) -``` - -### 批量更新指定列 -```csharp -var t8 = update.SetSource(items).Set(a => a.CreateTime, DateTime.Now).ToSql(); -//UPDATE `tb_topic` SET `CreateTime` = ?p_0 WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10)) -``` - -> 指定列更新后,批量保存将失效 - -### 更新条件 - -> 除了顶上介绍的 dywhere 构造参数外,还支持 Where lambda/sql 方法 - -```csharp -var t9 = update.Set(a => a.Title, "新标题").Where(a => a.Id == 1).ToSql(); -//UPDATE `tb_topic` SET `Title` = '新标题' WHERE (Id = 1) -``` - -### 自定义SQL - -```csharp -var t10 = update.SetRaw("Title = {0}", "新标题").Where("Id = {0}", 1).ToSql(); -//UPDATE `tb_topic` SET Title = '新标题' WHERE (Id = 1) -//sql语法条件,参数使用 {0},与 string.Format 保持一致,无须加单引号,错误的用法:'{0}' -``` - -### 执行命令 - -| 方法 | 返回值 | 参数 | 描述 | -| - | - | - | - | -| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 | -| ExecuteUpdated | List\ | | 执行SQL语句,返回更新后的记录 | \ No newline at end of file diff --git a/FreeSql.DbContext/readme.md b/FreeSql.DbContext/readme.md index beee15e9..ac006f8d 100644 --- a/FreeSql.DbContext/readme.md +++ b/FreeSql.DbContext/readme.md @@ -1,284 +1,216 @@ -这是 [FreeSql](https://github.com/2881099/FreeSql) 衍生出来的扩展包,包含 DbContext & DbSet、Repository & UnitOfWork 实现面向对象的特性(QQ群:4336577)。 +FreeSql.DbContext 实现类似 EFCore 使用习惯,跟踪对象状态,最终通过 SaveChanges 方法提交事务。 + +## 安装 > dotnet add package FreeSql.DbContext -## 更新日志 +## 如何使用 -### v0.6.5 - -- 修复 Repository 级联保存的 bug; -- 添加工作单元开启方法; -- 适配 .net framework 4.5、netstandard 2.0; - -### v0.6.1 - -- 拆分 FreeSql 小包引用,各数据库单独包、延时加载包; -- FreeSql.Extensions.LazyLoading -- FreeSql.Provider.MySql -- FreeSql.Provider.PostgreSQL -- FreeSql.Provider.SqlServer -- FreeSql.Provider.Sqlite -- FreeSql.Provider.Oracle -- 移除 IFreeSql.Cache,以及 ISelect.Caching 方法; -- 移除 IFreeSql.Log,包括内部原有的日志输出,改为 Trace.WriteLine; -- IAdo.Query\ 读取返回变为 List\\>; -- 定义 IFreeSql 和以前一样,移除了 UseCache、UseLogger 方法; - -## DbContext & DbSet - -```csharp -using (var ctx = new SongContext()) { - var song = new Song { BigNumber = "1000000000000000000" }; - ctx.Songs.Add(song); - - song.BigNumber = (BigInteger.Parse(song.BigNumber) + 1).ToString(); - ctx.Songs.Update(song); - - var tag = new Tag { - Name = "testaddsublist", - Tags = new[] { - new Tag { Name = "sub1" }, - new Tag { Name = "sub2" }, - new Tag { - Name = "sub3", - Tags = new[] { - new Tag { Name = "sub3_01" } - } - } - } - }; - ctx.Tags.Add(tag); +0、通用方法,为啥是0??? +``` +using (var ctx = fsql.CreateDbContext()) { + //var db1 = ctx.Set(); + //var db2 = ctx.Set(); + var item = new Song { }; + ctx.Add(item); ctx.SaveChanges(); } ``` -## Repository & UnitOfWork +> 注意:DbContext 对象多线程不安全 -仓储与工作单元一起使用,工作单元具有事务特点。 +1、在 OnConfiguring 方法上配置与 IFreeSql 关联 ```csharp -using (var unitOfWork = fsql.CreateUnitOfWork()) { - var songRepository = unitOfWork.GetRepository(); - var tagRepository = unitOfWork.GetRepository(); +public class SongContext : DbContext { - var song = new Song { BigNumber = "1000000000000000000" }; - songRepository.Insert(song); + public DbSet Songs { get; set; } + public DbSet Tags { get; set; } - songRepository.Update(song); - - song.BigNumber = (BigInteger.Parse(song.BigNumber) + 1).ToString(); - songRepository.Update(song); - - var tag = new Tag { - Name = "testaddsublist", - Tags = new[] { - new Tag { Name = "sub1" }, - new Tag { Name = "sub2" }, - new Tag { - Name = "sub3", - Tags = new[] { - new Tag { Name = "sub3_01" } - } - } - } - }; - tagRepository.Insert(tag); - - ctx.Commit(); + protected override void OnConfiguring(DbContextOptionsBuilder builder) { + builder.UseFreeSql(dbcontext_01.Startup.Fsql); + } } -``` -## Repository - -简单使用仓储,有状态跟踪,它不包含事务的特点。 - -```csharp -var songRepository = fsql.GetRepository(); -var song = new Song { BigNumber = "1000000000000000000" }; -songRepository.Insert(song); -``` - -## IFreeSql 核心定义 - -```csharp -var fsql = new FreeSql.FreeSqlBuilder() - .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\dd2.db;Pooling=true;Max Pool Size=10") - .UseAutoSyncStructure(true) - .UseNoneCommandParameter(true) - - .UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText)) - .Build(); public class Song { [Column(IsIdentity = true)] public int Id { get; set; } - public string BigNumber { get; set; } - - [Column(IsVersion = true)] //乐观锁 - public long versionRow { get; set; } -} -public class Tag { - [Column(IsIdentity = true)] - public int Id { get; set; } - - public int? Parent_id { get; set; } - public virtual Tag Parent { get; set; } - - public string Name { get; set; } + public DateTime? Create_time { get; set; } + public bool? Is_deleted { get; set; } + public string Title { get; set; } + public string Url { get; set; } public virtual ICollection Tags { get; set; } } +public class Song_tag { + public int Song_id { get; set; } + public virtual Song Song { get; set; } -public class SongContext : DbContext { - public DbSet Songs { get; set; } - public DbSet Tags { get; set; } + public int Tag_id { get; set; } + public virtual Tag Tag { get; set; } +} - protected override void OnConfiguring(DbContextOptionsBuilder builder) { - builder.UseFreeSql(fsql); - } +public class Tag { + [Column(IsIdentity = true)] + public int Id { get; set; } + public int? Parent_id { get; set; } + public virtual Tag Parent { get; set; } + + public decimal? Ddd { get; set; } + public string Name { get; set; } + + public virtual ICollection Songs { get; set; } + public virtual ICollection Tags { get; set; } } ``` -# 过滤器与验证 - -假设我们有User(用户)、Topic(主题)两个实体,在领域类中定义了两个仓储: +使用的时候与 EFCore 类似: ```csharp -var userRepository = fsql.GetGuidRepository(); -var topicRepository = fsql.GetGuidRepository(); +long id = 0; + +using (var ctx = new SongContext()) { + + var song = new Song { }; + await ctx.Songs.AddAsync(song); + id = song.Id; + + var adds = Enumerable.Range(0, 100) + .Select(a => new Song { Create_time = DateTime.Now, Is_deleted = false, Title = "xxxx" + a, Url = "url222" }) + .ToList(); + await ctx.Songs.AddRangeAsync(adds); + + for (var a = 0; a < adds.Count; a++) + adds[a].Title = "dkdkdkdk" + a; + + ctx.Songs.UpdateRange(adds); + + ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList()); + + //ctx.Songs.Update(adds.First()); + + adds.Last().Url = "skldfjlksdjglkjjcccc"; + ctx.Songs.Update(adds.Last()); + + //throw new Exception("回滚"); + + await ctx.SaveChangesAsync(); +} ``` -在开发过程中,总是担心 topicRepository 的数据安全问题,即有可能查询或操作到其他用户的主题。因此我们在v0.0.7版本进行了改进,增加了 filter lambad 表达式参数。 +2、注入方式使用 ```csharp -var userRepository = fsql.GetGuidRepository(a => a.Id == 1); -var topicRepository = fsql.GetGuidRepository(a => a.UserId == 1); +public void ConfigureServices(IServiceCollection services) +{ + services.AddSingleton(Fsql); + services.AddFreeDbContext(options => options.UseFreeSql(Fsql)); +} ``` -* 在查询/修改/删除时附加此条件,从而达到不会修改其他用户的数据; -* 在添加时,使用表达式验证数据的合法性,若不合法则抛出异常; - -# 分表与分库 - -FreeSql 提供 AsTable 分表的基础方法,GuidRepository 作为分存式仓储将实现了分表与分库(不支持跨服务器分库)的封装。 +在 mvc 中获取: ```csharp -var logRepository = fsql.GetGuidRepository(null, oldname => $"{oldname}_{DateTime.Now.ToString("YYYYMM")}"); +IFreeSql _orm; +public ValuesController(SongContext songContext) { +} ``` -上面我们得到一个日志仓储按年月分表,使用它 CURD 最终会操作 Log_201903 表。 +## 优先级 -合并两个仓储,实现分表下的联表查询: +OnConfiguring > AddFreeDbContext + +## 乐观锁 + +更新实体数据,在并发情况下极容易造成旧数据将新的记录更新。FreeSql 核心部分已经支持乐观锁。 + +乐观锁的原理,是利用实体某字段,如:long version,更新前先查询数据,此时 version 为 1,更新时产生的 SQL 会附加 where version = 1,当修改失败时(即 Affrows == 0)抛出异常。 + +每个实体只支持一个乐观锁,在属性前标记特性:[Column(IsVersion = true)] 即可。 + +> 无论是使用 FreeSql/FreeSql.Repository/FreeSql.DbContext,每次更新 version 的值都会增加 1 + +## 说明 + +- DbContext 操作的数据在最后 SaveChanges 时才批量保存; +- DbContext 内所有操作,使用同一个事务; +- 当实体存在自增时,或者 Add/AddRange 的时候主键值为空,会提前开启事务; +- 支持同步/异步方法; + +## 合并机制 + +db.Add(new Xxx()); +db.Add(new Xxx()); +db.Add(new Xxx()); + +这三步,会合并成一个批量插入的语句执行,前提是它们没有自增属性。 + +适用 Guid 主键,Guid 主键的值不用设置,交给 FreeSql 处理即可,空着的 Guid 主键会在插入时获取有序不重值的 Guid 值。 + +又比如: + +db.Add(new Xxx()); +db.Add(new Xxx()); +db.Update(xxx); +db.Add(new Xxx()); + +Guid Id 的情况下,执行三次命令:前两次插入合并执行,update 为一次,后面的 add 为一次。 + +## 联级保存 + +请移步文档[《联级保存》](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98) + +## 实体变化事件 + +全局设置: ```csharp -fsql.GetGuidRepository().Select.FromRepository(logRepository) - .LeftJoin(b => b.UserId == a.Id) - .ToList(); +fsql.SetDbContextOptions(opt => { + opt.OnEntityChange = report => { + Console.WriteLine(report); + }; +}); ``` -注意事项: +单独设置 DbContext 或者 UnitOfWork: -* 不能使用 CodeFirst 迁移分表,开发环境时仍然可以迁移 Log 表; -* 不可在分表分库的实体类型中使用《延时加载》; +```csharp +var ctx = fsql.CreateDbContext(); +ctx.Options.OnEntityChange = report => { + Console.WriteLine(report); +}; -# 历史版本 +var uow = fsql.CreateUnitOfWork(); +uow.OnEntityChange = report => { + Console.WriteLine(report); +}; +``` -### v0.5.23 +参数 report 是一个 List 集合,集合元素的类型定义如下: -- 增加 DbSet/Repository FlushState 手工清除状态管理数据; +```csharp +public class EntityChangeInfo +{ + public object Object { get; set; } + public EntityChangeType Type { get; set; } +} +public enum EntityChangeType { Insert, Update, Delete, SqlRaw } +``` -### v0.5.21 +| 变化类型 | 说明 | +| -- | -- | +| Insert | 实体对象被插入 | +| Update | 实体对象被更新 | +| Delete | 实体对象被删除 | +| SqlRaw | 执行了SQL语句 | -- 修复 AddOrUpdate/InsertOrUpdate 当主键无值时,仍然查询了一次数据库; -- 增加 查询数据时 TrackToList 对导航集合的状态跟踪; -- 完善 AddOrUpdateNavigateList 级联保存,忽略标记 IsIgnore 的集合属性; -- 完成 IFreeSql.Include、IncludeMany 功能; +SqlRaw 目前有两处地方比较特殊: +- 多对多联级更新导航属性的时候,对中间表的全部删除操作; +- 通用仓储类 BaseRepository 有一个 Delete 方法,参数为表达式,而并非实体; +```csharp +int Delete(Expression> predicate); +``` -### v0.5.12 - -- 增加 工作单元开关,可在任意 Insert/Update/Delete 之前调用,以关闭工作单元使其失效;[PR #1](https://github.com/2881099/FreeSql.DbContext/pull/1) - -### v0.5.9 - -- 增加 linq to sql 的查询语法,以及单元测试,[wiki](https://github.com/2881099/FreeSql/wiki/LinqToSql); -- 修复 EnableAddOrUpdateNavigateList 设置对异步方法无效的 bug; - -### v0.5.8 - -- 增加 IFreeSql.SetDbContextOptions 设置 DbContext 的功能:开启或禁用连级一对多导航集合属性保存的功能,EnableAddOrUpdateNavigateList(默认开启); -- 增加 IUnitOfWork.IsolationLevel 设置事务级别; - -### v0.5.7 - -- 修复 UnitOfWork.GetRepository() 事务 bug,原因:仓储的每步操作都提交了事务; - -### v0.5.5 - -- 修复 MapEntityValue 对 IsIgnore 未处理的 bug; - -### v0.5.4 - -- 修复 Repository 追加导航集合的保存 bug; -- 公开 IRepository.Orm 对象; - -### v0.5.3 - -- 修复 实体跟踪的 bug,当查询到的实体自增值为 0 时重现; -- 优化 状态管理字典为 ConcurrentDictionary; - -### v0.5.2 - -- 优化 SqlServer UnitOfWork 使用bug,在 FreeSql 内部解决的; -- 补充 测试与支持联合主键的自增; - -### v0.5.1 - -- 补充 开放 DbContext.UnitOfWork 对象,方便扩展并保持在同一个事务执行; -- 补充 增加 DbSet\、Repository\ 使用方法,配合 AsType(实体类型),实现弱类型操作; -- 修复 DbContext.AddOrUpdate 传入 null 时,任然会查询一次数据库的 bug; -- 优化 DbContext.AddOrUpdate 未添加实体主键的错误提醒; -- 修复 DbContext.Set\ 缓存的 bug,使用多种弱类型时发生; -- 修复 IsIgnore 过滤字段后,查询的错误; -- 修复 全局过滤器功能迁移的遗留 bug; - -### v0.4.14 - -- 优化 Add 时未设置主键的错误提醒; - -### v0.4.13 - -- 补充 Repository 增加 Attach 方法; -- 优化 Update/AddOrUpdate 实体的时候,若状态管理不存在,尝试查询一次数据库,以便跟踪对象; - -### v0.4.12 - -- 修复 非自增情况下,Add 后再 Update 该实体时,错误(需要先 Attach 或查询)的 bug; - -### v0.4.10 - -- 补充 开放 DbContext.Orm 对象; -- 修复 OnConfiguring 未配置时注入获取失败的 bug; - -### v0.4.6 - -- 修复 DbSet AddRange/UpdateRange/RemoveRange 参数为空列表时报错,现在不用判断 data.Any() == true 再执行; -- 增加 DbContext 对 DbSet 的快速代理方法(Add/Update/Remove/Attach); -- 增加 DbContext 通用类,命名为:FreeContext,也可以通过 IFreeSql 扩展方法 CreateDbContext 创建; -- 增加 ISelect NoTracking 扩展方法,查询数据时不追踪(从而提升查询性能); - -### v0.4.5 - -- 增加 DbSet Attach 方法附加实体,可用于不查询就更新或删除; - -### v0.4.2 - -- 增加 DbSet UpdateAsync/UpdateRangeAsync 方法,当一个实体被更新两次时,会先执行前面的队列; -- 增加 GetRepository 获取联合主键的适用仓储类; -- 增加 DbSet 在 Add/Update 时对导航属性(OneToMany) 的处理(AddOrUpdate); - -### v0.4.1 -- 独立 FreeSql.DbContext 项目; -- 实现 Repository + DbSet 统一的状态跟踪与工作单元; -- 增加 DbSet AddOrUpdate 方法; -- 增加 Repository InsertOrUpdate 方法; \ No newline at end of file +DbContext.SaveChanges,或者 Repository 对实体的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都会最多触发一次该事件。 \ No newline at end of file diff --git a/FreeSql.Repository/readme.md b/FreeSql.Repository/readme.md index 32fee71e..1ba1f896 100644 --- a/FreeSql.Repository/readme.md +++ b/FreeSql.Repository/readme.md @@ -1,95 +1,68 @@ -这是 [FreeSql](https://github.com/2881099/FreeSql) 衍生出来的扩展包,包含 Repository & UnitOfWork 实现面向对象的特性(QQ群:4336577)。 +FreeSql.Repository 作为扩展,实现了通用仓储层功能。与其他规范标准一样,仓储层也有相应的规范定义。FreeSql.Repository 参考 abp vnext 接口,定义和实现基础的仓储层(CURD),应该算比较通用的方法吧。 + +## 安装 > dotnet add package FreeSql.Repository -## Repository & UnitOfWork - -仓储与工作单元一起使用,工作单元具有事务特点。 +## 定义 ```csharp -using (var unitOfWork = fsql.CreateUnitOfWork()) { - var songRepository = unitOfWork.GetRepository(); - var tagRepository = unitOfWork.GetRepository(); - - var song = new Song { BigNumber = "1000000000000000000" }; - songRepository.Insert(song); - - songRepository.Update(song); - - song.BigNumber = (BigInteger.Parse(song.BigNumber) + 1).ToString(); - songRepository.Update(song); - - var tag = new Tag { - Name = "testaddsublist", - Tags = new[] { - new Tag { Name = "sub1" }, - new Tag { Name = "sub2" }, - new Tag { - Name = "sub3", - Tags = new[] { - new Tag { Name = "sub3_01" } - } - } - } - }; - tagRepository.Insert(tag); - - ctx.Commit(); -} -``` - -## Repository - -简单使用仓储,有状态跟踪,它不包含事务的特点。 - -```csharp -var songRepository = fsql.GetRepository(); -var song = new Song { BigNumber = "1000000000000000000" }; -songRepository.Insert(song); -``` - -## IFreeSql 核心定义 - -```csharp -var fsql = new FreeSql.FreeSqlBuilder() - .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\dd2.db;Pooling=true;Max Pool Size=10") - .UseAutoSyncStructure(true) - .UseNoneCommandParameter(true) - - .UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText)) - .Build(); +static IFreeSql fsql = new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10") + .UseAutoSyncStructure(true) //自动迁移实体的结构到数据库 + .Build(); //请务必定义成 Singleton 单例模式 public class Song { [Column(IsIdentity = true)] public int Id { get; set; } - public string BigNumber { get; set; } - - [Column(IsVersion = true)] //乐观锁 - public long versionRow { get; set; } -} -public class Tag { - [Column(IsIdentity = true)] - public int Id { get; set; } - - public int? Parent_id { get; set; } - public virtual Tag Parent { get; set; } - - public string Name { get; set; } - - public virtual ICollection Tags { get; set; } -} - -public class SongContext : DbContext { - public DbSet Songs { get; set; } - public DbSet Tags { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder builder) { - builder.UseFreeSql(fsql); - } + public string Title { get; set; } } ``` -# 过滤器与验证 +## 使用方法 + +1、IFreeSql 的扩展方法; + +```csharp +var curd = fsql.GetRepository(); +``` + +> 注意:Repository对象多线程不安全 + +2、继承实现; + +```csharp +public class SongRepository : BaseRepository { + public SongRepository(IFreeSql fsql) : base(fsql, null, null) {} + + //在这里增加 CURD 以外的方法 +} +``` + +3、依赖注入; + +```csharp +public void ConfigureServices(IServiceCollection services) { + + services.AddSingleton(Fsql); + services.AddFreeRepository(filter => filter + .Apply("SoftDelete", a => a.IsDeleted == false) + .Apply("Tenant", a => a.TenantId == 1) + , + this.GetType().Assembly + ); +} + +//在控制器使用 +public SongsController(GuidRepository repos1) { +} +``` + +> 依赖注入的方式可实现全局【过滤与验证】的设定,方便租户功能的设计; + +更多资料:[《过滤器、全局过滤器》](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8) + +## 过滤与验证 假设我们有User(用户)、Topic(主题)两个实体,在领域类中定义了两个仓储: @@ -98,7 +71,7 @@ var userRepository = fsql.GetGuidRepository(); var topicRepository = fsql.GetGuidRepository(); ``` -在开发过程中,总是担心 topicRepository 的数据安全问题,即有可能查询或操作到其他用户的主题。因此我们在v0.0.7版本进行了改进,增加了 filter lambad 表达式参数。 +在开发过程中,总是担心 topicRepository 的数据安全问题,即有可能查询或操作到其他用户的主题。因此我们在v0.0.7版本进行了改进,增加了 filter lambda 表达式参数。 ```csharp var userRepository = fsql.GetGuidRepository(a => a.Id == 1); @@ -108,7 +81,7 @@ var topicRepository = fsql.GetGuidRepository(a => a.UserId == 1); * 在查询/修改/删除时附加此条件,从而达到不会修改其他用户的数据; * 在添加时,使用表达式验证数据的合法性,若不合法则抛出异常; -# 分表与分库 +## 分表与分库 FreeSql 提供 AsTable 分表的基础方法,GuidRepository 作为分存式仓储将实现了分表与分库(不支持跨服务器分库)的封装。 @@ -118,15 +91,148 @@ var logRepository = fsql.GetGuidRepository(null, oldname => $"{oldname}_{Da 上面我们得到一个日志仓储按年月分表,使用它 CURD 最终会操作 Log_201903 表。 -合并两个仓储,实现分表下的联表查询: - -```csharp -fsql.GetGuidRepository().Select.FromRepository(logRepository) - .LeftJoin(b => b.UserId == a.Id) - .ToList(); -``` - 注意事项: -* 不能使用 CodeFirst 迁移分表,开发环境时仍然可以迁移 Log 表; +* v0.11.12以后的版本可以使用 CodeFirst 迁移分表; * 不可在分表分库的实体类型中使用《延时加载》; + +## 兼容问题 + +FreeSql 支持五种数据库,分别为 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/达梦,虽然他们都为关系型数据库,但各自有着独特的技术亮点,有许多亮点值得我们使用; + +比如 SqlServer 提供的 output inserted 特性,在表使用了自增或数据库定义了默认值的时候,使用它可以快速将 insert 的数据返回。PostgreSQL 也有相应的功能,如此方便却不是每个数据库都支持。 + +IRepository 接口定义: + +```csharp +TEntity Insert(TEntity entity); +Task InsertAsync(TEntity entity); +``` + +于是我们做了两种仓库层实现: + +- BaseRepository 采用 ExecuteInserted 执行; +- GuidRepository 采用 ExecuteAffrows 执行(兼容性好); + +当采用了不支持的数据库时(Sqlite/MySql/Oracle),建议: + +* 使用 uuid 作为主键(即 Guid); +* 避免使用数据库的默认值功能; +* 仓储层实现请使用 GuidRepository; + +## UnitOfWork + +UnitOfWork 可将多个仓储放在一个单元管理执行,最终通用 Commit 执行所有操作,内部采用了数据库事务; + +```csharp +using (var uow = fsql.CreateUnitOfWork()) { + var songRepo = uow.GetRepository(); + var userRepo = uow.GetRepository(); + + //上面两个仓储,由同一UnitOfWork uow 创建 + //在此执行仓储操作 + + //这里不受异步方便影响 + + uow.Commit(); +} +``` + +参考:在 asp.net core 中注入工作单元方法 + +```csharp +//第一步: +public class UnitOfWorkRepository : BaseRepository +{ + public UnitOfWorkRepository(IFreeSql fsql, IUnitOfWork uow) : base(fsql, null, null) + { + this.UnitOfWork = uow; + } +} +public class UnitOfWorkRepository : BaseRepository +{ + public UnitOfWorkRepository(IFreeSql fsql, IUnitOfWork uow) : base(fsql, null, null) + { + this.UnitOfWork = uow; + } +} + +//第二步: +public void ConfigureServices(IServiceCollection services) +{ + services.AddSingleton(fsql); + services.AddScoped(sp => fsql.CreateUnitOfWork()); + + services.AddScoped(typeof(IReadOnlyRepository<>), typeof(UnitOfWorkRepository<>)); + services.AddScoped(typeof(IBasicRepository<>), typeof(UnitOfWorkRepository<>)); + services.AddScoped(typeof(BaseRepository<>), typeof(UnitOfWorkRepository<>)); + + services.AddScoped(typeof(IReadOnlyRepository<,>), typeof(UnitOfWorkRepository<,>)); + services.AddScoped(typeof(IBasicRepository<,>), typeof(UnitOfWorkRepository<,>)); + services.AddScoped(typeof(BaseRepository<,>), typeof(UnitOfWorkRepository<,>)); + + //批量注入程序集内的所有自建仓储类,可以根据自己需要来修改 + Assembly[] assemblies = new [] { typeof(XxxRepository).Assembly }; + if (assemblies?.Any() == true) + foreach (var asse in assemblies) + foreach (var repo in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(UnitOfWorkRepository).IsAssignableFrom(a))) + services.AddScoped(repo); +} +``` + +## 联级保存 + +请移步文档[《联级保存》](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98) + +## 实体变化事件 + +全局设置: + +```csharp +fsql.SetDbContextOptions(opt => { + opt.OnEntityChange = report => { + Console.WriteLine(report); + }; +}); +``` + +单独设置 DbContext 或者 UnitOfWork: + +```csharp +var ctx = fsql.CreateDbContext(); +ctx.Options.OnEntityChange = report => { + Console.WriteLine(report); +}; + +var uow = fsql.CreateUnitOfWork(); +uow.OnEntityChange = report => { + Console.WriteLine(report); +}; +``` + +参数 report 是一个 List 集合,集合元素的类型定义如下: + +```csharp +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 方法,参数为表达式,而并非实体; +```csharp +int Delete(Expression> predicate); +``` + +DbContext.SaveChanges,或者 Repository 对实体的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都会最多触发一次该事件。 \ No newline at end of file diff --git a/readme.md b/readme.md index 567d27b8..5f51591d 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,14 @@

- +

FreeSql 是功能强大的对象关系映射技术(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.0+ 或 Xamarin 扶摇直上,至强ORM只为自由编码;鹏程万里,至简Linq可使保留黑发;横批:FreeSql(诗人:Coder) -# Features +[![nuget](https://img.shields.io/nuget/v/FreeSql.svg?style=flat-square)](https://www.nuget.org/packages/FreeSql) [![stats](https://img.shields.io/nuget/dt/FreeSql.svg?style=flat-square)](https://www.nuget.org/stats/packages/FreeSql?groupby=Version) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/2881099/FreeSql/master/LICENSE.txt) + +## Features - [x] 支持 CodeFirst 迁移,哪怕使用 Access 数据库也支持; - [x] 支持 DbFirst 从数据库导入实体类,[安装实体类生成工具](https://github.com/2881099/FreeSql/wiki/DbFirst); @@ -16,29 +18,22 @@ FreeSql 是功能强大的对象关系映射技术(O/RM),支持 .NETCore 2.1+ - [x] 支持 读写分离、分表分库,租户设计,过滤器,乐观锁,悲观锁; - [x] 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/达梦数据库/Access; -| | | -| - | - | -| | [《新人学习指引》](https://www.cnblogs.com/FreeSql/p/11531300.html) \| [《Select》](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2) \| [《Update》](https://github.com/2881099/FreeSql/wiki/%e4%bf%ae%e6%94%b9) \| [《Insert》](https://github.com/2881099/FreeSql/wiki/%e6%b7%bb%e5%8a%a0) \| [《Delete》](https://github.com/2881099/FreeSql/wiki/%e5%88%a0%e9%99%a4) | -| | [《表达式函数》](https://github.com/2881099/FreeSql/wiki/%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0) \| [《CodeFirst》](https://github.com/2881099/FreeSql/wiki/CodeFirst) \| [《DbFirst》](https://github.com/2881099/FreeSql/wiki/DbFirst) \| [《BaseEntity》](https://github.com/2881099/FreeSql/tree/master/Examples/base_entity) | -| | [《Repository》](https://github.com/2881099/FreeSql/wiki/Repository) \| [《UnitOfWork》](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83) \| [《过滤器》](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8) \| [《乐观锁》](https://github.com/2881099/FreeSql/wiki/%e4%bf%ae%e6%94%b9#%E4%B9%90%E8%A7%82%E9%94%81) \| [《DbContext》](https://github.com/2881099/FreeSql/wiki/DbContext) | -| | [《读写分离》](https://github.com/2881099/FreeSql/wiki/%e8%af%bb%e5%86%99%e5%88%86%e7%a6%bb) \| [《分区分表》](https://github.com/2881099/FreeSql/wiki/%e5%88%86%e5%8c%ba%e5%88%86%e8%a1%a8) \| [《租户》](https://github.com/2881099/FreeSql/wiki/%e7%a7%9f%e6%88%b7) \| [《AOP》](https://github.com/2881099/FreeSql/wiki/AOP) \| [《黑科技》](https://github.com/2881099/FreeSql/wiki/%E9%AA%9A%E6%93%8D%E4%BD%9C) \| [*更新日志*](https://github.com/2881099/FreeSql/wiki/%e6%9b%b4%e6%96%b0%e6%97%a5%e5%bf%97) | +## Documentation -# Packages +[《新人学习指引》](https://www.cnblogs.com/FreeSql/p/11531300.html)、[《Select》](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2)、[《Update》](https://github.com/2881099/FreeSql/wiki/%e4%bf%ae%e6%94%b9)、[《Insert》](https://github.com/2881099/FreeSql/wiki/%e6%b7%bb%e5%8a%a0)、[《Delete》](https://github.com/2881099/FreeSql/wiki/%e5%88%a0%e9%99%a4) -| Package Name | NuGet | Downloads | -|--------------| ------- | ---- | -| FreeSql | [![nuget](https://img.shields.io/nuget/v/FreeSql.svg?style=flat-square)](https://www.nuget.org/packages/FreeSql) | [![stats](https://img.shields.io/nuget/dt/FreeSql.svg?style=flat-square)](https://www.nuget.org/stats/packages/FreeSql?groupby=Version) | -| FreeSql.Repository | [![nuget](https://img.shields.io/nuget/v/FreeSql.Repository.svg?style=flat-square)](https://www.nuget.org/packages/FreeSql.Repository) | [![stats](https://img.shields.io/nuget/dt/FreeSql.Repository.svg?style=flat-square)](https://www.nuget.org/stats/packages/FreeSql.Repository?groupby=Version) | -| FreeSql.DbContext | [![nuget](https://img.shields.io/nuget/v/FreeSql.DbContext.svg?style=flat-square)](https://www.nuget.org/packages/FreeSql.DbContext) | [![stats](https://img.shields.io/nuget/dt/FreeSql.DbContext.svg?style=flat-square)](https://www.nuget.org/stats/packages/FreeSql.DbContext?groupby=Version) | -| [FreeSql.AdminLTE](https://github.com/2881099/FreeSql.AdminLTE) | [![nuget](https://img.shields.io/nuget/v/FreeSql.AdminLTE.svg?style=flat-square)](https://www.nuget.org/packages/FreeSql.AdminLTE) | [![stats](https://img.shields.io/nuget/dt/FreeSql.AdminLTE.svg?style=flat-square)](https://www.nuget.org/stats/packages/FreeSql.AdminLTE?groupby=Version) | +[《表达式函数》](https://github.com/2881099/FreeSql/wiki/%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0)、[《CodeFirst》](https://github.com/2881099/FreeSql/wiki/CodeFirst)、[《DbFirst》](https://github.com/2881099/FreeSql/wiki/DbFirst)、[《BaseEntity》](https://github.com/2881099/FreeSql/tree/master/Examples/base_entity) -> FreeSql 提供了五种使用习惯,请根据实际情况选择团队合适的一种: +[《Repository》](https://github.com/2881099/FreeSql/wiki/Repository)、[《UnitOfWork》](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83)、[《过滤器》](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8)、[《乐观锁》](https://github.com/2881099/FreeSql/wiki/%e4%bf%ae%e6%94%b9#%E4%B9%90%E8%A7%82%E9%94%81)、[《DbContext》](https://github.com/2881099/FreeSql/wiki/DbContext) + +[《读写分离》](https://github.com/2881099/FreeSql/wiki/%e8%af%bb%e5%86%99%e5%88%86%e7%a6%bb)、[《分区分表》](https://github.com/2881099/FreeSql/wiki/%e5%88%86%e5%8c%ba%e5%88%86%e8%a1%a8)、[《租户》](https://github.com/2881099/FreeSql/wiki/%e7%a7%9f%e6%88%b7)、[《AOP》](https://github.com/2881099/FreeSql/wiki/AOP)、[《黑科技》](https://github.com/2881099/FreeSql/wiki/%E9%AA%9A%E6%93%8D%E4%BD%9C)、[*更新日志*](https://github.com/2881099/FreeSql/wiki/%e6%9b%b4%e6%96%b0%e6%97%a5%e5%bf%97) + +> FreeSql 提供多种使用习惯,请根据实际情况选择团队合适的一种: - 要么FreeSql,原始用法; - 要么[FreeSql.Repository](https://github.com/2881099/FreeSql/wiki/Repository),仓储+工作单元习惯; - 要么[FreeSql.DbContext](https://github.com/2881099/FreeSql/wiki/DbContext),有点像efcore的使用习惯; -- 要么[FreeSql.Connection.Extensions](https://github.com/2881099/FreeSql.Connection.Extensions),有点像Dapper的使用习惯; -- 要么[FreeSql.BaseEntity](https://github.com/2881099/FreeSql/tree/master/Examples/base_entity),我求简单现在使用的这个; +- 要么[FreeSql.BaseEntity](https://github.com/2881099/FreeSql/tree/master/Examples/base_entity),求简单使用这个; > [FluentApi 与 EfCore 90% 相似的扩展包](https://github.com/2881099/FreeSql/tree/master/Extensions/FreeSql.Extensions.EfCoreFluentApi); @@ -49,173 +44,139 @@ FreeSql 是功能强大的对象关系映射技术(O/RM),支持 .NETCore 2.1+ 欢迎更多使用 FreeSql 的开源项目加入目录 -# Providers - -| Package Name | Version | -|--------------| ------- | -| FreeSql.Provider.MySql | NETStandard2.0、net45、net40 | -| FreeSql.Provider.MySqlConnector | NETStandard2.0、net45 | -| FreeSql.Provider.PostgreSQL | NETStandard2.0、net45 | -| FreeSql.Provider.SqlServer | NETStandard2.0、net45、net40 | -| FreeSql.Provider.Sqlite | NETStandard2.0、net45、net40 | -| FreeSql.Provider.Oracle | NETStandard2.0、net45、net40 | -| [FreeSql.Provider.Odbc](https://github.com/2881099/FreeSql/tree/master/Providers/FreeSql.Provider.Odbc) | NETStandard2.0、net45、net40 | -| FreeSql.Extensions.LazyLoading | NETStandard2.0、net45、net40 | -| FreeSql.Extensions.JsonMap | NETStandard2.0、net45、net40 | -| FreeSql.Extensions.BaseEntity | NETStandard2.0 | - -# ConnectionStrings - -| DataType | ConnectionString | -| --- | --- | -| DataType.MySql | Data Source=127.0.0.1;Port=3306;User ID=root;Password=root; Initial Catalog=cccddd;Charset=utf8; SslMode=none;Min pool size=1 | -| DataType.PostgreSQL | Host=192.168.164.10;Port=5432;Username=postgres;Password=123456; Database=tedb;Pooling=true;Minimum Pool Size=1 | -| DataType.SqlServer | Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=1 | -| DataType.Oracle | user id=user1;password=123456; data source=//127.0.0.1:1521/XE;Pooling=true;Min Pool Size=1 | -| DataType.Sqlite | Data Source=\|DataDirectory\|\document.db; Attachs=xxxtb.db; Pooling=true;Min Pool Size=1 | -| DataType.OdbcMySql | Driver={MySQL ODBC 8.0 Unicode Driver}; Server=127.0.0.1;Persist Security Info=False; Trusted_Connection=Yes;UID=root;PWD=root; DATABASE=cccddd_odbc;Charset=utf8; SslMode=none;Min Pool Size=1 | -| DataType.OdbcSqlServer | Driver={SQL Server};Server=.;Persist Security Info=False; Trusted_Connection=Yes;Integrated Security=True; DATABASE=freesqlTest_odbc; Pooling=true;Min Pool Size=1 | -| DataType.OdbcOracle | Driver={Oracle in XE};Server=//127.0.0.1:1521/XE; Persist Security Info=False; Trusted_Connection=Yes;UID=odbc1;PWD=123456; Min Pool Size=1 | -| DataType.OdbcPostgreSQL | Driver={PostgreSQL Unicode(x64)};Server=192.168.164.10; Port=5432;UID=postgres;PWD=123456; Database=tedb_odbc;Pooling=true;Min Pool Size=1 | -| DataType.OdbcDameng (达梦) | Driver={DM8 ODBC DRIVER};Server=127.0.0.1:5236; Persist Security Info=False; Trusted_Connection=Yes; UID=USER1;PWD=123456789 | -| DataType.Odbc | Driver={SQL Server};Server=.;Persist Security Info=False; Trusted_Connection=Yes;Integrated Security=True; DATABASE=freesqlTest_odbc; Pooling=true;Min pool size=1 | -

-# Quick start +## Quick start > dotnet add package FreeSql.Provider.Sqlite ```csharp static IFreeSql fsql = new FreeSql.FreeSqlBuilder() - .UseConnectionString(FreeSql.DataType.Sqlite, - @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10") - .UseAutoSyncStructure(true) //自动同步实体结构到数据库 - .Build(); //请务必定义成 Singleton 单例模式 + .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=document.db") + .UseAutoSyncStructure(true) //自动同步实体结构到数据库 + .Build(); //请务必定义成 Singleton 单例模式 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 virtual ICollection Tags { get; set; } + [Column(IsIdentity = true)] + public int Id { get; set; } + public string Title { get; set; } + public string Url { get; set; } + public DateTime CreateTime { get; set; } + + public virtual ICollection Tags { get; set; } } class Song_tag { - public int Song_id { get; set; } - public virtual Song Song { get; set; } - - public int Tag_id { get; set; } - public virtual Tag Tag { get; set; } + public int Song_id { get; set; } + public virtual Song Song { get; set; } + + public int Tag_id { get; set; } + public virtual Tag Tag { get; set; } } class Tag { - [Column(IsIdentity = true)] - public int Id { get; set; } - public string Name { get; set; } - - public int? Parent_id { get; set; } - public virtual Tag Parent { get; set; } - - public virtual ICollection Songs { get; set; } - public virtual ICollection Tags { get; set; } + [Column(IsIdentity = true)] + public int Id { get; set; } + public string Name { get; set; } + + public int? Parent_id { get; set; } + public virtual Tag Parent { get; set; } + + public virtual ICollection Songs { get; set; } + public virtual ICollection Tags { get; set; } } ``` -# Query +## Query ```csharp //OneToOne、ManyToOne -var t0 = fsql.Select() - .Where(a => a.Parent.Parent.Name == "粤语") - .IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx")) - .ToList(); +fsql.Select() + .Where(a => a.Parent.Parent.Name == "粤语") + .IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx")) + .ToList(); //OneToMany -var t1 = fsql.Select() - .Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10)) - .ToList(); +fsql.Select() + .Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10)) + .ToList(); //ManyToMany -var t2 = fsql.Select() +fsql.Select() .Where(s => s.Tags.AsSelect().Any(t => t.Name == "国语")) .IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx")) .ToList(); //Other -var t3 = fsql.Select() - .Where(a => a.IsDelete == 0) - .WhereIf(keyword != null, a => a.UserName.Contains(keyword)) - .WhereIf(role_id > 0, a => a.RoleId == role_id) - .Where(a => a.Nodes.AsSelect().Any(t => t.Parent.Id == t.UserId)) - .Count(out var total) - .Page(page, size) - .OrderByDescending(a => a.Id) - .ToList() +fsql.Select() + .Where(a => a.IsDelete == 0) + .WhereIf(keyword != null, a => a.UserName.Contains(keyword)) + .WhereIf(role_id > 0, a => a.RoleId == role_id) + .Where(a => a.Nodes.AsSelect().Any(t => t.Parent.Id == t.UserId)) + .Count(out var total) + .Page(page, size) + .OrderByDescending(a => a.Id) + .ToList() ``` 更多前往Wiki:[《Select 查询数据文档》](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2) ```csharp -var t3 = fsql.Select() - .Where(a => new[] { 1, 2, 3 }.Contains(a.Id)) - .ToList(); -``` -```csharp -var t4 = fsql.Select() - .Where(a => a.CreateTime.Date == DateTime.Now.Date) - .ToList(); -``` -```csharp -var t5 = fsql.Select() - .OrderBy(a => Guid.NewGuid()) - .Limit(1) - .ToList(); +fsql.Select() + .Where(a => new[] { 1, 2, 3 }.Contains(a.Id)) + .ToList(); + +fsql.Select() + .Where(a => a.CreateTime.Date == DateTime.Today) + .ToList(); + +fsql.Select() + .OrderBy(a => Guid.NewGuid()) + .Limit(1) + .ToList(); ``` 更多前往Wiki:[《表达式函数》](https://github.com/2881099/FreeSql/wiki/%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0) -# Repository & UnitOfWork +## Repository & UnitOfWork > dotnet add package FreeSql.Repository ```csharp using (var uow = fsql.CreateUnitOfWork()) { - var repo1 = uow.GetRepository(); - var repo2 = uow.GetRepository(); + var repo1 = uow.GetRepository(); + var repo2 = uow.GetRepository(); - await repo1.InsertAsync(new Song()); - await repo2.InsertAsync(new Tag()); - uow.Commit(); + await repo1.InsertAsync(new Song()); + await repo2.InsertAsync(new Tag()); + uow.Commit(); } ``` -# DbContext & DbSet +## DbContext & DbSet > dotnet add package FreeSql.DbContext ```csharp using (var ctx = new fsql.CreateDbContext()) { - var songs = ctx.Set(); - var tags = ctx.Set(); + var songs = ctx.Set(); + var tags = ctx.Set(); - var tag = new Tag { - Name = "testaddsublist", + var tag = new Tag { + Name = "testaddsublist", + Tags = new[] { + new Tag { Name = "sub1" }, + new Tag { Name = "sub2" }, + new Tag { + Name = "sub3", Tags = new[] { - new Tag { Name = "sub1" }, - new Tag { Name = "sub2" }, - new Tag { - Name = "sub3", - Tags = new[] { - new Tag { Name = "sub3_01" } - } - } + new Tag { Name = "sub3_01" } } - }; - //tags.Add(tag); - ctx.Add(tag); - await ctx.SaveChangesAsync(); + } + } + }; + //tags.Add(tag); + ctx.Add(tag); + await ctx.SaveChangesAsync(); } ``` -# Performance +## Performance FreeSql Query & Dapper Query ```shell @@ -243,7 +204,7 @@ Elapsed: 00:00:00.6495301; Query Entity Counts: 131072; ORM: Dapper [Test code](FreeSql.Tests.PerformanceTests/MySqlAdoTest.cs)、[More](https://github.com/2881099/FreeSql/wiki/%e6%80%a7%e8%83%bd) -# Contributors +## Contributors [systemhejiyong](https://github.com/systemhejiyong)、 [LambertW](https://github.com/LambertW)、 @@ -260,7 +221,7 @@ Elapsed: 00:00:00.6495301; Query Entity Counts: 131072; ORM: Dapper (QQ群:4336577) -# Donation +## Donation L*y 58元、花花 88元、麦兜很乖 50元、网络来者 2000元、John 99.99元、alex 666元