This commit is contained in:
alexinea 2020-11-27 11:03:22 +08:00
commit 40cc415f62
1179 changed files with 296119 additions and 62435 deletions

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
[*.cs]
# Default severity for analyzer diagnostics with category 'Style'
dotnet_analyzer_diagnostic.category-Style.severity = none
# CS0649: 从未对字段“TransactionalAttribute._uowManager”赋值字段将一直保持其默认值 null
dotnet_diagnostic.CS0649.severity = none

17
.github/workflows/gitee-mirror.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Publish
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Sync to Gitee 💕
uses: wearerequired/git-mirror-action@master
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
with:
source-repo: "git@github.com:dotnetcore/freesql.git"
destination-repo: "git@gitee.com:FreeSql/FreeSql-ORM.git"

4
.gitignore vendored
View File

@ -1,6 +1,9 @@
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
FreeSql.xml
FreeSql.DbContext.xml
# User-specific files # User-specific files
*.suo *.suo
*.user *.user
@ -186,7 +189,6 @@ ClientBin/
*~ *~
*.dbmdl *.dbmdl
*.dbproj.schemaview *.dbproj.schemaview
*.pfx
*.publishsettings *.publishsettings
node_modules/ node_modules/
orleans.codegen.cs orleans.codegen.cs

View File

@ -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 入门]()。

View File

@ -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 应用
- * 新建数据库
- * 现有数据库

View File

View File

@ -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>();
//同步实体类型到数据库
```

View File

@ -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>
```

View File

@ -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语句返回被删除的记录 |

View File

@ -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) |

View File

