mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
update readme
This commit is contained in:
parent
86b6608bcc
commit
157dbc283e
@ -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<Blog>
|
|
||||||
.Where(b => b.Rating > 3)
|
|
||||||
.OrderBy(b => b.Url)
|
|
||||||
.ToList();
|
|
||||||
```
|
|
||||||
|
|
||||||
## 插入
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var blog = new Blog { Url = "http://sample.com" };
|
|
||||||
blog.BlogId = (int)fsql.Insert<Blog>()
|
|
||||||
.AppendData(blog)
|
|
||||||
.ExecuteIdentity();
|
|
||||||
```
|
|
||||||
|
|
||||||
## 更新
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
fsql.Update<Blog>()
|
|
||||||
.Set(b => b.Url, "http://sample2222.com")
|
|
||||||
.Where(b => b.Url == "http://sample.com")
|
|
||||||
.ExecuteAffrows();
|
|
||||||
```
|
|
||||||
|
|
||||||
## 删除
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
fsql.Delete<Blog>()
|
|
||||||
.Where(b => b.Url == "http://sample.com")
|
|
||||||
.ExecuteAffrows();
|
|
||||||
```
|
|
||||||
|
|
||||||
## 后续步骤
|
|
||||||
|
|
||||||
有关介绍性教程,请参阅 [FreeSql 入门]()。
|
|
28
Docs/2 入门.md
28
Docs/2 入门.md
@ -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 应用
|
|
||||||
|
|
||||||
- * 新建数据库
|
|
||||||
- * 现有数据库
|
|
@ -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\<int\> \| NpgsqlRange\<int\>? | - | - | int4range | - |
|
|
||||||
| NpgsqlRange\<long\> \| NpgsqlRange\<long\>? | - | - | int8range | - |
|
|
||||||
| NpgsqlRange\<decimal\> \| NpgsqlRange\<decimal\>? | - | - | numrange | - |
|
|
||||||
| NpgsqlRange\<DateTime\> \| NpgsqlRange\<DateTime\>? | - | - | tsrange | - |
|
|
||||||
| PostgisPoint | - | - | geometry | - |
|
|
||||||
| PostgisLineString | - | - | geometry | - |
|
|
||||||
| PostgisPolygon | - | - | geometry | - |
|
|
||||||
| PostgisMultiPoint | - | - | geometry | - |
|
|
||||||
| PostgisMultiLineString | - | - | geometry | - |
|
|
||||||
| PostgisMultiPolygon | - | - | geometry | - |
|
|
||||||
| PostgisGeometry | - | - | geometry | - |
|
|
||||||
| PostgisGeometryCollection | - | - | geometry | - |
|
|
||||||
| Dictionary<string, string> | - | - | 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<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; }
|
|
||||||
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<Topic>();
|
|
||||||
//同步实体类型到数据库
|
|
||||||
```
|
|
109
Docs/dbfirst.md
109
Docs/dbfirst.md
@ -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
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{#title}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<!--绑定表达式-->
|
|
||||||
{#表达式}
|
|
||||||
{##表达式} 当表达式可能发生runtime错误时使用,性能没有上面的高
|
|
||||||
|
|
||||||
<!--可嵌套使用,同一标签最多支持3个指令-->
|
|
||||||
{include ../header.html}
|
|
||||||
<div @for="i 1, 101">
|
|
||||||
<p @if="i === 50" @for="item,index in data">aaa</p>
|
|
||||||
<p @else="i % 3 === 0">bbb {#i}</p>
|
|
||||||
<p @else="">ccc {#i}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--定义模块,可以将公共模块定义到一个文件中-->
|
|
||||||
{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}
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
@ -1,82 +0,0 @@
|
|||||||
# 删除数据
|
|
||||||
|
|
||||||
| 方法 | 返回值 | 参数 | 描述 |
|
|
||||||
| - | - | - | - |
|
|
||||||
| Where | \<this\> | Lambda | 表达式条件,仅支持实体基础成员(不包含导航对象) |
|
|
||||||
| Where | \<this\> | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) |
|
|
||||||
| Where | \<this\> | T1 \| IEnumerable<T1> | 传入实体或集合,将其主键作为条件 |
|
|
||||||
| WhereExists | \<this\> | ISelect | 子查询是否存在 |
|
|
||||||
| ToSql | string | | 返回即将执行的SQL语句 |
|
|
||||||
| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
|
|
||||||
| ExecuteDeleted | List\<T1\> | | 执行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<Topic> delete => fsql.Delete<Topic>();
|
|
||||||
|
|
||||||
[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<Topic>(object dywhere)
|
|
||||||
```
|
|
||||||
dywhere 支持
|
|
||||||
|
|
||||||
* 主键值
|
|
||||||
* new[] { 主键值1, 主键值2 }
|
|
||||||
* Topic对象
|
|
||||||
* new[] { Topic对象1, Topic对象2 }
|
|
||||||
* new { id = 1 }
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var t1 = fsql.Delete<Topic>(new[] { 1, 2 }).ToSql();
|
|
||||||
//DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2)
|
|
||||||
|
|
||||||
var t2 = fsql.Delete<Topic>(new Topic { Id = 1, Title = "test" }).ToSql();
|
|
||||||
//DELETE FROM `tb_topic` WHERE (`Id` = 1)
|
|
||||||
|
|
||||||
var t3 = fsql.Delete<Topic>(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<Topic>(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<Topic>();
|
|
||||||
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\<T1\> | | 执行SQL语句,返回被删除的记录 |
|
|
@ -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<string, string>
|
|
||||||
| 表达式 | 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) |
|
|
@ -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
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{#title}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<!--绑定表达式-->
|
|
||||||
{#表达式}
|
|
||||||
{##表达式} 当表达式可能发生runtime错误时使用,性能没有上面的高
|
|
||||||
|
|
||||||
<!--可嵌套使用,同一标签最多支持3个指令-->
|
|
||||||
{include ../header.html}
|
|
||||||
<div @for="i 1, 101">
|
|
||||||
<p @if="i === 50" @for="item,index in data">aaa</p>
|
|
||||||
<p @else="i % 3 === 0">bbb {#i}</p>
|
|
||||||
<p @else="">ccc {#i}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--定义模块,可以将公共模块定义到一个文件中-->
|
|
||||||
{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}
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
@ -1,79 +0,0 @@
|
|||||||
# 插入数据
|
|
||||||
|
|
||||||
| 方法 | 返回值 | 参数 | 描述 |
|
|
||||||
| - | - | - | - |
|
|
||||||
| AppendData | \<this\> | T1 \| IEnumerable<T1> | 追加准备插入的实体 |
|
|
||||||
| InsertColumns | \<this\> | Lambda | 只插入的列 |
|
|
||||||
| IgnoreColumns | \<this\> | Lambda | 忽略的列 |
|
|
||||||
| ToSql | string | | 返回即将执行的SQL语句 |
|
|
||||||
| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
|
|
||||||
| ExecuteIdentity | long | | 执行SQL语句,返回自增值 |
|
|
||||||
| ExecuteInserted | List\<T1\> | | 执行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<Topic> insert => fsql.Insert<Topic>();
|
|
||||||
|
|
||||||
[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<Topic>();
|
|
||||||
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\<T1\> | 执行SQL语句,返回插入后的记录 |
|
|
259
Docs/select.md
259
Docs/select.md
@ -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<Topic> select => fsql.Select<Topic>();
|
|
||||||
|
|
||||||
[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<TestTypeInfo> 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<TestTypeInfo>((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<TestTypeInfo, TestTypeParentInfo>((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<TestTypeInfo, TestTypeParentInfo>((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<TestTypeInfo>((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<TestTypeInfo>((a, b) => b.Guid == a.TestTypeInfoGuid)
|
|
||||||
.LeftJoin<TestTypeParentInfo>((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<TestTypeInfo, TestTypeParentInfo>((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<Topic> t1 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList();
|
|
||||||
```
|
|
||||||
|
|
||||||
### 返回 List + 导航属性的数据
|
|
||||||
```csharp
|
|
||||||
List<Topic> t2 = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid).ToList();
|
|
||||||
//此时会返回普通字段 + 导航对象 Type 的数据
|
|
||||||
```
|
|
||||||
|
|
||||||
### 指定字段返回
|
|
||||||
```csharp
|
|
||||||
//返回一个字段
|
|
||||||
List<int> 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<xxx> t6 = fsql.Ado.Query<xxx>("select * from song");
|
|
||||||
List<(int, string ,string)> t7 = fsql.Ado.Query<(int, string, string)>("select * from song");
|
|
||||||
List<dynamic> t8 = fsql.Ado.Query<dynamic>("select * from song");
|
|
||||||
```
|
|
||||||
|
|
||||||
### 分组聚合
|
|
||||||
```csharp
|
|
||||||
var groupby = fsql.Select<Topic>()
|
|
||||||
.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<T1> | | 执行SQL查询,返回 T1 实体所有字段的记录,若存在导航属性则一起查询返回,记录不存在时返回 Count 为 0 的列表 |
|
|
||||||
| ToList\<T\> | List\<T\> | Lambda | 执行SQL查询,返回指定字段的记录,记录不存在时返回 Count 为 0 的列表 |
|
|
||||||
| ToList\<T\> | List\<T\> | 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 | \<this\> | out long | 查询的记录数量,以参数out形式返回 |
|
|
||||||
| Skip | \<this\> | int offset | 查询向后偏移行数 |
|
|
||||||
| Offset | \<this\> | int offset | 查询向后偏移行数 |
|
|
||||||
| Limit | \<this\> | int limit | 查询多少条数据 |
|
|
||||||
| Take | \<this\> | int limit | 查询多少条数据 |
|
|
||||||
| Page | \<this\> | int pageIndex, int pageSize | 分页 |
|
|
||||||
| 【条件】 |
|
|
||||||
| Where | \<this\> | Lambda | 支持多表查询表达式 |
|
|
||||||
| WhereIf | \<this\> | bool, Lambda | 支持多表查询表达式 |
|
|
||||||
| Where | \<this\> | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) |
|
|
||||||
| WhereIf | \<this\> | bool, string, parms | 原生sql语法条件,WhereIf(true, "id = ?id", new { id = 1 }) |
|
|
||||||
| 【分组】 |
|
|
||||||
| GroupBy | \<this\> | Lambda | 按选择的列分组,GroupBy(a => a.Name) | GroupBy(a => new{a.Name,a.Time}) |
|
|
||||||
| GroupBy | \<this\> | string, parms | 按原生sql语法分组,GroupBy("concat(name, ?cc)", new { cc = 1 }) |
|
|
||||||
| Having | \<this\> | string, parms | 按原生sql语法聚合条件过滤,Having("count(name) = ?cc", new { cc = 1 }) |
|
|
||||||
| 【排序】 |
|
|
||||||
| OrderBy | \<this\> | Lambda | 按列排序,OrderBy(a => a.Time) |
|
|
||||||
| OrderByDescending | \<this\> | Lambda | 按列倒向排序,OrderByDescending(a => a.Time) |
|
|
||||||
| OrderBy | \<this\> | string, parms | 按原生sql语法排序,OrderBy("count(name) + ?cc", new { cc = 1 }) |
|
|
||||||
| 【联表】 |
|
|
||||||
| LeftJoin | \<this\> | Lambda | 左联查询,可使用导航属性,或指定关联的实体类型 |
|
|
||||||
| InnerJoin | \<this\> | Lambda | 联接查询,可使用导航属性,或指定关联的实体类型 |
|
|
||||||
| RightJoin | \<this\> | Lambda | 右联查询,可使用导航属性,或指定关联的实体类型 |
|
|
||||||
| LeftJoin | \<this\> | string, parms | 左联查询,使用原生sql语法,LeftJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) |
|
|
||||||
| InnerJoin | \<this\> | string, parms | 联接查询,使用原生sql语法,InnerJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) |
|
|
||||||
| RightJoin | \<this\> | string, parms | 右联查询,使用原生sql语法,RightJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) |
|
|
||||||
| From | \<this\> | Lambda | 多表查询,3个表以上使用非常方便,目前设计最大支持10个表 |
|
|
||||||
| 【其他】 |
|
|
||||||
| As | \<this\> | string alias = "a" | 指定别名 |
|
|
||||||
| Master | \<this\> | | 指定从主库查询(默认查询从库) |
|
|
||||||
| Caching | \<this\> | int seconds, string key = null | 缓存查询结果 |
|
|
129
Docs/update.md
129
Docs/update.md
@ -1,129 +0,0 @@
|
|||||||
# 更新数据
|
|
||||||
|
|
||||||
| 方法 | 返回值 | 参数 | 描述 |
|
|
||||||
| - | - | - | - |
|
|
||||||
| SetSource | \<this\> | T1 \| IEnumerable<T1> | 更新数据,设置更新的实体 |
|
|
||||||
| IgnoreColumns | \<this\> | Lambda | 忽略的列 |
|
|
||||||
| Set | \<this\> | Lambda, value | 设置列的新值,Set(a => a.Name, "newvalue") |
|
|
||||||
| Set | \<this\> | Lambda | 设置列的的新值为基础上增加,Set(a => a.Clicks + 1),相当于 clicks=clicks+1; |
|
|
||||||
| SetRaw | \<this\> | string, parms | 设置值,自定义SQL语法,SetRaw("title = ?title", new { title = "newtitle" }) |
|
|
||||||
| Where | \<this\> | Lambda | 表达式条件,仅支持实体基础成员(不包含导航对象) |
|
|
||||||
| Where | \<this\> | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) |
|
|
||||||
| Where | \<this\> | T1 \| IEnumerable<T1> | 传入实体或集合,将其主键作为条件 |
|
|
||||||
| WhereExists | \<this\> | ISelect | 子查询是否存在 |
|
|
||||||
| ToSql | string | | 返回即将执行的SQL语句 |
|
|
||||||
| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
|
|
||||||
| ExecuteUpdated | List\<T1\> | | 执行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<Topic> update => fsql.Update<Topic>();
|
|
||||||
```
|
|
||||||
|
|
||||||
### 动态条件
|
|
||||||
```csharp
|
|
||||||
Update<Topic>(object dywhere)
|
|
||||||
```
|
|
||||||
dywhere 支持
|
|
||||||
|
|
||||||
* 主键值
|
|
||||||
* new[] { 主键值1, 主键值2 }
|
|
||||||
* Topic对象
|
|
||||||
* new[] { Topic对象1, Topic对象2 }
|
|
||||||
* new { id = 1 }
|
|
||||||
|
|
||||||
### 更新指定列
|
|
||||||
```csharp
|
|
||||||
var t1 = fsql.Update<Topic>(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<Topic>(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<Topic>();
|
|
||||||
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\<T1\> | | 执行SQL语句,返回更新后的记录 |
|
|
@ -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
|
> dotnet add package FreeSql.DbContext
|
||||||
|
|
||||||
## 更新日志
|
## 如何使用
|
||||||
|
|
||||||
### v0.6.5
|
0、通用方法,为啥是0???
|
||||||
|
```
|
||||||
- 修复 Repository 级联保存的 bug;
|
using (var ctx = fsql.CreateDbContext()) {
|
||||||
- 添加工作单元开启方法;
|
//var db1 = ctx.Set<Song>();
|
||||||
- 适配 .net framework 4.5、netstandard 2.0;
|
//var db2 = ctx.Set<Tag>();
|
||||||
|
|
||||||
### 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\<dynamic\> 读取返回变为 List\<Dictionary\<string, object\>\>;
|
|
||||||
- 定义 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);
|
|
||||||
|
|
||||||
|
var item = new Song { };
|
||||||
|
ctx.Add(item);
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Repository & UnitOfWork
|
> 注意:DbContext 对象多线程不安全
|
||||||
|
|
||||||
仓储与工作单元一起使用,工作单元具有事务特点。
|
1、在 OnConfiguring 方法上配置与 IFreeSql 关联
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using (var unitOfWork = fsql.CreateUnitOfWork()) {
|
public class SongContext : DbContext {
|
||||||
var songRepository = unitOfWork.GetRepository<Song, int>();
|
|
||||||
var tagRepository = unitOfWork.GetRepository<Tag, int>();
|
|
||||||
|
|
||||||
var song = new Song { BigNumber = "1000000000000000000" };
|
public DbSet<Song> Songs { get; set; }
|
||||||
songRepository.Insert(song);
|
public DbSet<Song> Tags { get; set; }
|
||||||
|
|
||||||
songRepository.Update(song);
|
protected override void OnConfiguring(DbContextOptionsBuilder builder) {
|
||||||
|
builder.UseFreeSql(dbcontext_01.Startup.Fsql);
|
||||||
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<Song, int>();
|
|
||||||
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 {
|
public class Song {
|
||||||
[Column(IsIdentity = true)]
|
[Column(IsIdentity = true)]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string BigNumber { get; set; }
|
public DateTime? Create_time { get; set; }
|
||||||
|
public bool? Is_deleted { get; set; }
|
||||||
[Column(IsVersion = true)] //乐观锁
|
public string Title { get; set; }
|
||||||
public long versionRow { get; set; }
|
public string Url { 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<Tag> Tags { get; set; }
|
public virtual ICollection<Tag> Tags { get; set; }
|
||||||
}
|
}
|
||||||
|
public class Song_tag {
|
||||||
|
public int Song_id { get; set; }
|
||||||
|
public virtual Song Song { get; set; }
|
||||||
|
|
||||||
public class SongContext : DbContext {
|
public int Tag_id { get; set; }
|
||||||
public DbSet<Song> Songs { get; set; }
|
public virtual Tag Tag { get; set; }
|
||||||
public DbSet<Tag> Tags { get; set; }
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder builder) {
|
public class Tag {
|
||||||
builder.UseFreeSql(fsql);
|
[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<Song> Songs { get; set; }
|
||||||
|
public virtual ICollection<Tag> Tags { get; set; }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# 过滤器与验证
|
使用的时候与 EFCore 类似:
|
||||||
|
|
||||||
假设我们有User(用户)、Topic(主题)两个实体,在领域类中定义了两个仓储:
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var userRepository = fsql.GetGuidRepository<User>();
|
long id = 0;
|
||||||
var topicRepository = fsql.GetGuidRepository<Topic>();
|
|
||||||
|
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
|
```csharp
|
||||||
var userRepository = fsql.GetGuidRepository<User>(a => a.Id == 1);
|
public void ConfigureServices(IServiceCollection services)
|
||||||
var topicRepository = fsql.GetGuidRepository<Topic>(a => a.UserId == 1);
|
{
|
||||||
|
services.AddSingleton<IFreeSql>(Fsql);
|
||||||
|
services.AddFreeDbContext<SongContext>(options => options.UseFreeSql(Fsql));
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* 在查询/修改/删除时附加此条件,从而达到不会修改其他用户的数据;
|
在 mvc 中获取:
|
||||||
* 在添加时,使用表达式验证数据的合法性,若不合法则抛出异常;
|
|
||||||
|
|
||||||
# 分表与分库
|
|
||||||
|
|
||||||
FreeSql 提供 AsTable 分表的基础方法,GuidRepository 作为分存式仓储将实现了分表与分库(不支持跨服务器分库)的封装。
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var logRepository = fsql.GetGuidRepository<Log>(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
|
```csharp
|
||||||
fsql.GetGuidRepository<User>().Select.FromRepository(logRepository)
|
fsql.SetDbContextOptions(opt => {
|
||||||
.LeftJoin<Log>(b => b.UserId == a.Id)
|
opt.OnEntityChange = report => {
|
||||||
.ToList();
|
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 当主键无值时,仍然查询了一次数据库;
|
SqlRaw 目前有两处地方比较特殊:
|
||||||
- 增加 查询数据时 TrackToList 对导航集合的状态跟踪;
|
- 多对多联级更新导航属性的时候,对中间表的全部删除操作;
|
||||||
- 完善 AddOrUpdateNavigateList 级联保存,忽略标记 IsIgnore 的集合属性;
|
- 通用仓储类 BaseRepository 有一个 Delete 方法,参数为表达式,而并非实体;
|
||||||
- 完成 IFreeSql.Include、IncludeMany 功能;
|
```csharp
|
||||||
|
int Delete(Expression<Func<TEntity, bool>> predicate);
|
||||||
|
```
|
||||||
|
|
||||||
### v0.5.12
|
DbContext.SaveChanges,或者 Repository 对实体的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都会最多触发一次该事件。
|
||||||
|
|
||||||
- 增加 工作单元开关,可在任意 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\<object\>、Repository\<object\> 使用方法,配合 AsType(实体类型),实现弱类型操作;
|
|
||||||
- 修复 DbContext.AddOrUpdate 传入 null 时,任然会查询一次数据库的 bug;
|
|
||||||
- 优化 DbContext.AddOrUpdate 未添加实体主键的错误提醒;
|
|
||||||
- 修复 DbContext.Set\<object\> 缓存的 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 方法;
|
|
@ -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
|
> dotnet add package FreeSql.Repository
|
||||||
|
|
||||||
## Repository & UnitOfWork
|
## 定义
|
||||||
|
|
||||||
仓储与工作单元一起使用,工作单元具有事务特点。
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using (var unitOfWork = fsql.CreateUnitOfWork()) {
|
static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
|
||||||
var songRepository = unitOfWork.GetRepository<Song, int>();
|
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10")
|
||||||
var tagRepository = unitOfWork.GetRepository<Tag, int>();
|
.UseAutoSyncStructure(true) //自动迁移实体的结构到数据库
|
||||||
|
.Build(); //请务必定义成 Singleton 单例模式
|
||||||
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<Song, int>();
|
|
||||||
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 {
|
public class Song {
|
||||||
[Column(IsIdentity = true)]
|
[Column(IsIdentity = true)]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string BigNumber { get; set; }
|
public string Title { 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<Tag> Tags { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SongContext : DbContext {
|
|
||||||
public DbSet<Song> Songs { get; set; }
|
|
||||||
public DbSet<Tag> Tags { get; set; }
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder builder) {
|
|
||||||
builder.UseFreeSql(fsql);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# 过滤器与验证
|
## 使用方法
|
||||||
|
|
||||||
|
1、IFreeSql 的扩展方法;
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var curd = fsql.GetRepository<Song>();
|
||||||
|
```
|
||||||
|
|
||||||
|
> 注意:Repository对象多线程不安全
|
||||||
|
|
||||||
|
2、继承实现;
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class SongRepository : BaseRepository<Song, int> {
|
||||||
|
public SongRepository(IFreeSql fsql) : base(fsql, null, null) {}
|
||||||
|
|
||||||
|
//在这里增加 CURD 以外的方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3、依赖注入;
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public void ConfigureServices(IServiceCollection services) {
|
||||||
|
|
||||||
|
services.AddSingleton<IFreeSql>(Fsql);
|
||||||
|
services.AddFreeRepository(filter => filter
|
||||||
|
.Apply<ISoftDelete>("SoftDelete", a => a.IsDeleted == false)
|
||||||
|
.Apply<ITenant>("Tenant", a => a.TenantId == 1)
|
||||||
|
,
|
||||||
|
this.GetType().Assembly
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//在控制器使用
|
||||||
|
public SongsController(GuidRepository<Song> repos1) {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 依赖注入的方式可实现全局【过滤与验证】的设定,方便租户功能的设计;
|
||||||
|
|
||||||
|
更多资料:[《过滤器、全局过滤器》](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8)
|
||||||
|
|
||||||
|
## 过滤与验证
|
||||||
|
|
||||||
假设我们有User(用户)、Topic(主题)两个实体,在领域类中定义了两个仓储:
|
假设我们有User(用户)、Topic(主题)两个实体,在领域类中定义了两个仓储:
|
||||||
|
|
||||||
@ -98,7 +71,7 @@ var userRepository = fsql.GetGuidRepository<User>();
|
|||||||
var topicRepository = fsql.GetGuidRepository<Topic>();
|
var topicRepository = fsql.GetGuidRepository<Topic>();
|
||||||
```
|
```
|
||||||
|
|
||||||
在开发过程中,总是担心 topicRepository 的数据安全问题,即有可能查询或操作到其他用户的主题。因此我们在v0.0.7版本进行了改进,增加了 filter lambad 表达式参数。
|
在开发过程中,总是担心 topicRepository 的数据安全问题,即有可能查询或操作到其他用户的主题。因此我们在v0.0.7版本进行了改进,增加了 filter lambda 表达式参数。
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var userRepository = fsql.GetGuidRepository<User>(a => a.Id == 1);
|
var userRepository = fsql.GetGuidRepository<User>(a => a.Id == 1);
|
||||||
@ -108,7 +81,7 @@ var topicRepository = fsql.GetGuidRepository<Topic>(a => a.UserId == 1);
|
|||||||
* 在查询/修改/删除时附加此条件,从而达到不会修改其他用户的数据;
|
* 在查询/修改/删除时附加此条件,从而达到不会修改其他用户的数据;
|
||||||
* 在添加时,使用表达式验证数据的合法性,若不合法则抛出异常;
|
* 在添加时,使用表达式验证数据的合法性,若不合法则抛出异常;
|
||||||
|
|
||||||
# 分表与分库
|
## 分表与分库
|
||||||
|
|
||||||
FreeSql 提供 AsTable 分表的基础方法,GuidRepository 作为分存式仓储将实现了分表与分库(不支持跨服务器分库)的封装。
|
FreeSql 提供 AsTable 分表的基础方法,GuidRepository 作为分存式仓储将实现了分表与分库(不支持跨服务器分库)的封装。
|
||||||
|
|
||||||
@ -118,15 +91,148 @@ var logRepository = fsql.GetGuidRepository<Log>(null, oldname => $"{oldname}_{Da
|
|||||||
|
|
||||||
上面我们得到一个日志仓储按年月分表,使用它 CURD 最终会操作 Log_201903 表。
|
上面我们得到一个日志仓储按年月分表,使用它 CURD 最终会操作 Log_201903 表。
|
||||||
|
|
||||||
合并两个仓储,实现分表下的联表查询:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
fsql.GetGuidRepository<User>().Select.FromRepository(logRepository)
|
|
||||||
.LeftJoin<Log>(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<TEntity> 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<Song>();
|
||||||
|
var userRepo = uow.GetRepository<User>();
|
||||||
|
|
||||||
|
//上面两个仓储,由同一UnitOfWork uow 创建
|
||||||
|
//在此执行仓储操作
|
||||||
|
|
||||||
|
//这里不受异步方便影响
|
||||||
|
|
||||||
|
uow.Commit();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
参考:在 asp.net core 中注入工作单元方法
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
//第一步:
|
||||||
|
public class UnitOfWorkRepository<TEntity, TKey> : BaseRepository<TEntity, TKey>
|
||||||
|
{
|
||||||
|
public UnitOfWorkRepository(IFreeSql fsql, IUnitOfWork uow) : base(fsql, null, null)
|
||||||
|
{
|
||||||
|
this.UnitOfWork = uow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class UnitOfWorkRepository<TEntity> : BaseRepository<TEntity, int>
|
||||||
|
{
|
||||||
|
public UnitOfWorkRepository(IFreeSql fsql, IUnitOfWork uow) : base(fsql, null, null)
|
||||||
|
{
|
||||||
|
this.UnitOfWork = uow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//第二步:
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IFreeSql>(fsql);
|
||||||
|
services.AddScoped<FreeSql.IUnitOfWork>(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<Func<TEntity, bool>> predicate);
|
||||||
|
```
|
||||||
|
|
||||||
|
DbContext.SaveChanges,或者 Repository 对实体的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都会最多触发一次该事件。
|
227
readme.md
227
readme.md
@ -1,12 +1,14 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img height="210" src="https://github.com/2881099/FreeSql/blob/master/logo.png?raw=true"/>
|
<img height="160" src="https://github.com/2881099/FreeSql/blob/master/logo.png?raw=true"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
FreeSql 是功能强大的对象关系映射技术(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.0+ 或 Xamarin
|
FreeSql 是功能强大的对象关系映射技术(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.0+ 或 Xamarin
|
||||||
|
|
||||||
扶摇直上,至强ORM只为自由编码;鹏程万里,至简Linq可使保留黑发;横批:FreeSql(诗人:Coder)
|
扶摇直上,至强ORM只为自由编码;鹏程万里,至简Linq可使保留黑发;横批:FreeSql(诗人:Coder)
|
||||||
|
|
||||||
# Features
|
[](https://www.nuget.org/packages/FreeSql) [](https://www.nuget.org/stats/packages/FreeSql?groupby=Version) [](https://raw.githubusercontent.com/2881099/FreeSql/master/LICENSE.txt)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
- [x] 支持 CodeFirst 迁移,哪怕使用 Access 数据库也支持;
|
- [x] 支持 CodeFirst 迁移,哪怕使用 Access 数据库也支持;
|
||||||
- [x] 支持 DbFirst 从数据库导入实体类,[安装实体类生成工具](https://github.com/2881099/FreeSql/wiki/DbFirst);
|
- [x] 支持 DbFirst 从数据库导入实体类,[安装实体类生成工具](https://github.com/2881099/FreeSql/wiki/DbFirst);
|
||||||
@ -16,29 +18,22 @@ FreeSql 是功能强大的对象关系映射技术(O/RM),支持 .NETCore 2.1+
|
|||||||
- [x] 支持 读写分离、分表分库,租户设计,过滤器,乐观锁,悲观锁;
|
- [x] 支持 读写分离、分表分库,租户设计,过滤器,乐观锁,悲观锁;
|
||||||
- [x] 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/达梦数据库/Access;
|
- [x] 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/达梦数据库/Access;
|
||||||
|
|
||||||
| | |
|
## Documentation
|
||||||
| - | - |
|
|
||||||
| <img src="https://user-images.githubusercontent.com/16286519/55138232-f5e19e80-516d-11e9-9144-173cc7e52845.png" width="40" height="59"/> | [《新人学习指引》](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) |
|
|
||||||
| <img src="https://user-images.githubusercontent.com/16286519/55138241-faa65280-516d-11e9-8b27-139dea46e4df.png" width="40" height="59"/> | [《表达式函数》](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) |
|
|
||||||
| <img src="https://user-images.githubusercontent.com/16286519/55138263-06921480-516e-11e9-8da9-81f18a18b694.png" width="40" height="59"/> | [《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) |
|
|
||||||
| <img src="https://user-images.githubusercontent.com/16286519/55138284-0eea4f80-516e-11e9-8764-29264807f402.png" width="40" height="59"/> | [《读写分离》](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) |
|
|
||||||
|
|
||||||
# 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 |
|
[《表达式函数》](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 | [](https://www.nuget.org/packages/FreeSql) | [](https://www.nuget.org/stats/packages/FreeSql?groupby=Version) |
|
|
||||||
| FreeSql.Repository | [](https://www.nuget.org/packages/FreeSql.Repository) | [](https://www.nuget.org/stats/packages/FreeSql.Repository?groupby=Version) |
|
|
||||||
| FreeSql.DbContext | [](https://www.nuget.org/packages/FreeSql.DbContext) | [](https://www.nuget.org/stats/packages/FreeSql.DbContext?groupby=Version) |
|
|
||||||
| [FreeSql.AdminLTE](https://github.com/2881099/FreeSql.AdminLTE) | [](https://www.nuget.org/packages/FreeSql.AdminLTE) | [](https://www.nuget.org/stats/packages/FreeSql.AdminLTE?groupby=Version) |
|
|
||||||
|
|
||||||
> 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,原始用法;
|
||||||
- 要么[FreeSql.Repository](https://github.com/2881099/FreeSql/wiki/Repository),仓储+工作单元习惯;
|
- 要么[FreeSql.Repository](https://github.com/2881099/FreeSql/wiki/Repository),仓储+工作单元习惯;
|
||||||
- 要么[FreeSql.DbContext](https://github.com/2881099/FreeSql/wiki/DbContext),有点像efcore的使用习惯;
|
- 要么[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);
|
> [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 的开源项目加入目录
|
欢迎更多使用 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 |
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://images.cnblogs.com/cnblogs_com/kellynic/133561/o_functions06.png"/>
|
<img src="https://images.cnblogs.com/cnblogs_com/kellynic/133561/o_functions06.png"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Quick start
|
## Quick start
|
||||||
|
|
||||||
> dotnet add package FreeSql.Provider.Sqlite
|
> dotnet add package FreeSql.Provider.Sqlite
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
|
static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
|
||||||
.UseConnectionString(FreeSql.DataType.Sqlite,
|
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=document.db")
|
||||||
@"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10")
|
.UseAutoSyncStructure(true) //自动同步实体结构到数据库
|
||||||
.UseAutoSyncStructure(true) //自动同步实体结构到数据库
|
.Build(); //请务必定义成 Singleton 单例模式
|
||||||
.Build(); //请务必定义成 Singleton 单例模式
|
|
||||||
|
|
||||||
class Song {
|
class Song {
|
||||||
[Column(IsIdentity = true)]
|
[Column(IsIdentity = true)]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
public DateTime CreateTime { get; set; }
|
public DateTime CreateTime { get; set; }
|
||||||
|
|
||||||
public virtual ICollection<Tag> Tags { get; set; }
|
public virtual ICollection<Tag> Tags { get; set; }
|
||||||
}
|
}
|
||||||
class Song_tag {
|
class Song_tag {
|
||||||
public int Song_id { get; set; }
|
public int Song_id { get; set; }
|
||||||
public virtual Song Song { get; set; }
|
public virtual Song Song { get; set; }
|
||||||
|
|
||||||
public int Tag_id { get; set; }
|
public int Tag_id { get; set; }
|
||||||
public virtual Tag Tag { get; set; }
|
public virtual Tag Tag { get; set; }
|
||||||
}
|
}
|
||||||
class Tag {
|
class Tag {
|
||||||
[Column(IsIdentity = true)]
|
[Column(IsIdentity = true)]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public int? Parent_id { get; set; }
|
public int? Parent_id { get; set; }
|
||||||
public virtual Tag Parent { get; set; }
|
public virtual Tag Parent { get; set; }
|
||||||
|
|
||||||
public virtual ICollection<Song> Songs { get; set; }
|
public virtual ICollection<Song> Songs { get; set; }
|
||||||
public virtual ICollection<Tag> Tags { get; set; }
|
public virtual ICollection<Tag> Tags { get; set; }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# Query
|
## Query
|
||||||
```csharp
|
```csharp
|
||||||
//OneToOne、ManyToOne
|
//OneToOne、ManyToOne
|
||||||
var t0 = fsql.Select<Tag>()
|
fsql.Select<Tag>()
|
||||||
.Where(a => a.Parent.Parent.Name == "粤语")
|
.Where(a => a.Parent.Parent.Name == "粤语")
|
||||||
.IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx"))
|
.IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx"))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
//OneToMany
|
//OneToMany
|
||||||
var t1 = fsql.Select<Tag>()
|
fsql.Select<Tag>()
|
||||||
.Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10))
|
.Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
//ManyToMany
|
//ManyToMany
|
||||||
var t2 = fsql.Select<Song>()
|
fsql.Select<Song>()
|
||||||
.Where(s => s.Tags.AsSelect().Any(t => t.Name == "国语"))
|
.Where(s => s.Tags.AsSelect().Any(t => t.Name == "国语"))
|
||||||
.IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx"))
|
.IncludeMany(a => a.Tags, then => then.Where(sub => sub.Name == "xxx"))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
//Other
|
//Other
|
||||||
var t3 = fsql.Select<Xxx>()
|
fsql.Select<Xxx>()
|
||||||
.Where(a => a.IsDelete == 0)
|
.Where(a => a.IsDelete == 0)
|
||||||
.WhereIf(keyword != null, a => a.UserName.Contains(keyword))
|
.WhereIf(keyword != null, a => a.UserName.Contains(keyword))
|
||||||
.WhereIf(role_id > 0, a => a.RoleId == role_id)
|
.WhereIf(role_id > 0, a => a.RoleId == role_id)
|
||||||
.Where(a => a.Nodes.AsSelect().Any(t => t.Parent.Id == t.UserId))
|
.Where(a => a.Nodes.AsSelect().Any(t => t.Parent.Id == t.UserId))
|
||||||
.Count(out var total)
|
.Count(out var total)
|
||||||
.Page(page, size)
|
.Page(page, size)
|
||||||
.OrderByDescending(a => a.Id)
|
.OrderByDescending(a => a.Id)
|
||||||
.ToList()
|
.ToList()
|
||||||
```
|
```
|
||||||
更多前往Wiki:[《Select 查询数据文档》](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2)
|
更多前往Wiki:[《Select 查询数据文档》](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2)
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var t3 = fsql.Select<Song>()
|
fsql.Select<Song>()
|
||||||
.Where(a => new[] { 1, 2, 3 }.Contains(a.Id))
|
.Where(a => new[] { 1, 2, 3 }.Contains(a.Id))
|
||||||
.ToList();
|
.ToList();
|
||||||
```
|
|
||||||
```csharp
|
fsql.Select<Song>()
|
||||||
var t4 = fsql.Select<Song>()
|
.Where(a => a.CreateTime.Date == DateTime.Today)
|
||||||
.Where(a => a.CreateTime.Date == DateTime.Now.Date)
|
.ToList();
|
||||||
.ToList();
|
|
||||||
```
|
fsql.Select<Song>()
|
||||||
```csharp
|
.OrderBy(a => Guid.NewGuid())
|
||||||
var t5 = fsql.Select<Song>()
|
.Limit(1)
|
||||||
.OrderBy(a => Guid.NewGuid())
|
.ToList();
|
||||||
.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)
|
更多前往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
|
> dotnet add package FreeSql.Repository
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using (var uow = fsql.CreateUnitOfWork()) {
|
using (var uow = fsql.CreateUnitOfWork()) {
|
||||||
var repo1 = uow.GetRepository<Song, int>();
|
var repo1 = uow.GetRepository<Song>();
|
||||||
var repo2 = uow.GetRepository<Tag, int>();
|
var repo2 = uow.GetRepository<Tag>();
|
||||||
|
|
||||||
await repo1.InsertAsync(new Song());
|
await repo1.InsertAsync(new Song());
|
||||||
await repo2.InsertAsync(new Tag());
|
await repo2.InsertAsync(new Tag());
|
||||||
uow.Commit();
|
uow.Commit();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# DbContext & DbSet
|
## DbContext & DbSet
|
||||||
> dotnet add package FreeSql.DbContext
|
> dotnet add package FreeSql.DbContext
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using (var ctx = new fsql.CreateDbContext()) {
|
using (var ctx = new fsql.CreateDbContext()) {
|
||||||
var songs = ctx.Set<Song>();
|
var songs = ctx.Set<Song>();
|
||||||
var tags = ctx.Set<Tag>();
|
var tags = ctx.Set<Tag>();
|
||||||
|
|
||||||
var tag = new Tag {
|
var tag = new Tag {
|
||||||
Name = "testaddsublist",
|
Name = "testaddsublist",
|
||||||
|
Tags = new[] {
|
||||||
|
new Tag { Name = "sub1" },
|
||||||
|
new Tag { Name = "sub2" },
|
||||||
|
new Tag {
|
||||||
|
Name = "sub3",
|
||||||
Tags = new[] {
|
Tags = new[] {
|
||||||
new Tag { Name = "sub1" },
|
new Tag { Name = "sub3_01" }
|
||||||
new Tag { Name = "sub2" },
|
|
||||||
new Tag {
|
|
||||||
Name = "sub3",
|
|
||||||
Tags = new[] {
|
|
||||||
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
|
FreeSql Query & Dapper Query
|
||||||
```shell
|
```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)
|
[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)、
|
[systemhejiyong](https://github.com/systemhejiyong)、
|
||||||
[LambertW](https://github.com/LambertW)、
|
[LambertW](https://github.com/LambertW)、
|
||||||
@ -260,7 +221,7 @@ Elapsed: 00:00:00.6495301; Query Entity Counts: 131072; ORM: Dapper
|
|||||||
|
|
||||||
(QQ群:4336577)
|
(QQ群:4336577)
|
||||||
|
|
||||||
# Donation
|
## Donation
|
||||||
|
|
||||||
L*y 58元、花花 88元、麦兜很乖 50元、网络来者 2000元、John 99.99元、alex 666元
|
L*y 58元、花花 88元、麦兜很乖 50元、网络来者 2000元、John 99.99元、alex 666元
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user