@ -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>
```

View File

@ -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语句返回插入后的记录 |

View File

@ -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 | 缓存查询结果 |

View File

@ -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语句返回更新后的记录 |

View File

@ -0,0 +1,158 @@
using FreeSql;
using FreeSql.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace aspnetcore_transaction.Controllers
{
[ApiController]
[Route("")]
public class HomeController : ControllerBase
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
[HttpGet("1")]
//[Transactional]
virtual public object Get([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2,
[FromServices] SongService serviceSong)
{
serviceSong.Test1();
return "111";
}
[HttpGet("2")]
//[Transactional]
async virtual public Task<object> GetAsync([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2,
[FromServices] SongService serviceSong)
{
await serviceSong.Test2();
await serviceSong.Test3();
return "111";
}
}
public class SongService
{
BaseRepository<Song> _repoSong;
BaseRepository<Detail> _repoDetail;
SongRepository _repoSong2;
public SongService(BaseRepository<Song> repoSong, BaseRepository<Detail> repoDetail, SongRepository repoSong2)
{
var tb = repoSong.Orm.CodeFirst.GetTableByEntity(typeof(Song));
_repoSong = repoSong;
_repoDetail = repoDetail;
_repoSong2 = repoSong2;
}
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
public virtual void Test1()
{
_repoSong.Insert(new Song());
_repoDetail.Insert(new Detail());
_repoSong2.Insert(new Song());
}
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
async public virtual Task Test2()
{
await _repoSong.InsertAsync(new Song());
await _repoDetail.InsertAsync(new Detail());
await _repoSong2.InsertAsync(new Song());
}
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
async public virtual Task<object> Test3()
{
await _repoSong.InsertAsync(new Song());
await _repoDetail.InsertAsync(new Detail());
await _repoSong2.InsertAsync(new Song());
return "123";
}
}
public class SongRepository : DefaultRepository<Song, int>
{
public SongRepository(UnitOfWorkManager uowm) : base(uowm?.Orm, uowm) { }
}
[Description("123")]
public class Song
{
/// <summary>
/// 自增
/// </summary>
[Column(IsIdentity = true)]
[Description("自增id")]
public int Id { get; set; }
public string Title { get; set; }
}
public class Detail
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public int SongId { get; set; }
public string Title { get; set; }
}
public static class IdleBusExtesions
{
static AsyncLocal<string> AsyncLocalTenantId = new AsyncLocal<string>();
public static IdleBus<IFreeSql> ChangeTenant(this IdleBus<IFreeSql> ib, string tenantId)
{
AsyncLocalTenantId.Value = tenantId;
return ib;
}
public static IFreeSql Get(this IdleBus<IFreeSql> ib) => ib.Get(AsyncLocalTenantId.Value ?? "default");
public static IBaseRepository<T> GetRepository<T>(this IdleBus<IFreeSql> ib) where T : class => ib.Get().GetRepository<T>();
static void test()
{
IdleBus<IFreeSql> ib = null; //单例注入
var fsql = ib.Get(); //获取当前租户对应的 IFreeSql
var fsql00102 = ib.ChangeTenant("00102").Get(); //切换租户,后面的操作都是针对 00102
var songRepository = ib.GetRepository<Song>();
var detailRepository = ib.GetRepository<Detail>();
}
public static IServiceCollection AddRepository(this IServiceCollection services, params Assembly[] assemblies)
{
services.AddScoped(typeof(IBaseRepository<>), typeof(YourDefaultRepository<>));
services.AddScoped(typeof(BaseRepository<>), typeof(YourDefaultRepository<>));
services.AddScoped(typeof(IBaseRepository<,>), typeof(YourDefaultRepository<,>));
services.AddScoped(typeof(BaseRepository<,>), typeof(YourDefaultRepository<,>));
if (assemblies?.Any() == true)
foreach (var asse in assemblies)
foreach (var repo in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(IBaseRepository).IsAssignableFrom(a)))
services.AddScoped(repo);
return services;
}
}
class YourDefaultRepository<T> : BaseRepository<T> where T : class
{
public YourDefaultRepository(IdleBus<IFreeSql> ib) : base(ib.Get(), null, null) { }
}
class YourDefaultRepository<T, TKey> : BaseRepository<T, TKey> where T : class
{
public YourDefaultRepository(IdleBus<IFreeSql> ib) : base(ib.Get(), null, null) { }
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace aspnetcore_transaction
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseServiceProviderFactory(new FreeSql.DynamicProxyServiceProviderFactory());
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:35350/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"dbcontext_01": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:35351/"
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using aspnetcore_transaction.Controllers;
using FreeSql;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace aspnetcore_transaction
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
Fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\test_trans.db")
.UseAutoSyncStructure(true)
.UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
.UseNoneCommandParameter(true)
.Build();
Fsql.Aop.TraceBefore += (_, e) => Trace.WriteLine($"----TraceBefore---{e.Identifier} {e.Operation}");
Fsql.Aop.TraceAfter += (_, e) => Trace.WriteLine($"----TraceAfter---{e.Identifier} {e.Operation} {e.Remark} {e.Exception?.Message} {e.ElapsedMilliseconds}ms\r\n");
}
public IConfiguration Configuration { get; }
public static IFreeSql Fsql { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IFreeSql>(Fsql);
services.AddScoped<UnitOfWorkManager>();
services.AddFreeRepository(null, typeof(Startup).Assembly);
services.AddScoped<SongService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Console.InputEncoding = Encoding.GetEncoding("GB2312");
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(a => a.MapControllers());
}
}
}

View File

@ -0,0 +1,54 @@
using FreeSql;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
namespace FreeSql
{
/// <summary>
/// 使用事务执行,请查看 Program.cs 代码开启动态代理
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : DynamicProxyAttribute, IActionFilter
{
public Propagation Propagation { get; set; } = Propagation.Required;
public IsolationLevel IsolationLevel { get => _IsolationLevelPriv.Value; set => _IsolationLevelPriv = value; }
IsolationLevel? _IsolationLevelPriv;
[DynamicProxyFromServices]
#pragma warning disable IDE0044 // 添加只读修饰符
UnitOfWorkManager _uowManager;
#pragma warning restore IDE0044 // 添加只读修饰符
IUnitOfWork _uow;
public override Task Before(DynamicProxyBeforeArguments args) => OnBefore(_uowManager);
public override Task After(DynamicProxyAfterArguments args) => OnAfter(args.Exception);
//这里是为了 controller
public void OnActionExecuting(ActionExecutingContext context) => OnBefore(context.HttpContext.RequestServices.GetService(typeof(UnitOfWorkManager)) as UnitOfWorkManager);
public void OnActionExecuted(ActionExecutedContext context) => OnAfter(context.Exception);
Task OnBefore(UnitOfWorkManager uowm)
{
_uow = uowm.Begin(this.Propagation, this._IsolationLevelPriv);
return Task.FromResult(false);
}
Task OnAfter(Exception ex)
{
try
{
if (ex == null) _uow.Commit();
else _uow.Rollback();
}
finally
{
_uow.Dispose();
}
return Task.FromResult(false);
}
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>aspnetcore_transaction.xml</DocumentationFile>
<WarningLevel>3</WarningLevel>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
<PackageReference Include="FreeSql.DynamicProxy" Version="1.4.0" />
<PackageReference Include="IdleBus" Version="1.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>aspnetcore_transaction</name>
</assembly>
<members>
<member name="P:aspnetcore_transaction.Controllers.Song.Id">
<summary>
自增
</summary>
</member>
<member name="T:FreeSql.TransactionalAttribute">
<summary>
使用事务执行,请查看 Program.cs 代码开启动态代理
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,59 @@
using FreeSql;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class UserGroup : BaseEntity<UserGroup, int>
{
/// <summary>
/// 组名
/// </summary>
public string GroupName { get; set; }
public List<User1> User1s { get; set; }
}
public class Role : BaseEntity<Role, string>
{
public List<User1> User1s { get; set; }
}
public class RoleUser1 : BaseEntity<RoleUser1>
{
public string RoleId { get; set; }
public Guid User1Id { get; set; }
public Role Role { get; set; }
public User1 User1 { get; set; }
}
public class User1 : BaseEntity<User1, Guid>
{
public int GroupId { get; set; }
public UserGroup Group { get; set; }
public virtual List<Role> Roles { get; set; }
/// <summary>
/// 登陆名
/// </summary>
[MaxLength(32)]
public string Username { get; set; }
/// <summary>
/// 昵称
/// </summary>
[MaxLength(64)]
public string Nickname { get; set; }
/// <summary>
/// 头像
/// </summary>
[MaxLength(1024)]
public string Avatar { get; set; }
/// <summary>
/// 描述
/// </summary>
[MaxLength(4000)]
public string Description { get; set; }
}

View File

@ -0,0 +1,405 @@
using FreeSql;
using FreeSql.DataAnnotations;
using FreeSql.Extensions;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Data.Odbc;
using System.Data.SqlClient;
using System.Data.SQLite;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace base_entity
{
class Program
{
class TestConfig
{
public int clicks { get; set; }
public string title { get; set; }
}
[Table(Name = "sysconfig")]
public class S_SysConfig<T> : BaseEntity<S_SysConfig<T>>
{
[Column(IsPrimary = true)]
public string Name { get; set; }
[JsonMap]
public T Config { get; set; }
public T Config2 { get; set; }
}
public class Products : BaseEntity<Products, int>
{
public string title { get; set; }
public int testint { get; set; }
}
static AsyncLocal<IUnitOfWork> _asyncUow = new AsyncLocal<IUnitOfWork>();
public class TestEnumCls
{
public CollationTypeEnum val { get; set; } = CollationTypeEnum.Binary;
}
class Sys_reg_user
{
public Guid Id { get; set; }
public Guid OwnerId { get; set; }
public string UnionId { get; set; }
[Navigate(nameof(OwnerId))]
public Sys_owner Owner { get; set; }
}
class Sys_owner
{
public Guid Id { get; set; }
public Guid RegUserId { get; set; }
[Navigate(nameof(RegUserId))]
public Sys_reg_user RegUser { get; set; }
}
public class tttorder
{
[Column(IsPrimary = true)]
public long Id { get; set; }
public string Title { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public tttorder(string title, int quantity, decimal price)
{
Id = DateTime.Now.Ticks;
Title = title;
Quantity = quantity;
Price = price;
}
}
static void Main(string[] args)
{
#region IFreeSql
var fsql = new FreeSql.FreeSqlBuilder()
.UseAutoSyncStructure(true)
.UseNoneCommandParameter(true)
.UseConnectionString(FreeSql.DataType.Sqlite, "data source=test.db;max pool size=5")
.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=2")
//.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=3")
//.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=2")
//.UseNameConvert(FreeSql.Internal.NameConvertType.ToLower)
//.UseConnectionString(FreeSql.DataType.Oracle, "user id=user1;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=2")
//.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper)
//.UseConnectionString(FreeSql.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;Max pool size=2")
//.UseConnectionString(FreeSql.DataType.OdbcSqlServer, "Driver={SQL Server};Server=.;Persist Security Info=False;Trusted_Connection=Yes;Integrated Security=True;DATABASE=freesqlTest_odbc;Pooling=true;Max pool size=3")
//.UseConnectionString(FreeSql.DataType.OdbcPostgreSQL, "Driver={PostgreSQL Unicode(x64)};Server=192.168.164.10;Port=5432;UID=postgres;PWD=123456;Database=tedb_odbc;Pooling=true;Maximum Pool Size=2")
//.UseNameConvert(FreeSql.Internal.NameConvertType.ToLower)
//.UseConnectionString(FreeSql.DataType.OdbcOracle, "Driver={Oracle in XE};Server=//127.0.0.1:1521/XE;Persist Security Info=False;Trusted_Connection=Yes;UID=odbc1;PWD=123456")
//.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper)
//.UseConnectionString(FreeSql.DataType.OdbcDameng, "Driver={DM8 ODBC DRIVER};Server=127.0.0.1:5236;Persist Security Info=False;Trusted_Connection=Yes;UID=USER1;PWD=123456789")
.UseMonitorCommand(umcmd => Console.WriteLine(umcmd.CommandText))
.UseLazyLoading(true)
.Build();
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
#endregion
fsql.Insert(new tttorder("xx1", 1, 10)).ExecuteAffrows();
fsql.Insert(new tttorder("xx2", 2, 20)).ExecuteAffrows();
var tttorders = fsql.Select<tttorder>().Limit(2).ToList();
var tsql1 = fsql.Select<Sys_reg_user>()
.Include(a => a.Owner)
.Where(a => a.UnionId == "xxx")
.ToSql();
var tsql2 = fsql.Select<Sys_owner>()
.Where(a => a.RegUser.UnionId == "xxx2")
.ToSql();
var names = (fsql.Select<object>() as Select0Provider)._commonUtils.SplitTableName("`Backups.ProductStockBak`");
var dbparams = fsql.Ado.GetDbParamtersByObject(new { id = 1, name = "xxx" });
var sql = fsql.CodeFirst.GetComparisonDDLStatements(typeof(EMSServerModel.Model.User), "testxsx001");
var test01 = EMSServerModel.Model.User.Select.IncludeMany(a => a.Roles).ToList();
var test02 = EMSServerModel.Model.UserRole.Select.ToList();
var test01tb = EMSServerModel.Model.User.Orm.CodeFirst.GetTableByEntity(typeof(EMSServerModel.Model.User));
var us = User1.Select.Limit(10).ToList();
new Products { title = "product-1" }.Save();
new Products { title = "product-2" }.Save();
new Products { title = "product-3" }.Save();
new Products { title = "product-4" }.Save();
new Products { title = "product-5" }.Save();
var wdy1 = JsonConvert.DeserializeObject<DynamicFilterInfo>(@"
{
""Logic"" : ""And"",
""Filters"" :
[
{
""Logic"" : ""Or"",
""Filters"" :
[
{
""Field"" : ""title"",
""Operator"" : ""contains"",
""Value"" : ""product-1111"",
},
{
""Field"" : ""title"",
""Operator"" : ""contains"",
""Value"" : ""product-2222"",
}
]
},
{
""Field"" : ""title"",
""Operator"" : ""eq"",
""Value"" : ""product-2""
},
{
""Field"" : ""title"",
""Operator"" : ""eq"",
""Value"" : ""product-3""
},
{
""Field"" : ""title"",
""Operator"" : ""eq"",
""Value"" : ""product-4""
},
{
""Field"" : ""testint"",
""Operator"" : ""Range"",
""Value"" : [100,200]
},
{
""Field"" : ""testint"",
""Operator"" : ""Range"",
""Value"" : [""101"",""202""]
},
]
}
");
var config = new JsonSerializerOptions()
{
PropertyNamingPolicy = null,
AllowTrailingCommas = true,
IgnoreNullValues = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Converters = { new JsonStringEnumConverter() }
};
var wdy2 = System.Text.Json.JsonSerializer.Deserialize<DynamicFilterInfo>(@"
{
""Logic"" : 1,
""Filters"" :
[
{
""Field"" : ""title"",
""Operator"" : 8,
""Value"" : ""product-1"",
""Filters"" :
[
{
""Field"" : ""title"",
""Operator"" : 0,
""Value"" : ""product-1111""
}
]
},
{
""Field"" : ""title"",
""Operator"" : 8,
""Value"" : ""product-2""
},
{
""Field"" : ""title"",
""Operator"" : 8,
""Value"" : ""product-3""
},
{
""Field"" : ""title"",
""Operator"" : 8,
""Value"" : ""product-4""
},
{
""Field"" : ""testint"",
""Operator"" : 8,
""Value"" : 11
},
{
""Field"" : ""testint"",
""Operator"" : 8,
""Value"" : ""12""
},
{
""Field"" : ""testint"",
""Operator"" : ""Range"",
""Value"" : [100,200]
},
{
""Field"" : ""testint"",
""Operator"" : ""Range"",
""Value"" : [""101"",""202""]
}
]
}
", config);
Products.Select.WhereDynamicFilter(wdy1).ToList();
Products.Select.WhereDynamicFilter(wdy2).ToList();
var items1 = Products.Select.Limit(10).OrderByDescending(a => a.CreateTime).ToList();
var items2 = fsql.Select<Products>().Limit(10).OrderByDescending(a => a.CreateTime).ToList();
BaseEntity.Orm.UseJsonMap();
BaseEntity.Orm.UseJsonMap();
BaseEntity.Orm.CodeFirst.ConfigEntity<S_SysConfig<TestConfig>>(a =>
{
a.Property(b => b.Config2).JsonMap();
});
new S_SysConfig<TestConfig> { Name = "testkey11", Config = new TestConfig { clicks = 11, title = "testtitle11" }, Config2 = new TestConfig { clicks = 11, title = "testtitle11" } }.Save();
new S_SysConfig<TestConfig> { Name = "testkey22", Config = new TestConfig { clicks = 22, title = "testtitle22" }, Config2 = new TestConfig { clicks = 11, title = "testtitle11" } }.Save();
new S_SysConfig<TestConfig> { Name = "testkey33", Config = new TestConfig { clicks = 33, title = "testtitle33" }, Config2 = new TestConfig { clicks = 11, title = "testtitle11" } }.Save();
var testconfigs11 = S_SysConfig<TestConfig>.Select.ToList();
var testconfigs11tb = S_SysConfig<TestConfig>.Select.ToDataTable();
var testconfigs111 = S_SysConfig<TestConfig>.Select.ToList(a => a.Name);
var testconfigs112 = S_SysConfig<TestConfig>.Select.ToList(a => a.Config);
var testconfigs1122 = S_SysConfig<TestConfig>.Select.ToList(a => new { a.Name, a.Config });
var testconfigs113 = S_SysConfig<TestConfig>.Select.ToList(a => a.Config2);
var testconfigs1133 = S_SysConfig<TestConfig>.Select.ToList(a => new { a.Name, a.Config2 });
var repo = BaseEntity.Orm.Select<TestConfig>().Limit(10).ToList();
//void ConfigEntityProperty(object sender, FreeSql.Aop.ConfigEntityPropertyEventArgs e)
//{
// if (e.Property.PropertyType == typeof(byte[]))
// {
// var orm = sender as IFreeSql;
// switch (orm.Ado.DataType)
// {
// case DataType.SqlServer:
// e.ModifyResult.DbType = "image";
// break;
// case DataType.MySql:
// e.ModifyResult.DbType = "longblob";
// break;
// }
// }
//}
//fsql.Aop.ConfigEntityProperty += ConfigEntityProperty;
Task.Run(async () =>
{
using (var uow = BaseEntity.Orm.CreateUnitOfWork())
{
_asyncUow.Value = uow;
try
{
var id = (await new User1().SaveAsync()).Id;
}
finally
{
_asyncUow.Value = null;
}
uow.Commit();
}
var ug1 = new UserGroup();
ug1.GroupName = "分组一";
await ug1.InsertAsync();
var ug2 = new UserGroup();
ug2.GroupName = "分组二";
await ug2.InsertAsync();
var u1 = new User1();
u1.GroupId = ug1.Id;
await u1.SaveAsync();
await u1.DeleteAsync();
await u1.RestoreAsync();
u1.Nickname = "x1";
await u1.UpdateAsync();
var u11 = await User1.FindAsync(u1.Id);
u11.Description = "备注";
await u11.SaveAsync();
await u11.DeleteAsync();
var slslsl = Newtonsoft.Json.JsonConvert.SerializeObject(u1);
var u11null = User1.Find(u1.Id);
var u11s = User1.Where(a => a.Group.Id == ug1.Id).Limit(10).ToList();
var u11s2 = User1.Select.LeftJoin<UserGroup>((a, b) => a.GroupId == b.Id).Limit(10).ToList();
var ug1s = UserGroup.Select
.IncludeMany(a => a.User1s)
.Limit(10).ToList();
var ug1s2 = UserGroup.Select.Where(a => a.User1s.AsSelect().Any(b => b.Nickname == "x1")).Limit(10).ToList();
var r1 = new Role();
r1.Id = "管理员";
await r1.SaveAsync();
var r2 = new Role();
r2.Id = "超级会员";
await r2.SaveAsync();
var ru1 = new RoleUser1();
ru1.User1Id = u1.Id;
ru1.RoleId = r1.Id;
await ru1.SaveAsync();
ru1.RoleId = r2.Id;
await ru1.SaveAsync();
var u1roles = await User1.Select.IncludeMany(a => a.Roles).ToListAsync();
var u1roles2 = await User1.Select.Where(a => a.Roles.AsSelect().Any(b => b.Id == "xx")).ToListAsync();
}).Wait();
Console.WriteLine("按任意键结束。。。");
Console.ReadKey();
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using FreeSql.DataAnnotations;
using FreeSql;
namespace EMSServerModel.Model
{
/// <summary>
/// 角色表
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
public partial class Role : BaseEntity<Role>{
/// <summary>
/// 角色编号
/// </summary>
[JsonProperty, Column(IsPrimary = true, IsIdentity = true)]
public long RoleId { get; set; }
/// <summary>
/// 角色名称
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string RoleName { get; set; } = string.Empty;
/// <summary>
/// 角色描述
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string RoleDesc { get; set; } = string.Empty;
///// <summary>
///// 创建时间
///// </summary>
//[JsonProperty, Column(DbType = "date")]
//public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 启用
/// </summary>
[JsonProperty]
public bool IsEnable { get; set; } = true;
/// <summary>
/// 角色用户多对多导航
/// </summary>
[Navigate(ManyToMany = typeof(UserRole))]
public virtual ICollection<User> Users { get; protected set; }
}
}

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using FreeSql.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using FreeSql;
namespace EMSServerModel.Model
{
/// <summary>
/// 用户表bb123123
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
public partial class User : BaseEntity<User> {
//[JsonProperty, Column(IsIdentity = true)]
//public long Id { get; set; }
/// <summary>
/// 编号
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)", IsPrimary = true)]
public string UserId { get; set; } = string.Empty;
/// <summary>
/// 头像
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string Avatar { get; set; } = string.Empty;
/// <summary>
/// 姓名
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string UserName { get; set; } = string.Empty;
/// <summary>
/// 艺名
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string NickName { get; set; } = string.Empty;
/// <summary>
/// 电话
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string Tel { get; set; } = string.Empty;
/// <summary>
/// 性别
/// </summary>
[JsonProperty]
public Sex Sex { get; set; } = Sex.;
/// <summary>
/// 证件号
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string UID { get; set; } = string.Empty;
/// <summary>
/// 生日
/// </summary>
[JsonProperty, Column(DbType = "date")]
public DateTime? DateOfBirth { get; set; }
/// <summary>
/// 出生地
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string PlaceOfBirth { get; set; } = string.Empty;
/// <summary>
/// 居住地
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string Addr { get; set; } = string.Empty;
/// <summary>
/// 密码
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string Pwd { get; set; } = string.Empty;
/// <summary>
/// 部门编号
/// </summary>
[JsonProperty]
public long? DeptId { get; set; }
/// <summary>
/// 职务编号
/// </summary>
[JsonProperty, Column(IsNullable = true)]
public long TitleId { get; set; }
[JsonProperty]
public long TitleId2 { get; set; }
///// <summary>
///// 创建时间
///// </summary>
//[JsonProperty, Column(DbType = "date")]
//public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 国籍
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string Nationality { get; set; } = string.Empty;
/// <summary>
/// 经手人
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string Handler { get; set; } = string.Empty;
/// <summary>
/// 启用
/// </summary>
[JsonProperty]
public bool IsEnable { get; set; } = true;
/// <summary>
/// 备注
/// </summary>
[JsonProperty, Column(DbType = "varchar(100)")]
public string Memos { get; set; }
/// <summary>
///
/// </summary>
[Navigate(ManyToMany = typeof(UserRole))]
public virtual ICollection<Role> Roles { get; protected set; }
}
/// <summary>
/// 性别枚举
/// </summary>
public enum Sex
{
/// <summary>
/// 女=0
/// </summary>
=0,
/// <summary>
/// 男=1
/// </summary>
=1
}
}

View File

@ -0,0 +1,36 @@
using Newtonsoft.Json;
using FreeSql.DataAnnotations;
using FreeSql;
namespace EMSServerModel.Model
{
/// <summary>
/// 用户角色关系表aa111
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
public partial class UserRole : BaseEntity<UserRole>{
/// <summary>
/// 角色编号1
/// </summary>
[JsonProperty]
public long RoleId { get; set; }
/// <summary>
/// 角色导航
/// </summary>
[Navigate("RoleId")]
public Role Roles { get; set; }
/// <summary>
/// 用户编号
/// </summary>
[JsonProperty, Column(DbType = "varchar(50)")]
public string UserId { get; set; }
/// <summary>
/// 用户导航
/// </summary>
[Navigate("UserId")]
public User Users { get; set; }
}
}

View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>base_entity.xml</DocumentationFile>
<WarningLevel>3</WarningLevel>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.BaseEntity\FreeSql.Extensions.BaseEntity.csproj" />
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.JsonMap\FreeSql.Extensions.JsonMap.csproj" />
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.LazyLoading\FreeSql.Extensions.LazyLoading.csproj" />
<ProjectReference Include="..\..\FreeSql.Repository\FreeSql.Repository.csproj" />
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.MySql\FreeSql.Provider.MySql.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Odbc\FreeSql.Provider.Odbc.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Oracle\FreeSql.Provider.Oracle.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.PostgreSQL\FreeSql.Provider.PostgreSQL.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.SqlServer\FreeSql.Provider.SqlServer.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,198 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>base_entity</name>
</assembly>
<members>
<member name="P:UserGroup.GroupName">
<summary>
组名
</summary>
</member>
<member name="P:User1.Username">
<summary>
登陆名
</summary>
</member>
<member name="P:User1.Nickname">
<summary>
昵称
</summary>
</member>
<member name="P:User1.Avatar">
<summary>
头像
</summary>
</member>
<member name="P:User1.Description">
<summary>
描述
</summary>
</member>
<member name="T:EMSServerModel.Model.Role">
<summary>
角色表
</summary>
</member>
<member name="P:EMSServerModel.Model.Role.RoleId">
<summary>
角色编号
</summary>
</member>
<member name="P:EMSServerModel.Model.Role.RoleName">
<summary>
角色名称
</summary>
</member>
<member name="P:EMSServerModel.Model.Role.RoleDesc">
<summary>
角色描述
</summary>
</member>
<member name="P:EMSServerModel.Model.Role.IsEnable">
<summary>
启用
</summary>
</member>
<member name="P:EMSServerModel.Model.Role.Users">
<summary>
角色用户多对多导航
</summary>
</member>
<member name="T:EMSServerModel.Model.User">
<summary>
用户表bb123123
</summary>
</member>
<member name="P:EMSServerModel.Model.User.UserId">
<summary>
编号
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Avatar">
<summary>
头像
</summary>
</member>
<member name="P:EMSServerModel.Model.User.UserName">
<summary>
姓名
</summary>
</member>
<member name="P:EMSServerModel.Model.User.NickName">
<summary>
艺名
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Tel">
<summary>
电话
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Sex">
<summary>
性别
</summary>
</member>
<member name="P:EMSServerModel.Model.User.UID">
<summary>
证件号
</summary>
</member>
<member name="P:EMSServerModel.Model.User.DateOfBirth">
<summary>
生日
</summary>
</member>
<member name="P:EMSServerModel.Model.User.PlaceOfBirth">
<summary>
出生地
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Addr">
<summary>
居住地
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Pwd">
<summary>
密码
</summary>
</member>
<member name="P:EMSServerModel.Model.User.DeptId">
<summary>
部门编号
</summary>
</member>
<member name="P:EMSServerModel.Model.User.TitleId">
<summary>
职务编号
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Nationality">
<summary>
国籍
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Handler">
<summary>
经手人
</summary>
</member>
<member name="P:EMSServerModel.Model.User.IsEnable">
<summary>
启用
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Memos">
<summary>
备注
</summary>
</member>
<member name="P:EMSServerModel.Model.User.Roles">
<summary>
</summary>
</member>
<member name="T:EMSServerModel.Model.Sex">
<summary>
性别枚举
</summary>
</member>
<member name="F:EMSServerModel.Model.Sex.女">
<summary>
女=0
</summary>
</member>
<member name="F:EMSServerModel.Model.Sex.男">
<summary>
男=1
</summary>
</member>
<member name="T:EMSServerModel.Model.UserRole">
<summary>
用户角色关系表aa111
</summary>
</member>
<member name="P:EMSServerModel.Model.UserRole.RoleId">
<summary>
角色编号1
</summary>
</member>
<member name="P:EMSServerModel.Model.UserRole.Roles">
<summary>
角色导航
</summary>
</member>
<member name="P:EMSServerModel.Model.UserRole.UserId">
<summary>
用户编号
</summary>
</member>
<member name="P:EMSServerModel.Model.UserRole.Users">
<summary>
用户导航
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1,126 @@
# 前言
尝试过 ado.net、dapper、ef以及Repository仓储甚至自己还写过生成器工具以便做常规CRUD操作。
它们日常操作不方便之处:
- 每次使用前需要声明,再操作;
- 很多人一个实体类对应一个操作类或DAL、DbContext、Repository
BaseEntity 是一种极简单的 CodeFirst 开发方式特别对单表或多表CRUD利用继承节省了每个实体类的重复属性创建时间、ID等字段软件删除等功能进行 crud 操作时不必时常考虑仓储的使用;
本文介绍 BaseEntity 一种极简约的 CRUD 操作方法。
# 功能特点
- 自动迁移实体结构CodeFirst到数据库
- 直接操作实体的方法,进行 CRUD 操作;
- 简化用户定义实体类型省去主键、常用字段的配置如CreateTime、UpdateTime
- 实现单表、多表查询的软删除逻辑;
# 声明
> dotnet add package FreeSql.Extensions.BaseEntity
> dotnet add package FreeSql.Provider.Sqlite
1、定义一个主键 int 并且自增的实体类型BaseEntity TKey 指定为 int/long 时,会认为主键是自增;
```csharp
public class UserGroup : BaseEntity<UserGroup, int>
{
public string GroupName { get; set; }
}
```
如果不想主键是自增键,可以重写属性:
```csharp
public class UserGroup : BaseEntity<UserGroup, int>
{
[Column(IsIdentity = false)]
public override int Id { get; set; }
public string GroupName { get; set; }
}
```
> 有关更多实体的特性配置请参考资料https://github.com/2881099/FreeSql/wiki/%e5%ae%9e%e4%bd%93%e7%89%b9%e6%80%a7
2、定义一个主键 Guid 的实体类型,保存数据时会自动产生有序不重复的 Guid 值(不用自己指定 Guid.NewGuid()
```csharp
public class User : BaseEntity<UserGroup, Guid>
{
public string UserName { get; set; }
}
```
# CRUD 使用
```csharp
//添加
var item = new UserGroup { GroupName = "组一" };
item.Insert();
//更新
item.GroupName = "组二";
item.Update();
//添加或更新
item.Save();
//软删除
item.Delete();
//恢复软删除
item.Restore();
//根据主键获取对象
var item = UserGroup.Find(1);
//查询数据
var items = UserGroup.Where(a => a.Id > 10).ToList();
```
实体类型.Select 是一个查询对象,使用方法和 FreeSql.ISelect 一样;
支持多表查询时,软删除条件会附加在每个表中;
> 有关更多查询方法请参考资料https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2
# 事务建议
由于 AsyncLocal 平台兼容不好,所以交给外部管理事务。
```csharp
static AsyncLocal<IUnitOfWork> _asyncUow = new AsyncLocal<IUnitOfWork>();
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
```
在 Scoped 开始时: _asyncUow.Value = fsql.CreateUnitOfWork(); (也可以使用 UnitOfWorkManager 对象获取 uow)
在 Scoped 结束时_asyncUow.Value = null;
如下:
```csharp
using (var uow = fsql.CreateUnitOfWork())
{
_asyncUow.Value = uow;
try
{
//todo ... BaseEntity 内部 curd 方法保持使用 uow 事务
}
finally
{
_asyncUow.Value = null;
}
uow.Commit();
}
```

View File

@ -8,207 +8,227 @@ using BenchmarkDotNet.Running;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
//using SqlSugar; //using SqlSugar;
namespace FreeSql.Bechmarker { namespace FreeSql.Bechmarker
{
public class Program { public class Program
public static void Main(string[] args) { {
var summaryInsert = BenchmarkRunner.Run<OrmVsInsert>(); public static void Main(string[] args)
var summarySelect = BenchmarkRunner.Run<OrmVsSelect>(); {
var summaryUpdate = BenchmarkRunner.Run<OrmVsUpdate>(); var summaryInsert = BenchmarkRunner.Run<OrmVsInsert>();
} var summarySelect = BenchmarkRunner.Run<OrmVsSelect>();
} var summaryUpdate = BenchmarkRunner.Run<OrmVsUpdate>();
}
}
public class Orm { public class Orm
public static IFreeSql fsql = new FreeSql.FreeSqlBuilder() {
.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=20") public static 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=20") .UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=20")
.UseAutoSyncStructure(false) //.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=20")
.UseNoneCommandParameter(true) .UseAutoSyncStructure(false)
//.UseConfigEntityFromDbFirst(true) .UseNoneCommandParameter(true)
.Build(); //.UseConfigEntityFromDbFirst(true)
.Build();
//public static SqlSugarClient sugar { //public static SqlSugarClient sugar {
// get => new SqlSugarClient(new ConnectionConfig() { // get => new SqlSugarClient(new ConnectionConfig() {
// //不欺负让连接池100个最小 // //不欺负让连接池100个最小
// ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=20;Max Pool Size=20", // ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=20;Max Pool Size=20",
// DbType = DbType.SqlServer, // DbType = DbType.SqlServer,
// //ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20", // //ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20",
// //DbType = DbType.MySql, // //DbType = DbType.MySql,
// IsAutoCloseConnection = true, // IsAutoCloseConnection = true,
// InitKeyType = InitKeyType.Attribute // InitKeyType = InitKeyType.Attribute
// }); // });
//} //}
} }
class SongContext : DbContext { class SongContext : DbContext
public DbSet<Song> Songs { get; set; } {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { public DbSet<Song> Songs { get; set; }
optionsBuilder.UseSqlServer(@"Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=21;Max Pool Size=21"); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21"); {
} optionsBuilder.UseSqlServer(@"Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=21;Max Pool Size=21");
} //optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21");
}
}
[CoreJob] [RPlotExporter, RankColumn]
[RPlotExporter, RankColumn] public class OrmVsInsert
public class OrmVsInsert { {
public IEnumerable<Song> songs; public IEnumerable<Song> songs;
[Params(1, 500, 1000, 5000, 10000, 50000, 100000)] [Params(1, 500, 1000, 5000, 10000, 50000, 100000)]
public int size; public int size;
[GlobalSetup] [GlobalSetup]
public void Setup() { public void Setup()
Orm.fsql.CodeFirst.SyncStructure(typeof(Song), typeof(Song_tag), typeof(Tag)); {
//Orm.sugar.CodeFirst.InitTables(typeof(Song), typeof(Song_tag), typeof(Tag)); Orm.fsql.CodeFirst.SyncStructure(typeof(Song), typeof(Song_tag), typeof(Tag));
//sugar创建表失败SqlSugar.SqlSugarException: Sequence contains no elements //Orm.sugar.CodeFirst.InitTables(typeof(Song), typeof(Song_tag), typeof(Tag));
//sugar创建表失败SqlSugar.SqlSugarException: Sequence contains no elements
//测试前清空数据 //测试前清空数据
Orm.fsql.Delete<Song>().Where(a => a.Id > 0).ExecuteAffrows(); Orm.fsql.Delete<Song>().Where(a => a.Id > 0).ExecuteAffrows();
//Orm.sugar.Deleteable<Song>().Where(a => a.Id > 0).ExecuteCommand(); //Orm.sugar.Deleteable<Song>().Where(a => a.Id > 0).ExecuteCommand();
Orm.fsql.Ado.ExecuteNonQuery("delete from efcore_song"); Orm.fsql.Ado.ExecuteNonQuery("delete from efcore_song");
songs = Enumerable.Range(0, size).Select(a => new Song { songs = Enumerable.Range(0, size).Select(a => new Song
Create_time = DateTime.Now, {
Is_deleted = false, Create_time = DateTime.Now,
Title = $"Insert_{a}", Is_deleted = false,
Url = $"Url_{a}" Title = $"Insert_{a}",
}); Url = $"Url_{a}"
});
//预热 //预热
Orm.fsql.Insert(songs.First()).ExecuteAffrows(); Orm.fsql.Insert(songs.First()).ExecuteAffrows();
//Orm.sugar.Insertable(songs.First()).ExecuteCommand(); //Orm.sugar.Insertable(songs.First()).ExecuteCommand();
using (var db = new SongContext()) { using (var db = new SongContext())
//db.Configuration.AutoDetectChangesEnabled = false; {
db.Songs.AddRange(songs.First()); //db.Configuration.AutoDetectChangesEnabled = false;
db.SaveChanges(); db.Songs.AddRange(songs.First());
} db.SaveChanges();
} }
}
[Benchmark] [Benchmark]
public int FreeSqlInsert() => Orm.fsql.Insert(songs).ExecuteAffrows(); public int FreeSqlInsert() => Orm.fsql.Insert(songs).ExecuteAffrows();
//[Benchmark] //[Benchmark]
//public int SqlSugarInsert() => Orm.sugar.Insertable(songs.ToArray()).ExecuteCommand(); //public int SqlSugarInsert() => Orm.sugar.Insertable(songs.ToArray()).ExecuteCommand();
[Benchmark] [Benchmark]
public int EfCoreInsert() { public int EfCoreInsert()
using (var db = new SongContext()) { {
//db.Configuration.AutoDetectChangesEnabled = false; using (var db = new SongContext())
db.Songs.AddRange(songs.ToArray()); {
return db.SaveChanges(); //db.Configuration.AutoDetectChangesEnabled = false;
} db.Songs.AddRange(songs.ToArray());
} return db.SaveChanges();
} }
}
}
[CoreJob] [RPlotExporter, RankColumn]
[RPlotExporter, RankColumn] public class OrmVsUpdate
public class OrmVsUpdate { {
public List<Song> songs; public List<Song> songs;
[Params(1, 500, 1000, 5000, 10000, 50000, 100000)] [Params(1, 500, 1000, 5000, 10000, 50000, 100000)]
public int size; public int size;
[GlobalSetup] [GlobalSetup]
public void Setup() { public void Setup()
songs = Orm.fsql.Select<Song>().Limit(size).ToList(); {
} songs = Orm.fsql.Select<Song>().Limit(size).ToList();
}
[Benchmark] [Benchmark]
public int FreeSqlUpdate() => Orm.fsql.Update<Song>().SetSource(songs).ExecuteAffrows(); public int FreeSqlUpdate() => Orm.fsql.Update<Song>().SetSource(songs).ExecuteAffrows();
//[Benchmark] //[Benchmark]
//public int SqlSugarUpdate() => Orm.sugar.Updateable(songs).ExecuteCommand(); //public int SqlSugarUpdate() => Orm.sugar.Updateable(songs).ExecuteCommand();
[Benchmark] [Benchmark]
public int EfCoreUpdate() { public int EfCoreUpdate()
using (var db = new SongContext()) { {
//db.Configuration.AutoDetectChangesEnabled = false; using (var db = new SongContext())
db.Songs.UpdateRange(songs.ToArray()); {
return db.SaveChanges(); //db.Configuration.AutoDetectChangesEnabled = false;
} db.Songs.UpdateRange(songs.ToArray());
} return db.SaveChanges();
} }
}
}
[CoreJob] [RPlotExporter, RankColumn]
[RPlotExporter, RankColumn] public class OrmVsSelect
public class OrmVsSelect { {
[Params(1, 500, 1000, 5000, 10000, 50000, 100000)] [Params(1, 500, 1000, 5000, 10000, 50000, 100000)]
public int size; public int size;
[GlobalSetup] [GlobalSetup]
public void Setup() { public void Setup()
{
}
[Benchmark] }
public List<Song> FreeSqlSelect() => Orm.fsql.Select<Song>().Limit(size).ToList();
//[Benchmark] [Benchmark]
//public List<Song> SqlSugarSelect() => Orm.sugar.Queryable<Song>().Take(size).ToList(); public List<Song> FreeSqlSelect() => Orm.fsql.Select<Song>().Limit(size).ToList();
[Benchmark] //[Benchmark]
public List<Song> EfCoreSelect() { //public List<Song> SqlSugarSelect() => Orm.sugar.Queryable<Song>().Take(size).ToList();
using (var db = new SongContext()) {
return db.Songs.Take(size).AsNoTracking().ToList();
}
}
}
[FreeSql.DataAnnotations.Table(Name = "freesql_song")] [Benchmark]
//[SugarTable("sugar_song")] public List<Song> EfCoreSelect()
[Table("efcore_song")] {
public class Song { using (var db = new SongContext())
[FreeSql.DataAnnotations.Column(IsIdentity = true)] {
//[SugarColumn(IsPrimaryKey = true, IsIdentity = true)] return db.Songs.Take(size).AsNoTracking().ToList();
[Key] }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] }
public int Id { get; set; } }
public DateTime? Create_time { get; set; }
public bool? Is_deleted { get; set; }
public string Title { get; set; }
public string Url { get; set; }
//[SugarColumn(IsIgnore = true)] [FreeSql.DataAnnotations.Table(Name = "freesql_song")]
[NotMapped] //[SugarTable("sugar_song")]
public virtual ICollection<Tag> Tags { get; set; } [Table("efcore_song")]
} public class Song
[FreeSql.DataAnnotations.Table(Name = "freesql_song_tag")] {
//[SugarTable("sugar_song_tag")] [FreeSql.DataAnnotations.Column(IsIdentity = true)]
[Table("efcore_song_tag")] //[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public class Song_tag { [Key]
public int Song_id { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
//[SugarColumn(IsIgnore = true)] public int Id { get; set; }
[NotMapped] public DateTime? Create_time { get; set; }
public virtual Song Song { get; set; } public bool? Is_deleted { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public int Tag_id { get; set; } //[SugarColumn(IsIgnore = true)]
//[SugarColumn(IsIgnore = true)] [NotMapped]
[NotMapped] public virtual ICollection<Tag> Tags { get; set; }
public virtual Tag Tag { get; set; } }
} [FreeSql.DataAnnotations.Table(Name = "freesql_song_tag")]
[FreeSql.DataAnnotations.Table(Name = "freesql_tag")] //[SugarTable("sugar_song_tag")]
//[SugarTable("sugar_tag")] [Table("efcore_song_tag")]
[Table("efcore_tag")] public class Song_tag
public class Tag { {
[FreeSql.DataAnnotations.Column(IsIdentity = true)] public int Song_id { get; set; }
//[SugarColumn(IsPrimaryKey = true, IsIdentity = true)] //[SugarColumn(IsIgnore = true)]
[Key] [NotMapped]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] public virtual Song Song { get; set; }
public int Id { get; set; }
public int? Parent_id { get; set; }
//[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Parent { get; set; }
public decimal? Ddd { get; set; } public int Tag_id { get; set; }
public string Name { get; set; } //[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Tag { get; set; }
}
[FreeSql.DataAnnotations.Table(Name = "freesql_tag")]
//[SugarTable("sugar_tag")]
[Table("efcore_tag")]
public class Tag
{
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
//[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? Parent_id { get; set; }
//[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Parent { get; set; }
//[SugarColumn(IsIgnore = true)] public decimal? Ddd { get; set; }
[NotMapped] public string Name { get; set; }
public virtual ICollection<Song> Songs { get; set; }
//[SugarColumn(IsIgnore = true)] //[SugarColumn(IsIgnore = true)]
[NotMapped] [NotMapped]
public virtual ICollection<Tag> Tags { get; set; } public virtual ICollection<Song> Songs { get; set; }
} //[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual ICollection<Tag> Tags { get; set; }
}
} }

View File

@ -2,17 +2,17 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" /> <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.0-rc1.final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" /> <ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "5.0.0",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FreeSql;
using Microsoft.AspNetCore.Mvc;
namespace dbcontext_01.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
IFreeSql _orm;
SongContext _songContext;
CurdAfterLog _curdLog;
public ValuesController(SongContext songContext, IFreeSql orm1, CurdAfterLog curdLog)
{
_songContext = songContext;
_orm = orm1;
_curdLog = curdLog;
}
// GET api/values
[HttpGet]
async public Task<string> Get()
{
_orm.SetDbContextOptions(opt => {
opt.OnEntityChange = changeReport => {
Console.WriteLine(changeReport);
};
});
long id = 0;
try
{
var repos2Song = _orm.GetRepository<Song, int>();
repos2Song.Where(a => a.Id > 10).ToList();
//查询结果,进入 states
var song = new Song { Title = "empty" };
repos2Song.Insert(song);
song.Title = "empty01";
repos2Song.Update(song);
id = song.Id;
var adds = Enumerable.Range(0, 100)
.Select(a => new Song { CreateTime = DateTime.Now, Title = "xxxx" + a, Url = "url222" })
.ToList();
//创建一堆无主键值
repos2Song.Insert(adds);
for (var a = 0; a < 10; a++)
adds[a].Title = "dkdkdkdk" + a;
repos2Song.Update(adds);
//批量修改
repos2Song.Delete(adds.Skip(10).Take(20).ToList());
//批量删除10-20 元素的主键值会被清除
adds.Last().Url = "skldfjlksdjglkjjcccc";
repos2Song.Update(adds.Last());
adds.First().Url = "skldfjlksdjglkjjcccc";
repos2Song.Update(adds.First());
var ctx = _songContext;
var tag = new Tag
{
Name = "testaddsublist"
};
ctx.Tags.Add(tag);
ctx.UnitOfWork.GetOrBeginTransaction();
var tagAsync = new Tag
{
Name = "testaddsublist"
};
await ctx.Tags.AddAsync(tagAsync);
ctx.Songs.Select.Where(a => a.Id > 10).ToList();
//查询结果,进入 states
song = new Song { Title = "empty" };
//可插入的 song
ctx.Songs.Add(song);
id = song.Id;
//因有自增类型立即开启事务执行SQL返回自增值
adds = Enumerable.Range(0, 100)
.Select(a => new Song { CreateTime = DateTime.Now, Title = "xxxx" + a, Url = "url222" })
.ToList();
//创建一堆无主键值
ctx.Songs.AddRange(adds);
//立即执行,将自增值赋给 adds 所有元素,因为有自增类型,如果其他类型,指定传入主键值,不会立即执行
for (var a = 0; a < 10; a++)
adds[a].Title = "dkdkdkdk" + a;
ctx.Songs.UpdateRange(adds);
//批量修改,进入队列
ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList());
//批量删除,进入队列,完成时 10-20 元素的主键值会被清除
//ctx.Songs.Update(adds.First());
adds.Last().Url = "skldfjlksdjglkjjcccc";
ctx.Songs.Update(adds.Last());
adds.First().Url = "skldfjlksdjglkjjcccc";
ctx.Songs.Update(adds.First());
//单条修改 urls 的值,进入队列
//throw new Exception("回滚");
//ctx.Songs.Select.First();
//这里做一个查询,会立即打包【执行队列】,避免没有提交的数据,影响查询结果
ctx.SaveChanges();
//打包【执行队列】,提交事务
using (var uow = _orm.CreateUnitOfWork())
{
var reposSong = uow.GetRepository<Song, int>();
reposSong.Where(a => a.Id > 10).ToList();
//查询结果,进入 states
song = new Song { Title = "empty" };
reposSong.Insert(song);
id = song.Id;
adds = Enumerable.Range(0, 100)
.Select(a => new Song { CreateTime = DateTime.Now, Title = "xxxx" + a, Url = "url222" })
.ToList();
//创建一堆无主键值
reposSong.Insert(adds);
for (var a = 0; a < 10; a++)
adds[a].Title = "dkdkdkdk" + a;
reposSong.Update(adds);
//批量修改
reposSong.Delete(adds.Skip(10).Take(20).ToList());
//批量删除10-20 元素的主键值会被清除
adds.Last().Url = "skldfjlksdjglkjjcccc";
reposSong.Update(adds.Last());
adds.First().Url = "skldfjlksdjglkjjcccc";
reposSong.Update(adds.First());
uow.Commit();
}
using (ctx = new SongContext())
{
song = new Song { Title = "empty" };
await ctx.Songs.AddAsync(song);
id = song.Id;
adds = Enumerable.Range(0, 100)
.Select(a => new Song { CreateTime = DateTime.Now, 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();
}
}
catch
{
var item = await _orm.Select<Song>().Where(a => a.Id == id).FirstAsync();
throw;
}
var item22 = await _orm.Select<Song>().Where(a => a.Id == id).FirstAsync();
var item33 = await _orm.Select<Song>().Where(a => a.Id > id).ToListAsync();
return item22.Id.ToString() + "\r\n\r\n" + _curdLog.Sb.ToString();
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<object> Get(int id)
{
return _orm.Select<Song>().Where(a => a.Id == id).First();
}
[HttpGet("get{id}")]
public ActionResult<string> Get2(int id)
{
var item1 = _orm.Select<Song>().Where(a => a.Id == id).First();
var item2 = _orm.Select<Song>().Where(a => a.Id == id).First();
return _curdLog.Sb.ToString();
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}

View File

@ -0,0 +1,113 @@
using FreeSql;
using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
namespace dbcontext_01
{
public class SongContext : DbContext
{
public DbSet<Song> Songs { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseFreeSql(Startup.Fsql);
//这里直接指定一个静态的 IFreeSql 对象即可,切勿重新 Build()
}
protected override void OnModelCreating(ICodeFirst codefirst)
{
codefirst.Entity<Song>(eb =>
{
eb.ToTable("tb_song");
eb.Ignore(a => a.Field1);
eb.Property(a => a.Title).HasColumnType("varchar(50)").IsRequired();
eb.Property(a => a.Url).HasMaxLength(100);
eb.Property(a => a.RowVersion).IsRowVersion();
eb.Property(a => a.CreateTime).HasDefaultValueSql("current_timestamp");
eb.HasKey(a => a.Id);
eb.HasIndex(a => new { a.Id, a.Title }).IsUnique().HasName("idx_xxx11");
//一对多、多对一
eb.HasOne(a => a.Type).HasForeignKey(a => a.TypeId).WithMany(a => a.Songs);
//多对多
eb.HasMany(a => a.Tags).WithMany(a => a.Songs, typeof(Song_tag));
});
codefirst.Entity<SongType>(eb =>
{
eb.HasMany(a => a.Songs).WithOne(a => a.Type).HasForeignKey(a => a.TypeId);
eb.HasData(new[]
{
new SongType
{
Id = 1,
Name = "流行",
Songs = new List<Song>(new[]
{
new Song{ Title = "真的爱你" },
new Song{ Title = "爱你一万年" },
})
},
new SongType
{
Id = 2,
Name = "乡村",
Songs = new List<Song>(new[]
{
new Song{ Title = "乡里乡亲" },
})
},
});
});
codefirst.SyncStructure<SongType>();
codefirst.SyncStructure<Song>();
}
}
public class SongType
{
public int Id { get; set; }
public string Name { get; set; }
public List<Song> Songs { get; set; }
}
public class Song
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public DateTime CreateTime { get; set; }
public int TypeId { get; set; }
public SongType Type { get; set; }
public List<Tag> Tags { get; set; }
public int Field1 { get; set; }
public long RowVersion { get; set; }
}
public class Song_tag
{
public int Song_id { get; set; }
public Song Song { get; set; }
public int Tag_id { get; set; }
public Tag Tag { get; set; }
}
public class Tag
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Name { get; set; }
public List<Song> Songs { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using FreeSql;
using FreeSql.DataAnnotations;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace dbcontext_01
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeleteExistingFiles>True</DeleteExistingFiles>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net5.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net5.0</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<ProjectGuid>690f89e0-a721-423f-8f5d-d262f73235ea</ProjectGuid>
<SelfContained>true</SelfContained>
<PublishSingleFile>True</PublishSingleFile>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:53030/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"dbcontext_01": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:53031/"
}
}
}

View File

@ -0,0 +1,84 @@
using FreeSql;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
namespace dbcontext_01
{
public class Startup
{
public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)
{
Configuration = configuration;
Fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document2.db;Pooling=true;Max Pool Size=10")
//.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=10")
//.UseConnectionString(DataType.MySql, "Data Source=192.168.164.10;Port=33061;User ID=root;Password=123456;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=5")
//.UseSlave("Data Source=192.168.164.10;Port=33062;User ID=root;Password=123456;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=5")
//.UseConnectionString(FreeSql.DataType.Oracle, "user id=user1;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=10")
//.UseSyncStructureToUpper(true)
.UseAutoSyncStructure(true)
.UseLazyLoading(true)
.UseNoneCommandParameter(true)
.UseMonitorCommand(cmd => { }, (cmd, log) => Trace.WriteLine(log))
.Build();
Fsql.Aop.CurdAfter += (s, e) =>
{
Console.WriteLine(e.Identifier + ": " + e.EntityType.FullName + " " + e.ElapsedMilliseconds + "ms, " + e.Sql);
CurdAfterLog.Current.Value?.Sb.AppendLine($"{Thread.CurrentThread.ManagedThreadId}: {e.EntityType.FullName} {e.ElapsedMilliseconds}ms, {e.Sql}");
};
}
enum MySql { }
enum PgSql { }
public IConfiguration Configuration { get; }
public static IFreeSql Fsql { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IFreeSql>(Fsql);
services.AddFreeDbContext<SongContext>(options => options.UseFreeSql(Fsql));
services.AddScoped<CurdAfterLog>();
}
public void Configure(IApplicationBuilder app)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Console.InputEncoding = Encoding.GetEncoding("GB2312");
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(a => a.MapControllers());
}
}
public class CurdAfterLog : IDisposable
{
public static AsyncLocal<CurdAfterLog> Current = new AsyncLocal<CurdAfterLog>();
public StringBuilder Sb { get; } = new StringBuilder();
public CurdAfterLog()
{
Current.Value = this;
}
public void Dispose()
{
Sb.Clear();
Current.Value = null;
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>dbcontext_01.xml</DocumentationFile>
<WarningLevel>3</WarningLevel>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.LazyLoading\FreeSql.Extensions.LazyLoading.csproj" />
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.MySqlConnector\FreeSql.Provider.MySqlConnector.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>dbcontext_01</name>
</assembly>
<members>
</members>
</doc>

View File

@ -2,18 +2,22 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace efcore_to_freesql.DBContexts { namespace efcore_to_freesql.DBContexts
{
public class BaseDBContext : DbContext { public class BaseDBContext : DbContext
{
public static IFreeSql Fsql { get; set; } public static IFreeSql Fsql { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) { protected override void OnModelCreating(ModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder); {
Fsql.CodeFirst.ConfigEntity(modelBuilder.Model); //ͬ²½ÅäÖà base.OnModelCreating(modelBuilder);
} Fsql.CodeFirst.ConfigEntity(modelBuilder.Model); //ͬ²½ÅäÖÃ
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { }
optionsBuilder.UseSqlite(@"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10"); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
} {
} optionsBuilder.UseSqlite(@"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10");
}
}
} }

View File

@ -1,18 +1,21 @@
using efcore_to_freesql.Entitys; using efcore_to_freesql.Entitys;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace efcore_to_freesql.DBContexts { namespace efcore_to_freesql.DBContexts
{
public class Topic1Context : BaseDBContext { public class Topic1Context : BaseDBContext
{
public DbSet<Topic1> Topic1s { get; set; } public DbSet<Topic1> Topic1s { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) { protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Topic1>().ToTable("topic1_sss").HasKey(a => a.Id); modelBuilder.Entity<Topic1>().ToTable("topic1_sss").HasKey(a => a.Id);
modelBuilder.Entity<Topic1>().Property(a => a.Id).HasColumnName("topic1_id").ValueGeneratedOnAdd(); modelBuilder.Entity<Topic1>().Property(a => a.Id).HasColumnName("topic1_id").ValueGeneratedOnAdd();
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
} }
} }

View File

@ -1,18 +1,21 @@
using efcore_to_freesql.Entitys; using efcore_to_freesql.Entitys;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace efcore_to_freesql.DBContexts { namespace efcore_to_freesql.DBContexts
{
public class Topic2Context : BaseDBContext { public class Topic2Context : BaseDBContext
{
public DbSet<Topic2> Topic2s { get; set; } public DbSet<Topic2> Topic2s { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) { protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Topic2>().ToTable("topic2_sss"); modelBuilder.Entity<Topic2>().ToTable("topic2_sss");
modelBuilder.Entity<Topic2>().Property(a => a.Id).HasColumnName("topic2_id"); modelBuilder.Entity<Topic2>().Property(a => a.Id).HasColumnName("topic2_id");
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
} }
} }

View File

@ -0,0 +1,48 @@
using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
namespace efcore_to_freesql.Entitys
{
public class SongType
{
public int Id { get; set; }
public string Name { get; set; }
public List<Song> Songs { get; set; }
}
public class Song
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public DateTime CreateTime { get; set; }
public int TypeId { get; set; }
public SongType Type { get; set; }
public List<Tag> Tags { get; set; }
public int Field1 { get; set; }
public long RowVersion { get; set; }
}
public class Song_tag
{
public int Song_id { get; set; }
public Song Song { get; set; }
public int Tag_id { get; set; }
public Tag Tag { get; set; }
}
public class Tag
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Name { get; set; }
public List<Song> Songs { get; set; }
}
}

View File

@ -1,6 +1,7 @@
using System; using System;
namespace efcore_to_freesql.Entitys { namespace efcore_to_freesql.Entitys
{
public class Topic1 public class Topic1
{ {

View File

@ -2,7 +2,8 @@ using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace efcore_to_freesql.Entitys { namespace efcore_to_freesql.Entitys
{
public class Topic2 public class Topic2
{ {

View File

@ -1,4 +1,5 @@
using FreeSql; using efcore_to_freesql.Entitys;
using FreeSql;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using System; using System;
@ -6,60 +7,175 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
public static class CodeFirstExtensions { public static class CodeFirstExtensions
{
public static void ConfigEntity(this ICodeFirst codeFirst, IModel efmodel) { public static void ConfigEntity(this ICodeFirst codeFirst, IModel efmodel)
{
foreach (var type in efmodel.GetEntityTypes()) { foreach (var type in efmodel.GetEntityTypes())
{
codeFirst.ConfigEntity(type.ClrType, a => { codeFirst.ConfigEntity(type.ClrType, a =>
{
//表名 //表名
var relationalTableName = type.FindAnnotation("Relational:TableName"); var relationalTableName = type.FindAnnotation("Relational:TableName");
if (relationalTableName != null) if (relationalTableName != null)
a.Name(relationalTableName.Value?.ToString() ?? type.ClrType.Name); a.Name(relationalTableName.Value?.ToString() ?? type.ClrType.Name);
foreach (var prop in type.GetProperties()) { foreach (var prop in type.GetProperties())
{
var freeProp = a.Property(prop.Name); var freeProp = a.Property(prop.Name);
//列名 //列名
var relationalColumnName = prop.FindAnnotation("Relational:ColumnName"); var relationalColumnName = prop.FindAnnotation("Relational:ColumnName");
if (relationalColumnName != null) if (relationalColumnName != null)
freeProp.Name(relationalColumnName.Value?.ToString() ?? prop.Name); freeProp.Name(relationalColumnName.Value?.ToString() ?? prop.Name);
//主键 //主键
freeProp.IsPrimary(prop.IsPrimaryKey()); freeProp.IsPrimary(prop.IsPrimaryKey());
//自增 //自增
freeProp.IsIdentity( freeProp.IsIdentity(
prop.ValueGenerated == ValueGenerated.Never || prop.ValueGenerated == ValueGenerated.Never ||
prop.ValueGenerated == ValueGenerated.OnAdd || prop.ValueGenerated == ValueGenerated.OnAdd ||
prop.GetAnnotations().Where(z => prop.GetAnnotations().Where(z =>
z.Name == "SqlServer:ValueGenerationStrategy" && z.Value.ToString().Contains("IdentityColumn") //sqlserver 自增 z.Name == "SqlServer:ValueGenerationStrategy" && z.Value.ToString().Contains("IdentityColumn") //sqlserver 自增
|| z.Value.ToString().Contains("IdentityColumn") //其他数据库实现未经测试 || z.Value.ToString().Contains("IdentityColumn") //其他数据库实现未经测试
).Any() ).Any()
); );
//可空 //可空
freeProp.IsNullable(prop.AfterSaveBehavior != PropertySaveBehavior.Throw); freeProp.IsNullable(prop.GetAfterSaveBehavior() != PropertySaveBehavior.Throw);
//类型 //类型
var relationalColumnType = prop.FindAnnotation("Relational:ColumnType"); var relationalColumnType = prop.FindAnnotation("Relational:ColumnType");
if (relationalColumnType != null) { if (relationalColumnType != null)
{
var dbType = relationalColumnType.ToString(); var dbType = relationalColumnType.ToString();
if (!string.IsNullOrEmpty(dbType)) { if (!string.IsNullOrEmpty(dbType))
{
var maxLength = prop.FindAnnotation("MaxLength"); var maxLength = prop.FindAnnotation("MaxLength");
if (maxLength != null) if (maxLength != null)
dbType += $"({maxLength})"; dbType += $"({maxLength})";
freeProp.DbType(dbType); freeProp.DbType(dbType);
} }
} }
} }
}); });
} }
} }
public static void EfCoreFluentApiTestGeneric(this ICodeFirst cf)
{
cf.Entity<Song>(eb =>
{
eb.ToTable("tb_song1");
eb.Ignore(a => a.Field1);
eb.Property(a => a.Title).HasColumnType("varchar(50)").IsRequired();
eb.Property(a => a.Url).HasMaxLength(100);
eb.Property(a => a.RowVersion).IsRowVersion();
eb.Property(a => a.CreateTime).HasDefaultValueSql("current_timestamp");
eb.HasKey(a => a.Id);
eb.HasIndex(a => a.Title).IsUnique().HasName("idx_tb_song1111");
//一对多、多对一
eb.HasOne(a => a.Type).HasForeignKey(a => a.TypeId).WithMany(a => a.Songs);
//多对多
eb.HasMany(a => a.Tags).WithMany(a => a.Songs, typeof(Song_tag));
});
cf.Entity<SongType>(eb =>
{
eb.ToTable("tb_songtype1");
eb.HasMany(a => a.Songs).WithOne(a => a.Type).HasForeignKey(a => a.TypeId);
eb.HasData(new[]
{
new SongType
{
Id = 1,
Name = "流行",
Songs = new List<Song>(new[]
{
new Song{ Title = "真的爱你" },
new Song{ Title = "爱你一万年" },
})
},
new SongType
{
Id = 2,
Name = "乡村",
Songs = new List<Song>(new[]
{
new Song{ Title = "乡里乡亲" },
})
},
});
});
cf.SyncStructure<SongType>();
cf.SyncStructure<Song>();
}
public static void EfCoreFluentApiTestDynamic(this ICodeFirst cf)
{
cf.Entity(typeof(Song), eb =>
{
eb.ToTable("tb_song2");
eb.Ignore("Field1");
eb.Property("Title").HasColumnType("varchar(50)").IsRequired();
eb.Property("Url").HasMaxLength(100);
eb.Property("RowVersion").IsRowVersion();
eb.Property("CreateTime").HasDefaultValueSql("current_timestamp");
eb.HasKey("Id");
eb.HasIndex("Title").IsUnique().HasName("idx_tb_song2222");
//一对多、多对一
eb.HasOne("Type").HasForeignKey("TypeId").WithMany("Songs");
//多对多
eb.HasMany("Tags").WithMany("Songs", typeof(Song_tag));
});
cf.Entity(typeof(SongType), eb =>
{
eb.ToTable("tb_songtype2");
eb.HasMany("Songs").WithOne("Type").HasForeignKey("TypeId");
eb.HasData(new[]
{
new SongType
{
Id = 1,
Name = "流行",
Songs = new List<Song>(new[]
{
new Song{ Title = "真的爱你" },
new Song{ Title = "爱你一万年" },
})
},
new SongType
{
Id = 2,
Name = "乡村",
Songs = new List<Song>(new[]
{
new Song{ Title = "乡里乡亲" },
})
},
});
});
cf.SyncStructure<SongType>();
cf.SyncStructure<Song>();
}
} }

View File

@ -1,30 +1,30 @@
{ {
"$schema": "http://json.schemastore.org/launchsettings.json", "$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": { "iisSettings": {
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:58143", "applicationUrl": "http://localhost:58143",
"sslPort": 44349 "sslPort": 44349
} }
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}, },
"efcore_to_freesql": { "profiles": {
"commandName": "Project", "IIS Express": {
"launchBrowser": true, "commandName": "IISExpress",
"launchUrl": "api/values", "launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000", "launchUrl": "api/values",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
},
"efcore_to_freesql": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
} }
}
} }

View File

@ -22,57 +22,66 @@ namespace efcore_to_freesql
{ {
Configuration = configuration; Configuration = configuration;
Fsql = new FreeSql.FreeSqlBuilder() Fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10") .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10")
.UseAutoSyncStructure(true) .UseAutoSyncStructure(true)
.Build(); .Build();
DBContexts.BaseDBContext.Fsql = Fsql; //Fsql.CodeFirst.EfCoreFluentApiTestGeneric();
Fsql.CodeFirst.EfCoreFluentApiTestDynamic();
var sql11 = Fsql.Select<Topic1>().ToSql(); BaseDBContext.Fsql = Fsql;
//SELECT a."Id", a."Title", a."CreateTime" FROM "Topic1" a
var sql12 = Fsql.Insert<Topic1>().AppendData(new Topic1()).ToSql();
//INSERT INTO "Topic1"("Id", "Title", "CreateTime") VALUES(@Id0, @Title0, @CreateTime0)
var sql21 = Fsql.Select<Topic2>().ToSql(); var sql11 = Fsql.Select<Topic1>().ToSql();
//SELECT a."Id", a."Title", a."CreateTime" FROM "Topic2" a //SELECT a."Id", a."Title", a."CreateTime" FROM "Topic1" a
var sql22 = Fsql.Insert<Topic2>().AppendData(new Topic2()).ToSql(); var sql12 = Fsql.Insert<Topic1>().AppendData(new Topic1()).ToSql();
//INSERT INTO "Topic2"("Id", "Title", "CreateTime") VALUES(@Id0, @Title0, @CreateTime0) //INSERT INTO "Topic1"("Id", "Title", "CreateTime") VALUES(@Id0, @Title0, @CreateTime0)
using (var db = new Topic1Context()) { var sql21 = Fsql.Select<Topic2>().ToSql();
db.Topic1s.Add(new Topic1()); //SELECT a."Id", a."Title", a."CreateTime" FROM "Topic2" a
} var sql22 = Fsql.Insert<Topic2>().AppendData(new Topic2()).ToSql();
using (var db = new Topic2Context()) { //INSERT INTO "Topic2"("Id", "Title", "CreateTime") VALUES(@Id0, @Title0, @CreateTime0)
db.Topic2s.Add(new Topic2());
}
var sql13 = Fsql.Select<Topic1>().ToSql(); using (var db = new Topic1Context())
//SELECT a."topic1_id", a."Title", a."CreateTime" FROM "topic1_sss" a {
var sql14 = Fsql.Insert<Topic1>().AppendData(new Topic1()).ToSql(); db.Topic1s.Add(new Topic1());
//INSERT INTO "topic1_sss"("Title", "CreateTime") VALUES(@Title0, @CreateTime0) }
using (var db = new Topic2Context())
{
db.Topic2s.Add(new Topic2());
}
var sql23 = Fsql.Select<Topic2>().ToSql(); var sql13 = Fsql.Select<Topic1>().ToSql();
//SELECT a."topic2_id", a."Title", a."CreateTime" FROM "topic2_sss" a //SELECT a."topic1_id", a."Title", a."CreateTime" FROM "topic1_sss" a
var sql24 = Fsql.Insert<Topic2>().AppendData(new Topic2()).ToSql(); var sql14 = Fsql.Insert<Topic1>().AppendData(new Topic1()).ToSql();
//INSERT INTO "topic2_sss"("Title", "CreateTime") VALUES(@Title0, @CreateTime0) //INSERT INTO "topic1_sss"("Title", "CreateTime") VALUES(@Title0, @CreateTime0)
}
public IConfiguration Configuration { get; } var sql23 = Fsql.Select<Topic2>().ToSql();
public IFreeSql Fsql { get; } //SELECT a."topic2_id", a."Title", a."CreateTime" FROM "topic2_sss" a
var sql24 = Fsql.Insert<Topic2>().AppendData(new Topic2()).ToSql();
public void ConfigureServices(IServiceCollection services) //INSERT INTO "topic2_sss"("Title", "CreateTime") VALUES(@Title0, @CreateTime0)
{
services.AddSingleton<IFreeSql>(Fsql);
services.AddMvc();
} }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public IConfiguration Configuration { get; }
{ public IFreeSql Fsql { get; }
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseDeveloperExceptionPage(); public void ConfigureServices(IServiceCollection services)
app.UseMvc(); {
} services.AddControllersWithViews();
services.AddSingleton<IFreeSql>(Fsql);
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Console.InputEncoding = Encoding.GetEncoding("GB2312");
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(a => a.MapControllers());
}
} }
} }

View File

@ -1,9 +1,9 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Debug", "Default": "Debug",
"System": "Information", "System": "Information",
"Microsoft": "Information" "Microsoft": "Information"
}
} }
}
} }

View File

@ -1,8 +1,8 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Warning" "Default": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,17 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" /> </ItemGroup>
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" /> <ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
</ItemGroup> <ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -1,11 +1,14 @@
using Microsoft.EntityFrameworkCore; using FreeSql.Internal;
using Microsoft.EntityFrameworkCore;
using SqlSugar; using SqlSugar;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Common;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,285 +17,654 @@ namespace orm_vs
{ {
class Program class Program
{ {
static IFreeSql fsql = new FreeSql.FreeSqlBuilder() static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=20") //.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=20")
//.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=20") .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=20")
.UseAutoSyncStructure(false) //.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=20")
.UseNoneCommandParameter(true) .UseAutoSyncStructure(false)
//.UseConfigEntityFromDbFirst(true) .UseNoneCommandParameter(true)
.Build(); //.UseConfigEntityFromDbFirst(true)
.Build();
static SqlSugarClient sugar { static SqlSugarClient sugar
get => new SqlSugarClient(new ConnectionConfig() { {
//不欺负让连接池100个最小 get => new SqlSugarClient(new ConnectionConfig()
ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=20;Max Pool Size=20", {
DbType = DbType.SqlServer, //ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=20;Max Pool Size=20",
//ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20", //DbType = DbType.SqlServer,
//DbType = DbType.MySql, ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20",
IsAutoCloseConnection = true, DbType = DbType.MySql,
InitKeyType = InitKeyType.Attribute //ConnectionString = "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=21",
}); //DbType = DbType.PostgreSQL,
} IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
});
}
class SongContext : DbContext { class SongContext : DbContext
public DbSet<Song> Songs { get; set; } {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { public DbSet<Song> Songs { get; set; }
optionsBuilder.UseSqlServer(@"Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=21;Max Pool Size=21"); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21"); {
} //optionsBuilder.UseSqlServer(@"Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=21;Max Pool Size=21");
} optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21");
//optionsBuilder.UseNpgsql("Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=21");
}
static void Main(string[] args) { protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
fsql.CodeFirst.SyncStructure(typeof(Song), typeof(Song_tag), typeof(Tag)); modelBuilder.Entity<Song>()
//sugar.CodeFirst.InitTables(typeof(Song), typeof(Song_tag), typeof(Tag)); .Property(a => a.create_time)
//sugar创建表失败SqlSugar.SqlSugarException: Sequence contains no elements .HasConversion(a => int.Parse(a.ToString()), a => new DateTime(a));
}
}
//测试前清空数据 static void Main(string[] args)
fsql.Delete<Song>().Where(a => a.Id > 0).ExecuteAffrows(); {
sugar.Deleteable<Song>().Where(a => a.Id > 0).ExecuteCommand(); //fsql.CodeFirst.SyncStructure(typeof(Song), typeof(Song_tag), typeof(Tag));
fsql.Ado.ExecuteNonQuery("delete from efcore_song"); //sugar.CodeFirst.InitTables(typeof(Song), typeof(Song_tag), typeof(Tag));
//sugar创建表失败SqlSugar.SqlSugarException: Sequence contains no elements
fsql.CodeFirst.SyncStructure(typeof(Song), "freesql_song");
fsql.CodeFirst.SyncStructure(typeof(Song), "sugar_song");
fsql.CodeFirst.SyncStructure(typeof(Song), "efcore_song");
var sb = new StringBuilder(); fsql.CodeFirst.SyncStructure(typeof(Song_tag), "freesql_song_tag");
Console.WriteLine("插入性能:"); fsql.CodeFirst.SyncStructure(typeof(Song_tag), "sugar_song_tag");
Insert(sb, 1000, 1); fsql.CodeFirst.SyncStructure(typeof(Song_tag), "efcore_song_tag");
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1000, 10);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 1000); fsql.CodeFirst.SyncStructure(typeof(Tag), "freesql_tag");
Console.Write(sb.ToString()); fsql.CodeFirst.SyncStructure(typeof(Tag), "sugar_tag");
sb.Clear(); fsql.CodeFirst.SyncStructure(typeof(Tag), "efcore_tag");
Insert(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("查询性能:"); var sb = new StringBuilder();
Select(sb, 1000, 1); var time = new Stopwatch();
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1000, 10);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 1000); var sql222 = fsql.Select<Song>().Where(a => DateTime.Now.Subtract(a.create_time.Value).TotalHours > 0).ToSql();
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("更新:"); #region ET test
Update(sb, 1000, 1); ////var t31 = fsql.Select<xxx>().ToList();
Console.Write(sb.ToString()); //fsql.Select<Song>().First();
sb.Clear();
Update(sb, 1000, 10);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 1000); //time.Restart();
Console.Write(sb.ToString()); //var t3 = fsql.Select<Song>().ToList();
sb.Clear(); //time.Stop();
Update(sb, 1, 10000); //sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3.Count}; ORM: FreeSql*");
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("测试结束,按任意键退出..."); //time.Restart();
Console.ReadKey(); //var adoarr1 = fsql.Ado.ExecuteArray("select * from freesql_song");
} //time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteArray Entity Counts: {adoarr1.Length}; ORM: FreeSql ExecuteArray*");
static void Select(StringBuilder sb, int forTime, int size) { //time.Restart();
Stopwatch sw = new Stopwatch(); //var adolist1 = new List<Song>();
sw.Restart(); //fsql.Ado.ExecuteReader(dr =>
for (var a = 0; a < forTime; a++) //{
fsql.Select<Song>().Limit(size).ToList(); // var xim = new Song();
sw.Stop(); // dr.GetValue(0)?.GetType();
sb.AppendLine($"FreeSql Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); // dr.GetValue(1)?.GetType();
// dr.GetValue(2)?.GetType();
// dr.GetValue(3)?.GetType();
// dr.GetValue(4)?.GetType();
// adolist1.Add(xim);
//}, "select * from freesql_song");
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReader Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReader*");
sw.Restart();
for (var a = 0; a < forTime; a++)
sugar.Queryable<Song>().Take(size).ToList();
sw.Stop();
sb.AppendLine($"SqlSugar Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
sw.Restart(); //time.Restart();
for (var a = 0; a < forTime; a++) { //adolist1 = new List<Song>();
using (var db = new SongContext()) { //fsql.Ado.ExecuteReader(dr =>
db.Songs.Take(size).AsNoTracking().ToList(); //{
} // var xim = new Song();
} // var v1 = dr.GetValue(0);
sw.Stop(); // var locvalue = (object)v1;
sb.AppendLine($"EFCore Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); // if (locvalue == null || locvalue == DBNull.Value) xim.Id = default;
} // else
// {
// if (locvalue is int iv) xim.Id = iv;
// else
// {
// if (locvalue is string)
// {
static void Insert(StringBuilder sb, int forTime, int size) { // }
var songs = Enumerable.Range(0, size).Select(a => new Song { // }
Create_time = DateTime.Now, // }
Is_deleted = false, // v1 = dr.GetValue(1);
Title = $"Insert_{a}", // locvalue = (object)v1;
Url = $"Url_{a}" // if (locvalue == null || locvalue == DBNull.Value) xim.Create_time = default;
}); // else
// {
// if (locvalue is DateTime dt) xim.Create_time = dt;
// else
// {
// if (locvalue is string)
// {
//预热 // }
fsql.Insert(songs.First()).ExecuteAffrows(); // }
sugar.Insertable(songs.First()).ExecuteCommand(); // }
using (var db = new SongContext()) { // v1 = dr.GetValue(2);
//db.Configuration.AutoDetectChangesEnabled = false; // locvalue = (object)v1;
db.Songs.AddRange(songs.First()); // if (locvalue == null || locvalue == DBNull.Value) xim.Is_deleted = default;
db.SaveChanges(); // else
} // {
Stopwatch sw = new Stopwatch(); // if (locvalue is bool bl) xim.Is_deleted = bl;
// else
// {
// if (locvalue is string)
// {
sw.Restart(); // }
for (var a = 0; a < forTime; a++) { // }
fsql.Insert(songs).ExecuteAffrows(); // }
//using (var db = new FreeSongContext()) { // v1 = dr.GetValue(3);
// //db.Configuration.AutoDetectChangesEnabled = false; // locvalue = (object)v1;
// db.Songs.AddRange(songs.ToArray()); // if (locvalue == null || locvalue == DBNull.Value) xim.Title = default;
// db.SaveChanges(); // else
//} // {
} // if (locvalue is string str) xim.Title = str;
sw.Stop(); // else
sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); // {
// if (locvalue is string)
// {
sw.Restart(); // }
Exception sugarEx = null; // }
try { // }
for (var a = 0; a < forTime; a++) // v1 = dr.GetValue(4);
sugar.Insertable(songs.ToArray()).ExecuteCommand(); // locvalue = (object)v1;
} catch (Exception ex) { // if (locvalue == null || locvalue == DBNull.Value) xim.Url = default;
sugarEx = ex; // else
} // {
sw.Stop(); // if (locvalue is string str) xim.Url = str;
sb.AppendLine($"SqlSugar Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : "")); // else
// {
// if (locvalue is string)
// {
sw.Restart(); // }
for (var a = 0; a < forTime; a++) { // }
// }
// adolist1.Add(xim);
//}, "select * from freesql_song");
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReaderObject Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReaderObject*");
using (var db = new SongContext()) { ////var type = typeof(Song);
//db.Configuration.AutoDetectChangesEnabled = false; ////var myfuncParam1 = Expression.Parameter(typeof(object[]), "values");
db.Songs.AddRange(songs.ToArray()); ////var retExp = Expression.Variable(type, "ret");
db.SaveChanges(); ////var objExp = Expression.Variable(typeof(object), "obj");
} ////var returnTarget = Expression.Label(type);
} ////var myfuncBody = Expression.Block(
sw.Stop(); //// new[] { retExp, objExp },
sb.AppendLine($"EFCore Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); //// Expression.Assign(retExp, type.InternalNewExpression()),
} //// Expression.Assign(objExp, Expression.ArrayIndex(myfuncParam1, Expression.Constant(0))),
//// Utils.GetConvertExpression(type.GetProperty("Id").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Id")), Expression.Convert(objExp, type.GetProperty("Id").PropertyType)),
//// Expression.Assign(objExp, Expression.ArrayIndex(myfuncParam1, Expression.Constant(1))),
//// Utils.GetConvertExpression(type.GetProperty("Create_time").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Create_time")), Expression.Convert(objExp, type.GetProperty("Create_time").PropertyType)),
//// Expression.Assign(objExp, Expression.ArrayIndex(myfuncParam1, Expression.Constant(2))),
//// Utils.GetConvertExpression(type.GetProperty("Is_deleted").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Is_deleted")), Expression.Convert(objExp, type.GetProperty("Is_deleted").PropertyType)),
//// Expression.Assign(objExp, Expression.ArrayIndex(myfuncParam1, Expression.Constant(3))),
//// Utils.GetConvertExpression(type.GetProperty("Title").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Title")), Expression.Convert(objExp, type.GetProperty("Title").PropertyType)),
//// Expression.Assign(objExp, Expression.ArrayIndex(myfuncParam1, Expression.Constant(4))),
//// Utils.GetConvertExpression(type.GetProperty("Url").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Url")), Expression.Convert(objExp, type.GetProperty("Url").PropertyType)),
//// Expression.Return(returnTarget, retExp),
//// Expression.Label(returnTarget, Expression.Default(type))
////);
////var myfunc = Expression.Lambda<Func<object[], Song>>(myfuncBody, myfuncParam1).Compile();
////time.Restart();
////adolist1 = new List<Song>();
////fsql.Ado.ExecuteReader(dr =>
////{
//// var values = new object[dr.FieldCount];
//// dr.GetValues(values);
//// var xim = myfunc(values);
//// adolist1.Add(xim);
////}, "select * from freesql_song");
////time.Stop();
////sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReaderMyFunc Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReaderMyFunc*");
static void Update(StringBuilder sb, int forTime, int size) {
Stopwatch sw = new Stopwatch();
var songs = fsql.Select<Song>().Limit(size).ToList(); ////var methodDrgv = typeof(DbDataReader).GetMethod("GetValue");
sw.Restart(); ////var myfunc2Param1 = Expression.Parameter(typeof(DbDataReader), "dr");
for (var a = 0; a < forTime; a++) { ////var myfunc2Body = Expression.Block(
fsql.Update<Song>().SetSource(songs).ExecuteAffrows(); //// new[] { retExp, objExp },
} //// Expression.Assign(retExp, type.InternalNewExpression()),
sw.Stop(); //// Expression.Assign(objExp, Expression.Call(myfunc2Param1, methodDrgv, Expression.Constant(0))),
sb.AppendLine($"FreeSql Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); //// Utils.GetConvertExpression(type.GetProperty("Id").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Id")), Expression.Convert(objExp, type.GetProperty("Id").PropertyType)),
//// Expression.Assign(objExp, Expression.Call(myfunc2Param1, methodDrgv, Expression.Constant(1))),
//// Utils.GetConvertExpression(type.GetProperty("Create_time").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Create_time")), Expression.Convert(objExp, type.GetProperty("Create_time").PropertyType)),
//// Expression.Assign(objExp, Expression.Call(myfunc2Param1, methodDrgv, Expression.Constant(2))),
//// Utils.GetConvertExpression(type.GetProperty("Is_deleted").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Is_deleted")), Expression.Convert(objExp, type.GetProperty("Is_deleted").PropertyType)),
//// Expression.Assign(objExp, Expression.Call(myfunc2Param1, methodDrgv, Expression.Constant(3))),
//// Utils.GetConvertExpression(type.GetProperty("Title").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Title")), Expression.Convert(objExp, type.GetProperty("Title").PropertyType)),
//// Expression.Assign(objExp, Expression.Call(myfunc2Param1, methodDrgv, Expression.Constant(4))),
//// Utils.GetConvertExpression(type.GetProperty("Url").PropertyType, objExp),
//// Expression.Assign(Expression.MakeMemberAccess(retExp, type.GetProperty("Url")), Expression.Convert(objExp, type.GetProperty("Url").PropertyType)),
//// Expression.Return(returnTarget, retExp),
//// Expression.Label(returnTarget, Expression.Default(type))
////);
////var myfunc2 = Expression.Lambda<Func<DbDataReader, Song>>(myfunc2Body, myfunc2Param1).Compile();
////time.Restart();
////adolist1 = new List<Song>();
////fsql.Ado.ExecuteReader(dr =>
////{
//// var xim = myfunc2(dr);
//// adolist1.Add(xim);
////}, "select * from freesql_song");
////time.Stop();
////sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReaderMyFunc22 Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReaderMyFunc22*");
songs = sugar.Queryable<Song>().Take(size).ToList();
sw.Restart();
Exception sugarEx = null;
try {
for (var a = 0; a < forTime; a++)
sugar.Updateable(songs).ExecuteCommand();
} catch (Exception ex) {
sugarEx = ex;
}
sw.Stop();
sb.AppendLine($"SqlSugar Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : ""));
using (var db = new SongContext()) { //time.Restart();
songs = db.Songs.Take(size).AsNoTracking().ToList(); //adolist1 = new List<Song>();
} //fsql.Ado.ExecuteReader(dr =>
sw.Restart(); //{
for (var a = 0; a < forTime; a++) { // var xim = new Song();
// dr.GetFieldValue<int>(0);
// dr.GetFieldValue<DateTime>(1);
// dr.GetFieldValue<bool>(2);
// dr.GetFieldValue<string>(3);
// dr.GetFieldValue<string>(4);
// adolist1.Add(xim);
//}, "select * from freesql_song");
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReader0000 Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReader0000*");
using (var db = new SongContext()) { //time.Restart();
//db.Configuration.AutoDetectChangesEnabled = false; //adolist1 = new List<Song>();
db.Songs.UpdateRange(songs.ToArray()); //fsql.Ado.ExecuteReader(dr =>
db.SaveChanges(); //{
} // var xim = new Song();
} // Utils.GetDataReaderValue(typeof(int), dr.GetValue(0));
sw.Stop(); // Utils.GetDataReaderValue(typeof(DateTime), dr.GetValue(1));
sb.AppendLine($"EFCore Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); // Utils.GetDataReaderValue(typeof(bool), dr.GetValue(2));
} // Utils.GetDataReaderValue(typeof(string), dr.GetValue(3));
} // Utils.GetDataReaderValue(typeof(string), dr.GetValue(4));
// adolist1.Add(xim);
//}, "select * from freesql_song");
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReader1111 Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReader1111*");
[FreeSql.DataAnnotations.Table(Name = "freesql_song")]
[SugarTable("sugar_song")]
[Table("efcore_song")]
public class Song {
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime? Create_time { get; set; }
public bool? Is_deleted { get; set; }
public string Title { get; set; }
public string Url { get; set; }
[SugarColumn(IsIgnore = true)] ////time.Restart();
[NotMapped] ////adolist1 = new List<Song>();
public virtual ICollection<Tag> Tags { get; set; } ////fsql.Ado.ExecuteReader(dr =>
} ////{
[FreeSql.DataAnnotations.Table(Name = "freesql_song_tag")] //// var xim = new Song();
[SugarTable("sugar_song_tag")] //// Utils.GetConvertValue(typeof(int), dr.GetValue(0));
[Table("efcore_song_tag")] //// Utils.GetConvertValue(typeof(DateTime), dr.GetValue(1));
public class Song_tag { //// Utils.GetConvertValue(typeof(bool), dr.GetValue(2));
public int Song_id { get; set; } //// Utils.GetConvertValue(typeof(string), dr.GetValue(3));
[SugarColumn(IsIgnore = true)] //// Utils.GetConvertValue(typeof(string), dr.GetValue(4));
[NotMapped] //// adolist1.Add(xim);
public virtual Song Song { get; set; } ////}, "select * from freesql_song");
////time.Stop();
////sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReader11112222 Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReader11112222*");
public int Tag_id { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Tag { get; set; }
}
[FreeSql.DataAnnotations.Table(Name = "freesql_tag")]
[SugarTable("sugar_tag")]
[Table("efcore_tag")]
public class Tag {
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? Parent_id { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Parent { get; set; }
public decimal? Ddd { get; set; } //time.Restart();
public string Name { get; set; } //adolist1 = new List<Song>();
//fsql.Ado.ExecuteReader(dr =>
//{
// var values = new object[dr.FieldCount];
// dr.GetValues(values);
[SugarColumn(IsIgnore = true)] // var xim = new Song();
[NotMapped] // xim.Id = (int)Utils.GetDataReaderValue(typeof(int), values[0]);
public virtual ICollection<Song> Songs { get; set; } // xim.Create_time = (DateTime)Utils.GetDataReaderValue(typeof(DateTime), values[1]);
[SugarColumn(IsIgnore = true)] // xim.Is_deleted = (bool)Utils.GetDataReaderValue(typeof(bool), values[2]);
[NotMapped] // xim.Title = (string)Utils.GetDataReaderValue(typeof(string), values[3]);
public virtual ICollection<Tag> Tags { get; set; } // xim.Url = (string)Utils.GetDataReaderValue(typeof(string), values[4]);
} // adolist1.Add(xim);
//}, "select * from freesql_song");
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReader1111 Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReader1111*");
////time.Restart();
////adolist1 = new List<Song>();
////fsql.Ado.ExecuteReader(dr =>
////{
//// var values = new object[dr.FieldCount];
//// dr.GetValues(values);
//// var xim = new Song();
//// xim.Id = (int)Utils.GetConvertValue(typeof(int), values[0]);
//// xim.Create_time = (DateTime)Utils.GetConvertValue(typeof(DateTime), values[1]);
//// xim.Is_deleted = (bool)Utils.GetConvertValue(typeof(bool), values[2]);
//// xim.Title = (string)Utils.GetConvertValue(typeof(string), values[3]);
//// xim.Url = (string)Utils.GetConvertValue(typeof(string), values[4]);
//// adolist1.Add(xim);
////}, "select * from freesql_song");
////time.Stop();
////sb.AppendLine($"Elapsed: {time.Elapsed}; ExecuteReader11112222 Entity Counts: {adolist1.Count}; ORM: FreeSql ExecuteReader11112222*");
//time.Restart();
//List<Song> dplist1 = null;
//using (var conn = fsql.Ado.MasterPool.Get())
//{
// dplist1 = Dapper.SqlMapper.Query<Song>(conn.Value, "select * from freesql_song").ToList();
//}
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1.Count}; ORM: Dapper");
//time.Restart();
//t3 = fsql.Select<Song>().ToList();
//time.Stop();
//sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3.Count}; ORM: FreeSql*");
//Console.WriteLine(sb.ToString());
//Console.ReadKey();
#endregion
var testlist1 = fsql.Select<Song>().OrderBy(a => a.id).ToList();
var testlist2 = new List<Song>();
fsql.Select<Song>().OrderBy(a => a.id).ToChunk(2, fetch =>
{
testlist2.AddRange(fetch.Object);
});
var testlist22 = new List<object>();
fsql.Select<Song, Song_tag>().LeftJoin((a, b) => a.id == b.song_id).ToChunk((a, b) => new { a.title, a.create_time, b.tag_id }, 2, fetch =>
{
testlist22.AddRange(fetch.Object);
});
//sugar.Aop.OnLogExecuted = (s, e) =>
//{
// Trace.WriteLine(s);
//};
//测试前清空数据
fsql.Delete<Song>().Where(a => a.id > 0).ExecuteAffrows();
sugar.Deleteable<Song>().Where(a => a.id > 0).ExecuteCommand();
fsql.Ado.ExecuteNonQuery("delete from efcore_song");
Console.WriteLine("插入性能:");
Insert(sb, 100, 1);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 100, 10);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 1000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("查询性能:");
Select(sb, 100, 1);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 100, 10);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 1000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("更新:");
Update(sb, 100, 1);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 100, 10);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 1000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("测试结束,按任意键退出...");
Console.ReadKey();
}
static void Select(StringBuilder sb, int forTime, int size)
{
Stopwatch sw = new Stopwatch();
sw.Restart();
for (var a = 0; a < forTime; a++)
fsql.Select<Song>().Limit(size).ToList();
sw.Stop();
sb.AppendLine($"FreeSql Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
sw.Restart();
for (var a = 0; a < forTime; a++)
sugar.Queryable<Song>().Take(size).ToList();
sw.Stop();
sb.AppendLine($"SqlSugar Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
sw.Restart();
for (var a = 0; a < forTime; a++)
{
using (var db = new SongContext())
{
//db.Songs.Take(size).AsNoTracking().ToList();
}
}
sw.Stop();
sb.AppendLine($"EFCore Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms .net5.0无效");
sw.Restart();
using (var conn = fsql.Ado.MasterPool.Get())
{
for (var a = 0; a < forTime; a++)
Dapper.SqlMapper.Query<Song>(conn.Value, $"select top {size} * from freesql_song").ToList();
}
sw.Stop();
sb.AppendLine($"Dapper Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n");
}
static void Insert(StringBuilder sb, int forTime, int size)
{
var songs = Enumerable.Range(0, size).Select(a => new Song
{
create_time = DateTime.Now,
is_deleted = false,
title = $"Insert_{a}",
url = $"Url_{a}"
});
//预热
fsql.Insert(songs.First()).ExecuteAffrows();
sugar.Insertable(songs.First()).ExecuteCommand();
using (var db = new SongContext())
{
//db.Configuration.AutoDetectChangesEnabled = false;
//db.Songs.AddRange(songs.First());
//db.SaveChanges(); //.net5.0 throw Microsoft.EntityFrameworkCore.DbUpdateException
}
Stopwatch sw = new Stopwatch();
sw.Restart();
for (var a = 0; a < forTime; a++)
{
fsql.Insert(songs).ExecuteAffrows();
//using (var db = new FreeSongContext()) {
// //db.Configuration.AutoDetectChangesEnabled = false;
// db.Songs.AddRange(songs.ToArray());
// db.SaveChanges();
//}
}
sw.Stop();
sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
sw.Restart();
Exception sugarEx = null;
try
{
for (var a = 0; a < forTime; a++)
sugar.Insertable(songs.ToArray()).ExecuteCommand();
}
catch (Exception ex)
{
sugarEx = ex;
}
sw.Stop();
sb.AppendLine($"SqlSugar Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : ""));
sw.Restart();
for (var a = 0; a < forTime; a++)
{
using (var db = new SongContext())
{
//db.Configuration.AutoDetectChangesEnabled = false;
//db.Songs.AddRange(songs.ToArray());
//db.SaveChanges(); //.net5.0 throw Microsoft.EntityFrameworkCore.DbUpdateException
}
}
sw.Stop();
sb.AppendLine($"EFCore Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms .net5.0无效\r\n");
}
static void Update(StringBuilder sb, int forTime, int size)
{
Stopwatch sw = new Stopwatch();
var songs = fsql.Select<Song>().Limit(size).ToList();
sw.Restart();
for (var a = 0; a < forTime; a++)
{
fsql.Update<Song>().SetSource(songs).ExecuteAffrows();
}
sw.Stop();
sb.AppendLine($"FreeSql Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
songs = sugar.Queryable<Song>().Take(size).ToList();
sw.Restart();
Exception sugarEx = null;
try
{
for (var a = 0; a < forTime; a++)
sugar.Updateable(songs).ExecuteCommand();
}
catch (Exception ex)
{
sugarEx = ex;
}
sw.Stop();
sb.AppendLine($"SqlSugar Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : ""));
using (var db = new SongContext())
{
songs = db.Songs.Take(size).AsNoTracking().ToList();
}
sw.Restart();
for (var a = 0; a < forTime; a++)
{
using (var db = new SongContext())
{
//db.Configuration.AutoDetectChangesEnabled = false;
//db.Songs.UpdateRange(songs.ToArray());
//db.SaveChanges();
}
}
sw.Stop();
sb.AppendLine($"EFCore Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms .net5.0无效\r\n");
}
}
[FreeSql.DataAnnotations.Table(Name = "freesql_song")]
[SugarTable("sugar_song")]
[Table("efcore_song")]
public class Song
{
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public DateTime? create_time { get; set; }
public bool? is_deleted { get; set; }
public string title { get; set; }
public string url { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual ICollection<Tag> Tags { get; set; }
}
[FreeSql.DataAnnotations.Table(Name = "freesql_song_tag")]
[SugarTable("sugar_song_tag")]
[Table("efcore_song_tag")]
public class Song_tag
{
public int song_id { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Song Song { get; set; }
public int tag_id { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Tag { get; set; }
}
[FreeSql.DataAnnotations.Table(Name = "freesql_tag")]
[SugarTable("sugar_tag")]
[Table("efcore_tag")]
public class Tag
{
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public int? parent_id { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual Tag Parent { get; set; }
public decimal? ddd { get; set; }
public string name { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual ICollection<Song> Songs { get; set; }
[SugarColumn(IsIgnore = true)]
[NotMapped]
public virtual ICollection<Tag> Tags { get; set; }
}
} }

View File

@ -1,24 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.3" /> <PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
<PackageReference Include="sqlSugarCore" Version="4.9.9.3" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
</ItemGroup> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.0" />
<PackageReference Include="sqlSugarCore" Version="5.0.1.2" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" /> <ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
</ItemGroup> <ProjectReference Include="..\..\Providers\FreeSql.Provider.MySql\FreeSql.Provider.MySql.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.PostgreSQL\FreeSql.Provider.PostgreSQL.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.SqlServer\FreeSql.Provider.SqlServer.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.EntityFrameworkCore"> <Reference Include="Microsoft.EntityFrameworkCore">
<HintPath>..\..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.entityframeworkcore\2.2.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll</HintPath> <HintPath>..\..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.entityframeworkcore\2.2.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,263 @@
//using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace orm_vs
{
class Program
{
static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=20")
//.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=20")
.UseAutoSyncStructure(false)
.UseNoneCommandParameter(true)
//.UseConfigEntityFromDbFirst(true)
.Build();
//static SqlSugarClient sugar
//{
// get => new SqlSugarClient(new ConnectionConfig()
// {
// //不欺负让连接池100个最小
// //ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Min Pool Size=20;Max Pool Size=20",
// //DbType = DbType.SqlServer,
// ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20",
// DbType = DbType.MySql,
// IsAutoCloseConnection = true,
// InitKeyType = InitKeyType.Attribute
// });
//}
static void Main(string[] args)
{
fsql.CodeFirst.SyncStructure(typeof(Song), typeof(Song_tag), typeof(Tag));
//sugar.CodeFirst.InitTables(typeof(Song), typeof(Song_tag), typeof(Tag));
//sugar创建表失败SqlSugar.SqlSugarException: Sequence contains no elements
//sugar.Aop.OnLogExecuted = (s, e) =>
//{
// Trace.WriteLine(s);
//};
//测试前清空数据
fsql.Delete<Song>().Where(a => a.Id > 0).ExecuteAffrows();
//sugar.Deleteable<Song>().Where(a => a.Id > 0).ExecuteCommand();
fsql.Ado.ExecuteNonQuery("delete from efcore_song");
var sb = new StringBuilder();
Console.WriteLine("插入性能:");
Insert(sb, 1000, 1);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1000, 10);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 1000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Insert(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("查询性能:");
Select(sb, 1000, 1);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1000, 10);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 1000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Select(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("更新:");
Update(sb, 1000, 1);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1000, 10);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 1000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 10000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 50000);
Console.Write(sb.ToString());
sb.Clear();
Update(sb, 1, 100000);
Console.Write(sb.ToString());
sb.Clear();
Console.WriteLine("测试结束,按任意键退出...");
Console.ReadKey();
}
static void Select(StringBuilder sb, int forTime, int size)
{
Stopwatch sw = new Stopwatch();
sw.Restart();
for (var a = 0; a < forTime; a++)
fsql.Select<Song>().Limit(size).ToList();
sw.Stop();
sb.AppendLine($"FreeSql Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
//sw.Restart();
//for (var a = 0; a < forTime; a++)
// sugar.Queryable<Song>().Take(size).ToList();
//sw.Stop();
//sb.AppendLine($"SqlSugar Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
sw.Restart();
using (var conn = fsql.Ado.MasterPool.Get())
{
for (var a = 0; a < forTime; a++)
Dapper.SqlMapper.Query<Song>(conn.Value, $"select top {size} * from freesql_song").ToList();
}
sw.Stop();
sb.AppendLine($"Dapper Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
}
static void Insert(StringBuilder sb, int forTime, int size)
{
var songs = Enumerable.Range(0, size).Select(a => new Song
{
Create_time = DateTime.Now,
Is_deleted = false,
Title = $"Insert_{a}",
Url = $"Url_{a}"
});
//预热
fsql.Insert(songs.First()).ExecuteAffrows();
//sugar.Insertable(songs.First()).ExecuteCommand();
Stopwatch sw = new Stopwatch();
sw.Restart();
for (var a = 0; a < forTime; a++)
{
fsql.Insert(songs).ExecuteAffrows();
//using (var db = new FreeSongContext()) {
// //db.Configuration.AutoDetectChangesEnabled = false;
// db.Songs.AddRange(songs.ToArray());
// db.SaveChanges();
//}
}
sw.Stop();
sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
//sw.Restart();
//Exception sugarEx = null;
//try
//{
// for (var a = 0; a < forTime; a++)
// sugar.Insertable(songs.ToArray()).ExecuteCommand();
//}
//catch (Exception ex)
//{
// sugarEx = ex;
//}
//sw.Stop();
//sb.AppendLine($"SqlSugar Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : ""));
}
static void Update(StringBuilder sb, int forTime, int size)
{
Stopwatch sw = new Stopwatch();
var songs = fsql.Select<Song>().Limit(size).ToList();
sw.Restart();
for (var a = 0; a < forTime; a++)
{
fsql.Update<Song>().SetSource(songs).ExecuteAffrows();
}
sw.Stop();
sb.AppendLine($"FreeSql Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
//songs = sugar.Queryable<Song>().Take(size).ToList();
//sw.Restart();
//Exception sugarEx = null;
//try
//{
// for (var a = 0; a < forTime; a++)
// sugar.Updateable(songs).ExecuteCommand();
//}
//catch (Exception ex)
//{
// sugarEx = ex;
//}
//sw.Stop();
//sb.AppendLine($"SqlSugar Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : ""));
}
}
[FreeSql.DataAnnotations.Table(Name = "freesql_song")]
//[SugarTable("sugar_song")]
public class Song
{
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
//[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
public DateTime? Create_time { get; set; }
public bool? Is_deleted { get; set; }
public string Title { get; set; }
public string Url { get; set; }
//[SugarColumn(IsIgnore = true)]
public virtual ICollection<Tag> Tags { get; set; }
}
[FreeSql.DataAnnotations.Table(Name = "freesql_song_tag")]
//[SugarTable("sugar_song_tag")]
public class Song_tag
{
public int Song_id { get; set; }
//[SugarColumn(IsIgnore = true)]
public virtual Song Song { get; set; }
public int Tag_id { get; set; }
//[SugarColumn(IsIgnore = true)]
public virtual Tag Tag { get; set; }
}
[FreeSql.DataAnnotations.Table(Name = "freesql_tag")]
//[SugarTable("sugar_tag")]
public class Tag
{
[FreeSql.DataAnnotations.Column(IsIdentity = true)]
//[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
public int? Parent_id { get; set; }
//[SugarColumn(IsIgnore = true)]
public virtual Tag Parent { get; set; }
public decimal? Ddd { get; set; }
public string Name { get; set; }
//[SugarColumn(IsIgnore = true)]
public virtual ICollection<Song> Songs { get; set; }
//[SugarColumn(IsIgnore = true)]
public virtual ICollection<Tag> Tags { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("orm_vs_net40")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("orm_vs_net40")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("1674bce3-eeb4-4003-a2a7-06f51efaea23")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1674BCE3-EEB4-4003-A2A7-06F51EFAEA23}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>orm_vs_net40</RootNamespace>
<AssemblyName>orm_vs_net40</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj">
<Project>{af9c50ec-6eb6-494b-9b3b-7edba6fd0ebb}</Project>
<Name>FreeSql</Name>
</ProjectReference>
<ProjectReference Include="..\..\Providers\FreeSql.Provider.MySql\FreeSql.Provider.MySql.csproj">
<Project>{28c6a39c-7ae7-4210-b7b0-0970216637a8}</Project>
<Name>FreeSql.Provider.MySql</Name>
</ProjectReference>
<ProjectReference Include="..\..\Providers\FreeSql.Provider.SqlServer\FreeSql.Provider.SqlServer.csproj">
<Project>{b61aac9e-59e9-4f47-bbe3-97ac24112efe}</Project>
<Name>FreeSql.Provider.SqlServer</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper">
<Version>1.50.2</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="..\..\.editorconfig">
<Link>.editorconfig</Link>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,104 @@
using FreeSql;
using Microsoft.AspNetCore.Mvc;
using repository_01;
using restful.Entitys;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace restful.Controllers
{
public class SongRepository : GuidRepository<Song>
{
public SongRepository(IFreeSql fsql) : base(fsql)
{
}
}
[Route("restapi/[controller]")]
public class SongsController : Controller
{
BaseRepository<Song, int> _songRepository;
public class xxxx
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
public SongsController(IFreeSql fsql,
BaseRepository<Song> repos3, BaseRepository<Song, int> repos4,
IBaseRepository<Song> repos31, IBaseRepository<Song, int> repos41,
SongRepository reposSong,
IBaseRepository<TestSoftDelete> reposTest
)
{
Console.Write(reposTest.Select.ToSql());
_songRepository = repos4;
//test code
var curd1 = fsql.GetRepository<Song, int>();
var curd2 = fsql.GetRepository<Song, string>();
var curd3 = fsql.GetRepository<Song, Guid>();
var curd4 = fsql.GetGuidRepository<Song>();
Console.WriteLine(reposSong.Select.ToSql());
using (reposSong.DataFilter.DisableAll())
{
Console.WriteLine(reposSong.Select.ToSql());
}
}
[HttpGet]
public Task<List<Song>> GetItems([FromQuery] string key, [FromQuery] int page = 1, [FromQuery] int limit = 20)
{
return _songRepository.Select.WhereIf(!string.IsNullOrEmpty(key), a => a.Title.Contains(key)).Page(page, limit).ToListAsync();
}
[HttpGet("{id}")]
public Task<Song> GetItem([FromRoute] int id)
{
return _songRepository.FindAsync(id);
}
public class ModelSong
{
public string title { get; set; }
}
[HttpPost, ProducesResponseType(201)]
public Task<Song> Create([FromBody] ModelSong model)
{
return _songRepository.InsertAsync(new Song { Title = model.title });
}
[HttpPut("{id}")]
public Task Update([FromRoute] int id, [FromBody] ModelSong model)
{
return _songRepository.UpdateAsync(new Song { Id = id, Title = model.title });
}
[HttpPatch("{id}")]
async public Task<Song> UpdateDiy([FromRoute] int id, [FromForm] string title)
{
var up = _songRepository.UpdateDiy.Where(a => a.Id == id);
if (!string.IsNullOrEmpty(title)) up.Set(a => a.Title, title);
var ret = await up.ExecuteUpdatedAsync();
return ret.FirstOrDefault();
}
[HttpDelete("{id}"), ProducesResponseType(204)]
public Task Delete([FromRoute] int id)
{
return _songRepository.DeleteAsync(a => a.Id == id);
}
}
}

View File

@ -0,0 +1,13 @@
using FreeSql.DataAnnotations;
using repository_01;
namespace restful.Entitys
{
public class Song
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace repository_01
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:52751/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"repository_01": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:52752/"
}
}
}

View File

@ -0,0 +1,95 @@
using FreeSql;
using FreeSql.DataAnnotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Text;
namespace repository_01
{
/// <summary>
/// 用户密码信息
/// </summary>
public class Sys1UserLogOn
{
[Column(IsPrimary = true, Name = "Id")]
public Guid UserLogOnId { get; set; }
public virtual Sys1User User { get; set; }
}
public class Sys1User
{
[Column(IsPrimary = true, Name = "Id")]
public Guid UserId { get; set; }
public virtual Sys1UserLogOn UserLogOn { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)
{
Configuration = configuration;
Fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10")
.UseAutoSyncStructure(true)
.UseLazyLoading(true)
.UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
.Build();
var sysu = new Sys1User { };
Fsql.Insert<Sys1User>().AppendData(sysu).ExecuteAffrows();
Fsql.Insert<Sys1UserLogOn>().AppendData(new Sys1UserLogOn { UserLogOnId = sysu.UserId }).ExecuteAffrows();
var a = Fsql.Select<Sys1UserLogOn>().ToList();
var b = Fsql.Select<Sys1UserLogOn>().Any();
}
public IConfiguration Configuration { get; }
public static IFreeSql Fsql { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
//services.AddTransient(s => s.)
services.AddControllersWithViews();
services.AddSingleton<IFreeSql>(Fsql);
services.AddFreeRepository(filter =>
{
filter
//.Apply<Song>("test", a => a.Title == DateTime.Now.ToString() + System.Threading.Thread.CurrentThread.ManagedThreadId)
.Apply<ISoftDelete>("softdelete", a => a.IsDeleted == false);
}, this.GetType().Assembly);
}
public void Configure(IApplicationBuilder app)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Console.InputEncoding = Encoding.GetEncoding("GB2312");
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(a => a.MapControllers());
}
}
public interface ISoftDelete
{
bool IsDeleted { get; set; }
}
public class TestSoftDelete : ISoftDelete
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
public bool IsDeleted { get; set; }
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Warning",
"Microsoft": "Warning"
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.LazyLoading\FreeSql.Extensions.LazyLoading.csproj" />
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
</ItemGroup>
</Project>

BIN
Examples/restful/001.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
Examples/restful/002.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
Examples/restful/003.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
Examples/restful/004.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -4,56 +4,66 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace restful.Controllers { namespace restful.Controllers
{
[Route("restapi/[controller]")] [Route("restapi/[controller]")]
public class SongsController : Controller { public class SongsController : Controller
{
IFreeSql _fsql; IFreeSql _fsql;
public SongsController(IFreeSql fsql) { public SongsController(IFreeSql fsql)
_fsql = fsql; {
} _fsql = fsql;
}
[HttpGet] [HttpGet]
public Task<List<Song>> GetItems([FromQuery] string key, [FromQuery] int page = 1, [FromQuery] int limit = 20) { public Task<List<Song>> GetItems([FromQuery] string key, [FromQuery] int page = 1, [FromQuery] int limit = 20)
return _fsql.Select<Song>().WhereIf(!string.IsNullOrEmpty(key), a => a.Title.Contains(key)).Page(page, limit).ToListAsync(); {
} return _fsql.Select<Song>().WhereIf(!string.IsNullOrEmpty(key), a => a.Title.Contains(key)).Page(page, limit).ToListAsync();
}
[HttpGet("{id}")] [HttpGet("{id}")]
public Task<Song> GetItem([FromRoute] int id) { public Task<Song> GetItem([FromRoute] int id)
return _fsql.Select<Song>().Where(a => a.Id == id).ToOneAsync(); {
} return _fsql.Select<Song>().Where(a => a.Id == id).ToOneAsync();
}
public class ModelSong { public class ModelSong
public string title { get; set; } {
} public string title { get; set; }
}
[HttpPost, ProducesResponseType(201)] [HttpPost, ProducesResponseType(201)]
async public Task<Song> Create([FromBody] ModelSong model) { async public Task<Song> Create([FromBody] ModelSong model)
var ret = await _fsql.Insert<Song>().AppendData(new Song { Title = model.title }).ExecuteInsertedAsync(); {
return ret.FirstOrDefault(); var ret = await _fsql.Insert<Song>().AppendData(new Song { Title = model.title }).ExecuteInsertedAsync();
} return ret.FirstOrDefault();
}
[HttpPut("{id}")] [HttpPut("{id}")]
async public Task<Song> Update([FromRoute] int id, [FromBody] ModelSong model) { async public Task<Song> Update([FromRoute] int id, [FromBody] ModelSong model)
var ret = await _fsql.Update<Song>().SetSource(new Song { Id = id, Title = model.title }).ExecuteUpdatedAsync(); {
return ret.FirstOrDefault(); var ret = await _fsql.Update<Song>().SetSource(new Song { Id = id, Title = model.title }).ExecuteUpdatedAsync();
} return ret.FirstOrDefault();
}
[HttpPatch("{id}")] [HttpPatch("{id}")]
async public Task<Song> UpdateDiy([FromRoute] int id, [FromForm] string title) { async public Task<Song> UpdateDiy([FromRoute] int id, [FromForm] string title)
var up = _fsql.Update<Song>().Where(a => a.Id == id); {
if (!string.IsNullOrEmpty(title)) up.Set(a => a.Title, title); var up = _fsql.Update<Song>().Where(a => a.Id == id);
var ret = await up.ExecuteUpdatedAsync(); if (!string.IsNullOrEmpty(title)) up.Set(a => a.Title, title);
return ret.FirstOrDefault(); var ret = await up.ExecuteUpdatedAsync();
} return ret.FirstOrDefault();
}
[HttpDelete("{id}"), ProducesResponseType(204)] [HttpDelete("{id}"), ProducesResponseType(204)]
async public Task<Song> Delete([FromRoute] int id) { async public Task<Song> Delete([FromRoute] int id)
var ret = await _fsql.Delete<Song>().Where(a => a.Id == id).ExecuteDeletedAsync(); {
return ret.FirstOrDefault(); var ret = await _fsql.Delete<Song>().Where(a => a.Id == id).ExecuteDeletedAsync();
} return ret.FirstOrDefault();
} }
}
} }

View File

@ -1,10 +1,12 @@
using FreeSql.DataAnnotations; using FreeSql.DataAnnotations;
namespace restful.Entitys { namespace restful.Entitys
public class Song { {
public 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; }
} }
} }

View File

@ -1,27 +1,27 @@
{ {
"iisSettings": { "iisSettings": {
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:49778/", "applicationUrl": "http://localhost:49778/",
"sslPort": 0 "sslPort": 0
} }
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}, },
"FreeSql.RESTful.Demo": { "profiles": {
"commandName": "Project", "IIS Express": {
"launchBrowser": true, "commandName": "IISExpress",
"environmentVariables": { "launchBrowser": true,
"ASPNETCORE_ENVIRONMENT": "Development" "environmentVariables": {
}, "ASPNETCORE_ENVIRONMENT": "Development"
"applicationUrl": "http://localhost:49779/" }
},
"FreeSql.RESTful.Demo": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:49779/"
}
} }
}
} }

View File

@ -1,67 +1,57 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Swashbuckle.AspNetCore.Swagger;
using System; using System;
using System.Text; using System.Text;
namespace restful { namespace restful
public class Startup { {
public Startup(IConfiguration configuration, ILoggerFactory loggerFactory) { public class Startup
Configuration = configuration; {
public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)
{
Configuration = configuration;
Fsql = new FreeSql.FreeSqlBuilder() Fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=10") .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=10")
.UseAutoSyncStructure(true) .UseAutoSyncStructure(true)
.Build(); .Build();
Fsql.Aop.CurdAfter = (s, e) => { Fsql.Aop.CurdAfter += (s, e) =>
if (e.ElapsedMilliseconds > 200) { {
//记录日志 if (e.ElapsedMilliseconds > 200)
//发送短信给负责人 {
} //记录日志
}; //发送短信给负责人
}
};
//Fsql.Aop.Where = (s, e) => { //Fsql.Aop.Where = (s, e) => {
// if (e.Parameters[0]?.ToString() == "1") // if (e.Parameters[0]?.ToString() == "1")
// e.IsCancel = true; // e.IsCancel = true;
//}; //};
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
public IFreeSql Fsql { get; } public IFreeSql Fsql { get; }
public void ConfigureServices(IServiceCollection services) { public void ConfigureServices(IServiceCollection services)
services.AddSingleton<IFreeSql>(Fsql); {
services.AddSingleton<IFreeSql>(Fsql);
services.AddControllersWithViews();
}
services.AddMvc(); public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
services.AddSwaggerGen(options => { {
options.SwaggerDoc("v1", new Info { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Version = "v1", Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Title = "FreeSql.RESTful API" Console.InputEncoding = Encoding.GetEncoding("GB2312");
});
//options.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); app.UseDeveloperExceptionPage();
Console.OutputEncoding = Encoding.GetEncoding("GB2312"); app.UseRouting();
Console.InputEncoding = Encoding.GetEncoding("GB2312"); app.UseEndpoints(a => a.MapControllers());
}
loggerFactory.AddConsole(Configuration.GetSection("Logging")); }
loggerFactory.AddDebug();
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "FreeSql.RESTful API V1");
});
}
}
} }

View File

@ -1,9 +1,9 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Debug", "Default": "Debug",
"System": "Information", "System": "Information",
"Microsoft": "Information" "Microsoft": "Information"
}
} }
}
} }

View File

@ -1,8 +1,8 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Warning" "Default": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,19 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="4.0.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" /> <ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

View File

@ -0,0 +1,9 @@
<Application x:Class="xamarinForm.Wpf.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:xamarinForm.Wpf"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace xamarinForm.Wpf
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,13 @@
<wpf:FormsApplicationPage x:Class="xamarinForm.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:xamarinForm.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
xmlns:wpf="clr-namespace:Xamarin.Forms.Platform.WPF;assembly=Xamarin.Forms.Platform.WPF">
<Grid>
</Grid>
</wpf:FormsApplicationPage>

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Xamarin.Forms;
using Xamarin.Forms.Platform.WPF;
namespace xamarinForm.Wpf
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : FormsApplicationPage
{
public MainWindow()
{
InitializeComponent();
Forms.Init();
LoadApplication(new xamarinFormApp.App());
}
}
}

View File

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("xamarinForm.Wpf")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("xamarinForm.Wpf")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//例如,如果您在源文件中使用的是美国英语,
//使用的是美国英语,请将 <UICulture> 设置为 en-US。 然后取消
//对以下 NeutralResourceLanguage 特性的注释。 更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
//(未在页面中找到资源时使用,
//或应用程序资源字典中找到时使用)
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
//(未在页面中找到资源时使用,
//、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,70 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
//
// 对此文件的更改可能导致不正确的行为,如果
// 重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace xamarinForm.Wpf.Properties
{
/// <summary>
/// 强类型资源类,用于查找本地化字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// 返回此类使用的缓存 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("xamarinForm.Wpf.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,29 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace xamarinForm.Wpf.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{12EC22F5-2586-4AD3-AE03-319C2FAC0B06}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>xamarinForm.Wpf</RootNamespace>
<AssemblyName>xamarinForm.Wpf</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms.Platform.WPF">
<Version>4.8.0.1687</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\xamarinFormApp\xamarinFormApp.csproj">
<Project>{e4094717-ed1a-4174-8d86-a65d2ad1380c}</Project>
<Name>xamarinFormApp</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,19 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

View File

@ -0,0 +1,33 @@
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
namespace xamarinFormApp.Droid
{
[Activity(Label = "xamarinFormApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.xamarinformapp" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
<application android:label="xamarinFormApp.Android" android:theme="@style/MainTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

View File

@ -0,0 +1,30 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("xamarinFormApp.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("xamarinFormApp.Android")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// Add some common permissions, these can be removed if not needed
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]

View File

@ -0,0 +1,50 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable-hdpi/
icon.png
drawable-ldpi/
icon.png
drawable-mdpi/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called
"Resource" that contains the tokens for each one of the resources included. For example,
for the above Resources layout, this is what the Resource class would expose:
public class Resource {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:tabIndicatorColor="@android:color/white"
app:tabGravity="fill"
app:tabMode="fixed" />

View File

@ -0,0 +1,9 @@
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

Some files were not shown because too many files have changed in this diff Show More