From 9b5e34032c0e988fe8633eeb192b8df3d80f35ea Mon Sep 17 00:00:00 2001
From: 28810 <28810@YEXIANGQIN>
Date: Tue, 18 Dec 2018 20:09:52 +0800
Subject: [PATCH] =?UTF-8?q?pgsql/mysql/sqlserver=E9=80=82=E9=85=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitattributes | 63 ++
.gitignore | 246 +++++++
Docs/Generator.md | 92 +++
Docs/codefirst.md | 135 ++++
Docs/dbfirst.md | 112 +++
Docs/delete.md | 80 +++
Docs/insert.md | 77 +++
Docs/select.md | 48 ++
Docs/update.md | 127 ++++
FreeSql.Tests/Class1.cs | 126 ++++
.../Extensions/StringExtensionsTest.cs | 65 ++
FreeSql.Tests/FreeSql.Tests.csproj | 19 +
.../Generator/MySqlTemplateGeneratorTest.cs | 27 +
FreeSql.Tests/MySql/Curd/MySqlDeleteTest.cs | 72 ++
FreeSql.Tests/MySql/Curd/MySqlInsertTest.cs | 85 +++
FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs | 501 ++++++++++++++
FreeSql.Tests/MySql/Curd/MySqlUpdateTest.cs | 107 +++
FreeSql.Tests/MySql/MySqlAdo/MySqlAdoTest.cs | 54 ++
FreeSql.Tests/MySql/MySqlCodeFirstTest.cs | 350 ++++++++++
FreeSql.Tests/MySql/MySqlDbFirstTest.cs | 21 +
FreeSql.Tests/MySql/MySqlExpressionTest.cs | 82 +++
FreeSql.Tests/UnitTest1.cs | 150 ++++
FreeSql.Tests/g.cs | 15 +
FreeSql.sln | 76 +++
FreeSql/DataAnnotations/ColumnAttribute.cs | 32 +
FreeSql/DataAnnotations/TableAttribute.cs | 19 +
FreeSql/DatabaseModel/DBColumnInfo.cs | 52 ++
FreeSql/DatabaseModel/DBTableInfo.cs | 52 ++
FreeSql/DatabaseModel/DbForeignInfo.cs | 13 +
FreeSql/Extensions/NpgsqlTypesExtensions.cs | 39 ++
FreeSql/Extensions/StringExtensions.cs | 31 +
FreeSql/FreeSql.csproj | 21 +
FreeSql/FreeSqlBuilder.cs | 66 ++
FreeSql/FreeUtil.cs | 34 +
FreeSql/Generator/TemplateEngin.cs | 640 ++++++++++++++++++
FreeSql/Generator/TemplateGenerator.cs | 71 ++
FreeSql/Interface/Curd/IDelete.cs | 57 ++
FreeSql/Interface/Curd/IInsert.cs | 55 ++
FreeSql/Interface/Curd/ISelect/ISelect0.cs | 198 ++++++
FreeSql/Interface/Curd/ISelect/ISelect1.cs | 230 +++++++
FreeSql/Interface/Curd/ISelect/ISelect10.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect2.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect3.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect4.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect5.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect6.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect7.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect8.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelect9.cs | 26 +
FreeSql/Interface/Curd/ISelect/ISelectFrom.cs | 64 ++
FreeSql/Interface/Curd/IUpdate.cs | 100 +++
FreeSql/Interface/IAdo.cs | 138 ++++
FreeSql/Interface/ICache.cs | 118 ++++
FreeSql/Interface/ICodeFirst.cs | 43 ++
FreeSql/Interface/IDasql.cs | 100 +++
FreeSql/Interface/iDbFirst.cs | 70 ++
FreeSql/Internal/CommonExpression.cs | 256 +++++++
.../CommonProvider/AdoProvider/AdoProvider.cs | 267 ++++++++
.../AdoProvider/AdoProviderAsync.cs | 215 ++++++
.../AdoProvider/AdoProviderTransaction.cs | 152 +++++
.../AdoProvider/AdoProviderUtils.cs | 19 +
.../Internal/CommonProvider/CacheProvider.cs | 176 +++++
.../Internal/CommonProvider/DeleteProvider.cs | 52 ++
.../Internal/CommonProvider/InsertProvider.cs | 90 +++
.../SelectProvider/Select0Provider.cs | 263 +++++++
.../SelectProvider/Select10Provider.cs | 47 ++
.../SelectProvider/Select1Provider.cs | 114 ++++
.../SelectProvider/Select2Provider.cs | 39 ++
.../SelectProvider/Select3Provider.cs | 40 ++
.../SelectProvider/Select4Provider.cs | 41 ++
.../SelectProvider/Select5Provider.cs | 42 ++
.../SelectProvider/Select6Provider.cs | 43 ++
.../SelectProvider/Select7Provider.cs | 44 ++
.../SelectProvider/Select8Provider.cs | 45 ++
.../SelectProvider/Select9Provider.cs | 46 ++
.../Internal/CommonProvider/UpdateProvider.cs | 172 +++++
FreeSql/Internal/CommonUtils.cs | 119 ++++
FreeSql/Internal/Model/ColumnInfo.cs | 11 +
.../Internal/Model/ReadAnonymousTypeInfo.cs | 14 +
FreeSql/Internal/Model/SelectColumnInfo.cs | 10 +
FreeSql/Internal/Model/SelectTableInfo.cs | 9 +
FreeSql/Internal/Model/TableInfo.cs | 18 +
FreeSql/Internal/Utils.cs | 144 ++++
FreeSql/MySql/Curd/MySqlDelete.cs | 25 +
FreeSql/MySql/Curd/MySqlInsert.cs | 28 +
FreeSql/MySql/Curd/MySqlSelect.cs | 114 ++++
FreeSql/MySql/Curd/MySqlUpdate.cs | 49 ++
FreeSql/MySql/MySqlAdo/MySqlAdo.cs | 63 ++
FreeSql/MySql/MySqlAdo/MySqlAdoExtensions.cs | 36 +
FreeSql/MySql/MySqlAdo/MySqlConnectionPool.cs | 125 ++++
FreeSql/MySql/MySqlAdo/MygisTypes.cs | 308 +++++++++
FreeSql/MySql/MySqlCodeFirst.cs | 184 +++++
FreeSql/MySql/MySqlDbFirst.cs | 367 ++++++++++
FreeSql/MySql/MySqlExpression.cs | 159 +++++
FreeSql/MySql/MySqlProvider.cs | 50 ++
FreeSql/MySql/MySqlUtils.cs | 48 ++
FreeSql/PostgreSQL/Curd/PostgreSQLDelete.cs | 25 +
FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs | 30 +
FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs | 116 ++++
FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs | 49 ++
.../PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs | 61 ++
.../PostgreSQLAdo/PostgreSQLConnectionPool.cs | 153 +++++
.../PostgreSQLAdo/PostgreSQLTypesConverter.cs | 140 ++++
.../PostgreSQLTypesExtensions.cs | 18 +
FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs | 188 +++++
FreeSql/PostgreSQL/PostgreSQLDbFirst.cs | 366 ++++++++++
FreeSql/PostgreSQL/PostgreSQLExpression.cs | 159 +++++
FreeSql/PostgreSQL/PostgreSQLProvider.cs | 50 ++
FreeSql/PostgreSQL/PostgreSQLUtils.cs | 48 ++
FreeSql/SqlServer/Curd/SqlServerDelete.cs | 34 +
FreeSql/SqlServer/Curd/SqlServerInsert.cs | 37 +
FreeSql/SqlServer/Curd/SqlServerSelect.cs | 126 ++++
FreeSql/SqlServer/Curd/SqlServerUpdate.cs | 58 ++
.../SqlServer/SqlServerAdo/SqlServerAdo.cs | 61 ++
.../SqlServerAdo/SqlServerConnectionPool.cs | 147 ++++
FreeSql/SqlServer/SqlServerCodeFirst.cs | 164 +++++
FreeSql/SqlServer/SqlServerDbFirst.cs | 383 +++++++++++
FreeSql/SqlServer/SqlServerExpression.cs | 82 +++
FreeSql/SqlServer/SqlServerProvider.cs | 50 ++
FreeSql/SqlServer/SqlServerUtils.cs | 49 ++
LICENSE | 21 +
Templates/MySql/include/enumtype.tpl | 71 ++
.../rich-entity-navigation-object/Const.cs | 5 +
.../Models/for-table.cs.freesql | 239 +++++++
.../rich-entity-navigation-object/readme.md | 0
.../Models/for-table.cs.freesql | 59 ++
.../simple-entity-navigation-object/readme.md | 0
.../simple-entity/Models/for-table.cs.freesql | 33 +
Templates/MySql/simple-entity/readme.md | 0
readme.md | 10 +
130 files changed, 12283 insertions(+)
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 Docs/Generator.md
create mode 100644 Docs/codefirst.md
create mode 100644 Docs/dbfirst.md
create mode 100644 Docs/delete.md
create mode 100644 Docs/insert.md
create mode 100644 Docs/select.md
create mode 100644 Docs/update.md
create mode 100644 FreeSql.Tests/Class1.cs
create mode 100644 FreeSql.Tests/Extensions/StringExtensionsTest.cs
create mode 100644 FreeSql.Tests/FreeSql.Tests.csproj
create mode 100644 FreeSql.Tests/Generator/MySqlTemplateGeneratorTest.cs
create mode 100644 FreeSql.Tests/MySql/Curd/MySqlDeleteTest.cs
create mode 100644 FreeSql.Tests/MySql/Curd/MySqlInsertTest.cs
create mode 100644 FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs
create mode 100644 FreeSql.Tests/MySql/Curd/MySqlUpdateTest.cs
create mode 100644 FreeSql.Tests/MySql/MySqlAdo/MySqlAdoTest.cs
create mode 100644 FreeSql.Tests/MySql/MySqlCodeFirstTest.cs
create mode 100644 FreeSql.Tests/MySql/MySqlDbFirstTest.cs
create mode 100644 FreeSql.Tests/MySql/MySqlExpressionTest.cs
create mode 100644 FreeSql.Tests/UnitTest1.cs
create mode 100644 FreeSql.Tests/g.cs
create mode 100644 FreeSql.sln
create mode 100644 FreeSql/DataAnnotations/ColumnAttribute.cs
create mode 100644 FreeSql/DataAnnotations/TableAttribute.cs
create mode 100644 FreeSql/DatabaseModel/DBColumnInfo.cs
create mode 100644 FreeSql/DatabaseModel/DBTableInfo.cs
create mode 100644 FreeSql/DatabaseModel/DbForeignInfo.cs
create mode 100644 FreeSql/Extensions/NpgsqlTypesExtensions.cs
create mode 100644 FreeSql/Extensions/StringExtensions.cs
create mode 100644 FreeSql/FreeSql.csproj
create mode 100644 FreeSql/FreeSqlBuilder.cs
create mode 100644 FreeSql/FreeUtil.cs
create mode 100644 FreeSql/Generator/TemplateEngin.cs
create mode 100644 FreeSql/Generator/TemplateGenerator.cs
create mode 100644 FreeSql/Interface/Curd/IDelete.cs
create mode 100644 FreeSql/Interface/Curd/IInsert.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect0.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect1.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect10.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect2.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect3.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect4.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect5.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect6.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect7.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect8.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelect9.cs
create mode 100644 FreeSql/Interface/Curd/ISelect/ISelectFrom.cs
create mode 100644 FreeSql/Interface/Curd/IUpdate.cs
create mode 100644 FreeSql/Interface/IAdo.cs
create mode 100644 FreeSql/Interface/ICache.cs
create mode 100644 FreeSql/Interface/ICodeFirst.cs
create mode 100644 FreeSql/Interface/IDasql.cs
create mode 100644 FreeSql/Interface/iDbFirst.cs
create mode 100644 FreeSql/Internal/CommonExpression.cs
create mode 100644 FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs
create mode 100644 FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs
create mode 100644 FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderTransaction.cs
create mode 100644 FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderUtils.cs
create mode 100644 FreeSql/Internal/CommonProvider/CacheProvider.cs
create mode 100644 FreeSql/Internal/CommonProvider/DeleteProvider.cs
create mode 100644 FreeSql/Internal/CommonProvider/InsertProvider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select10Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select2Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select3Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select4Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select5Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select6Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select7Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select8Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select9Provider.cs
create mode 100644 FreeSql/Internal/CommonProvider/UpdateProvider.cs
create mode 100644 FreeSql/Internal/CommonUtils.cs
create mode 100644 FreeSql/Internal/Model/ColumnInfo.cs
create mode 100644 FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs
create mode 100644 FreeSql/Internal/Model/SelectColumnInfo.cs
create mode 100644 FreeSql/Internal/Model/SelectTableInfo.cs
create mode 100644 FreeSql/Internal/Model/TableInfo.cs
create mode 100644 FreeSql/Internal/Utils.cs
create mode 100644 FreeSql/MySql/Curd/MySqlDelete.cs
create mode 100644 FreeSql/MySql/Curd/MySqlInsert.cs
create mode 100644 FreeSql/MySql/Curd/MySqlSelect.cs
create mode 100644 FreeSql/MySql/Curd/MySqlUpdate.cs
create mode 100644 FreeSql/MySql/MySqlAdo/MySqlAdo.cs
create mode 100644 FreeSql/MySql/MySqlAdo/MySqlAdoExtensions.cs
create mode 100644 FreeSql/MySql/MySqlAdo/MySqlConnectionPool.cs
create mode 100644 FreeSql/MySql/MySqlAdo/MygisTypes.cs
create mode 100644 FreeSql/MySql/MySqlCodeFirst.cs
create mode 100644 FreeSql/MySql/MySqlDbFirst.cs
create mode 100644 FreeSql/MySql/MySqlExpression.cs
create mode 100644 FreeSql/MySql/MySqlProvider.cs
create mode 100644 FreeSql/MySql/MySqlUtils.cs
create mode 100644 FreeSql/PostgreSQL/Curd/PostgreSQLDelete.cs
create mode 100644 FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs
create mode 100644 FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs
create mode 100644 FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesConverter.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesExtensions.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLDbFirst.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLExpression.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLProvider.cs
create mode 100644 FreeSql/PostgreSQL/PostgreSQLUtils.cs
create mode 100644 FreeSql/SqlServer/Curd/SqlServerDelete.cs
create mode 100644 FreeSql/SqlServer/Curd/SqlServerInsert.cs
create mode 100644 FreeSql/SqlServer/Curd/SqlServerSelect.cs
create mode 100644 FreeSql/SqlServer/Curd/SqlServerUpdate.cs
create mode 100644 FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs
create mode 100644 FreeSql/SqlServer/SqlServerAdo/SqlServerConnectionPool.cs
create mode 100644 FreeSql/SqlServer/SqlServerCodeFirst.cs
create mode 100644 FreeSql/SqlServer/SqlServerDbFirst.cs
create mode 100644 FreeSql/SqlServer/SqlServerExpression.cs
create mode 100644 FreeSql/SqlServer/SqlServerProvider.cs
create mode 100644 FreeSql/SqlServer/SqlServerUtils.cs
create mode 100644 LICENSE
create mode 100644 Templates/MySql/include/enumtype.tpl
create mode 100644 Templates/MySql/rich-entity-navigation-object/Const.cs
create mode 100644 Templates/MySql/rich-entity-navigation-object/Models/for-table.cs.freesql
create mode 100644 Templates/MySql/rich-entity-navigation-object/readme.md
create mode 100644 Templates/MySql/simple-entity-navigation-object/Models/for-table.cs.freesql
create mode 100644 Templates/MySql/simple-entity-navigation-object/readme.md
create mode 100644 Templates/MySql/simple-entity/Models/for-table.cs.freesql
create mode 100644 Templates/MySql/simple-entity/readme.md
create mode 100644 readme.md
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..1ff0c423
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..616428fc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,246 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+[Xx]64/
+[Xx]86/
+[Bb]uild/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+package-lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+
+# TODO: Un-comment the next line if you do not want to checkin
+# your web deploy settings because they may include unencrypted
+# passwords
+#*.pubxml
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Microsoft Azure ApplicationInsights config file
+ApplicationInsights.config
+
+# Windows Store app package directory
+AppPackages/
+BundleArtifacts/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# LightSwitch generated files
+GeneratedArtifacts/
+ModelManifest.xml
+
+# Paket dependency manager
+.paket/paket.exe
+
+# FAKE - F# Make
+.fake/
\ No newline at end of file
diff --git a/Docs/Generator.md b/Docs/Generator.md
new file mode 100644
index 00000000..7c21ead4
--- /dev/null
+++ b/Docs/Generator.md
@@ -0,0 +1,92 @@
+# 生成器
+
+生成器是基于 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
+//定义 mysql FreeSql
+var mysql = 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();
+
+//创建模板生成类现实
+var gen = new FreeSql.Generator.TemplateGenerator();
+gen.Build(mysql.DbFirst,
+ @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity", //模板目录(事先下载)
+ @"C:\Users\28810\Desktop\新建文件夹 (9)", //生成后保存的目录
+ "cccddd" //数据库
+);
+```
+
+## 模板语法
+
+```html
+
+
+{#title}
+
+
+
+
+{#表达式}
+{##表达式} 当表达式可能发生runtime错误时使用,性能没有上面的高
+
+
+{include ../header.html}
+
+
aaa
+
bbb {#i}
+
ccc {#i}
+
+
+
+{module module_name1 parms1, 2, 3...}
+{/module}
+{module module_name2 parms1, 2, 3...}
+{/module}
+
+
+{import ../module.html as myname}
+{#myname.module_name(parms1, 2, 3...)}
+
+
+{extends ../inc/layout.html}
+{block body}{/block}
+
+
+{%
+for (var a = 0; a < 100; a++)
+ print(a);
+%}
+
+
+{if i === 50}
+{elseif i > 60}
+{else}
+{/if}
+
+
+{for i 1,101} 可自定义名 {for index2 表达式1 in 表达式2}
+
+{for item,index in items} 可选参数称 index
+ 可自定义名 {for item2, index99 in 数组表达式}
+
+{for key,item,index on json} 可选参数 item, index,
+ 可自定义名 {for key2, item2, index99 in 对象表达式}
+{/for}
+
+
+{miss}
+此块内容不被bmw.js解析
+{/miss}
+
+
+
+```
\ No newline at end of file
diff --git a/Docs/codefirst.md b/Docs/codefirst.md
new file mode 100644
index 00000000..5ca33e0a
--- /dev/null
+++ b/Docs/codefirst.md
@@ -0,0 +1,135 @@
+# CodeFirst
+
+### 支持的类型
+
+bool, byte, short, int, long, byte, ushort, uint, ulong, double, float, decimal, int
+
+bool?, byte?, short?, int?, long?, byte?, ushort?, uint?, ulong?, double?, float?, decimal?, int?
+
+TimeSpan, DateTime
+
+TimeSpan?, DateTime?
+
+byte[], string
+
+MygisPoint, MygisLineString, MygisPolygon, MygisMultiPoint, MygisMultiLineString, MygisMultiPolygon
+
+```csharp
+var mysql = new MySql(null, null, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=3", null, null);
+```
+
+### 自动同步实体结构【开发环境必备】
+
+自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改
+
+```csharp
+mysql.CodeFirst.IsAutoSyncDataStructure = true;
+```
+
+> 此功能默认为开启状态,发布正式环境后,请修改此设置
+
+> 虽然【自动同步实体结构】功能开发非常好用,但是有个坏处,就是数据库后面会很乱,没用的字段一大堆
+
+### 手工同步实体结构
+
+| 实体&表对比 | 添加 | 改名 | 删除 |
+| - | - | - | - |
+| | √ | √ | X |
+
+| 实体属性&字段对比 | 添加 | 修改可空 | 修改自增 | 修改类型 | 改名 | 删除 |
+| - | - | - | - | - | - | - |
+| | √ | √ | √ | √ | √ | X |
+
+> 为了保证安全,不提供删除字段
+
+
+1、提供方法对比实体,与数据库中的变化部分
+
+```csharp
+var t1 = mysql.CodeFirst.GetComparisonDDLStatements();
+
+class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ public ushort fusho { get; set; }
+}
+```
+```sql
+CREATE TABLE IF NOT EXISTS `cccddd`.`Topic` (
+ `Id` INT(11) NOT NULL AUTO_INCREMENT,
+ `Clicks` INT(11) NOT NULL,
+ `Title` VARCHAR(255),
+ `CreateTime` DATETIME NOT NULL,
+ `fusho` SMALLINT(5) UNSIGNED NOT NULL,
+ PRIMARY KEY (`Id`)
+) Engine=InnoDB CHARACTER SET utf8;
+```
+
+2、指定实体的表名
+
+指定 Name 后,实体类名变化不影响数据库对应的表
+```csharp
+[Table(Name = "tb_topic111")]
+class Topic {
+ //...
+}
+```
+
+3、无指定实体的表名,修改实体类名
+
+指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】
+
+```csharp
+[Table(OldName = "Topic")]
+class Topic2 {
+ //...
+}
+```
+```sql
+ALTER TABLE `cccddd`.`Topic` RENAME TO `cccddd`.`Topic2`;
+```
+
+4、修改属性的类型
+
+把 Id 类型改为 uint 后
+```sql
+ALTER TABLE `cccddd`.`Topic2` MODIFY `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;
+```
+```csharp
+[Column(DbType = "varchar(128)")]
+public string Title { get; set; }
+```
+```sql
+ALTER TABLE `cccddd`.`Topic2` MODIFY `Title2` VARCHAR(128);
+```
+
+5、指定属性的字段名
+
+这样指定后,修改实体的属性名不影响数据库对应的列
+```csharp
+[Column(Name = "titl2")]
+public string Title { get; set; }
+```
+
+6、无指定属性的字段名,修改属性名
+
+指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】
+
+```csharp
+[Column(OldName = "Title2")]
+public string Title { get; set; }
+```
+```sql
+ALTER TABLE `cccddd`.`Topic2` CHANGE COLUMN `Title2` `Title` VARCHAR(255);
+```
+
+7、提供方法同步结构
+
+```csharp
+var t2 = mysql.CodeFirst.SyncStructure();
+//同步实体类型到数据库
+```
diff --git a/Docs/dbfirst.md b/Docs/dbfirst.md
new file mode 100644
index 00000000..b8ba7091
--- /dev/null
+++ b/Docs/dbfirst.md
@@ -0,0 +1,112 @@
+# DbFirst
+
+```csharp
+MySql mysql = new MySql(null, null, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=3", null, null);
+```
+
+### 获取所有数据库
+
+```csharp
+var t1 = mysql.DbFirst.GetDatabases();
+//返回字符串数组, ["cccddd", "test"]
+```
+
+### 获取指定数据库的表信息
+
+```csharp
+var t2 = mysql.DbFirst.GetTablesByDatabase(mysql.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
+//定义 mysql FreeSql
+var mysql = 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();
+
+//创建模板生成类现实
+var gen = new FreeSql.Generator.TemplateGenerator();
+gen.Build(mysql.DbFirst,
+ @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity", //模板目录(事先下载)
+ @"C:\Users\28810\Desktop\新建文件夹 (9)", //生成后保存的目录
+ "cccddd" //数据库
+);
+```
+
+## 模板语法
+
+```html
+
+
+{#title}
+
+
+
+
+{#表达式}
+{##表达式} 当表达式可能发生runtime错误时使用,性能没有上面的高
+
+
+{include ../header.html}
+
+
aaa
+
bbb {#i}
+
ccc {#i}
+
+
+
+{module module_name1 parms1, 2, 3...}
+{/module}
+{module module_name2 parms1, 2, 3...}
+{/module}
+
+
+{import ../module.html as myname}
+{#myname.module_name(parms1, 2, 3...)}
+
+
+{extends ../inc/layout.html}
+{block body}{/block}
+
+
+{%
+for (var a = 0; a < 100; a++)
+ print(a);
+%}
+
+
+{if i === 50}
+{elseif i > 60}
+{else}
+{/if}
+
+
+{for i 1,101} 可自定义名 {for index2 表达式1 in 表达式2}
+
+{for item,index in items} 可选参数称 index
+ 可自定义名 {for item2, index99 in 数组表达式}
+
+{for key,item,index on json} 可选参数 item, index,
+ 可自定义名 {for key2, item2, index99 in 对象表达式}
+{/for}
+
+
+{miss}
+此块内容不被bmw.js解析
+{/miss}
+
+
+
+```
\ No newline at end of file
diff --git a/Docs/delete.md b/Docs/delete.md
new file mode 100644
index 00000000..c12c71c0
--- /dev/null
+++ b/Docs/delete.md
@@ -0,0 +1,80 @@
+# 删除数据
+
+| 方法 | 返回值 | 参数 | 描述 |
+| - | - | - | - |
+| Where | \ | Lambda | 表达式条件,仅支持实体基础成员(不包含导航对象) |
+| Where | \ | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) |
+| Where | \ | T1 \| IEnumerable | 传入实体或集合,将其主键作为条件 |
+| WhereExists | \ | ISelect | 子查询是否存在 |
+| ToSql | string | | 返回即将执行的SQL语句 |
+| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
+| ExecuteDeleted | List\ | | 执行SQL语句,返回被删除的记录 |
+
+### 测试代码
+
+```csharp
+var mysql = new MySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=3");
+IDelete delete => mysql.Delete();
+
+[Table(Name = "tb_topic")]
+class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+}
+```
+
+### 动态条件
+```csharp
+Delete(object dywhere)
+```
+dywhere 支持
+
+* 主键值
+* new[] { 主键值1, 主键值2 }
+* Topic对象
+* new[] { Topic对象1, Topic对象2 }
+* new { id = 1 }
+
+```csharp
+var t1 = mysql.Delete(new[] { 1, 2 }).ToSql();
+//DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2)
+
+var t2 = mysql.Delete(new Topic { Id = 1, Title = "test" }).ToSql();
+//DELETE FROM `tb_topic` WHERE (`Id` = 1)
+
+var t3 = mysql.Delete(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).ToSql();
+//DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2)
+
+var t4 = mysql.Delete(new { id = 1 }).ToSql();
+//DELETE FROM `tb_topic` WHERE (`Id` = 1)
+```
+
+### 删除条件
+
+```csharp
+var t5 = delete.Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
+//DELETE FROM `tb_topic` WHERE (`Id` = 1)
+
+var t6 = delete.Where("id = ?id", new { id = 1 }).ToSql().Replace("\r\n", "");
+//DELETE FROM `tb_topic` WHERE (id = ?id)
+
+var item = new Topic { Id = 1, Title = "newtitle" };
+var t7 = delete.Where(item).ToSql().Replace("\r\n", "");
+//DELETE FROM `tb_topic` WHERE (`Id` = 1)
+
+var items = new List();
+for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+var t8 = delete.Where(items).ToSql().Replace("\r\n", "");
+//DELETE FROM `tb_topic` WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))
+```
+
+### 执行命令
+
+| 方法 | 返回值 | 参数 | 描述 |
+| - | - | - | - |
+| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
+| ExecuteDeleted | List\ | | 执行SQL语句,返回被删除的记录 |
\ No newline at end of file
diff --git a/Docs/insert.md b/Docs/insert.md
new file mode 100644
index 00000000..fc8fff86
--- /dev/null
+++ b/Docs/insert.md
@@ -0,0 +1,77 @@
+# 插入数据
+
+| 方法 | 返回值 | 参数 | 描述 |
+| - | - | - | - |
+| AppendData | \ | T1 \| IEnumerable | 追加准备插入的实体 |
+| InsertColumns | \ | Lambda | 只插入的列 |
+| IgnoreColumns | \ | Lambda | 忽略的列 |
+| ToSql | string | | 返回即将执行的SQL语句 |
+| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
+| ExecuteIdentity | long | | 执行SQL语句,返回自增值 |
+| ExecuteInserted | List\ | | 执行SQL语句,返回插入后的记录 |
+
+### 列优先级
+
+> 全部列 < 指定列(InsertColumns) < 忽略列(IgnoreColumns)
+
+### 测试代码
+
+```csharp
+var mysql = new MySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=3");
+IInsert insert => mysql.Insert();
+
+[Table(Name = "tb_topic")]
+class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+}
+
+var items = new List();
+for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+```
+
+### 插入
+
+```csharp
+var t1 = insert.AppendData(items.First()).ToSql();
+//INSERT INTO `tb_topic`(`Clicks`, `Title`, `CreateTime`) VALUES(?Clicks0, ?Title0, ?CreateTime0)
+```
+
+### 批量插入
+
+```csharp
+var t2 = insert.AppendData(items).ToSql();
+//INSERT INTO `tb_topic`(`Clicks`, `Title`, `CreateTime`) VALUES(?Clicks0, ?Title0, ?CreateTime0), (?Clicks1, ?Title1, ?CreateTime1), (?Clicks2, ?Title2, ?CreateTime2), (?Clicks3, ?Title3, ?CreateTime3), (?Clicks4, ?Title4, ?CreateTime4), (?Clicks5, ?Title5, ?CreateTime5), (?Clicks6, ?Title6, ?CreateTime6), (?Clicks7, ?Title7, ?CreateTime7), (?Clicks8, ?Title8, ?CreateTime8), (?Clicks9, ?Title9, ?CreateTime9)
+```
+
+### 只想插入指定的列
+
+```csharp
+var t3 = insert.AppendData(items).InsertColumns(a => a.Title).ToSql();
+//INSERT INTO `tb_topic`(`Title`) VALUES(?Title0), (?Title1), (?Title2), (?Title3), (?Title4), (?Title5), (?Title6), (?Title7), (?Title8), (?Title9)
+
+var t4 = insert.AppendData(items).InsertColumns(a =>new { a.Title, a.Clicks }).ToSql();
+//INSERT INTO `tb_topic`(`Clicks`, `Title`) VALUES(?Clicks0, ?Title0), (?Clicks1, ?Title1), (?Clicks2, ?Title2), (?Clicks3, ?Title3), (?Clicks4, ?Title4), (?Clicks5, ?Title5), (?Clicks6, ?Title6), (?Clicks7, ?Title7), (?Clicks8, ?Title8), (?Clicks9, ?Title9)
+```
+
+### 忽略列
+
+```csharp
+var t5 = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql();
+//INSERT INTO `tb_topic`(`Clicks`, `Title`) VALUES(?Clicks0, ?Title0), (?Clicks1, ?Title1), (?Clicks2, ?Title2), (?Clicks3, ?Title3), (?Clicks4, ?Title4), (?Clicks5, ?Title5), (?Clicks6, ?Title6), (?Clicks7, ?Title7), (?Clicks8, ?Title8), (?Clicks9, ?Title9)
+
+var t6 = insert.AppendData(items).IgnoreColumns(a => new { a.Title, a.CreateTime }).ToSql();
+///INSERT INTO `tb_topic`(`Clicks`) VALUES(?Clicks0), (?Clicks1), (?Clicks2), (?Clicks3), (?Clicks4), (?Clicks5), (?Clicks6), (?Clicks7), (?Clicks8), (?Clicks9)
+```
+
+### 执行命令
+
+| 方法 | 返回值 | 描述 |
+| - | - | - |
+| ExecuteAffrows | long | 执行SQL语句,返回影响的行数 |
+| ExecuteIdentity | long | 执行SQL语句,返回自增值 |
+| ExecuteInserted | List\ | 执行SQL语句,返回插入后的记录 |
diff --git a/Docs/select.md b/Docs/select.md
new file mode 100644
index 00000000..49ca66d4
--- /dev/null
+++ b/Docs/select.md
@@ -0,0 +1,48 @@
+# 查询数据
+
+| 方法 | 返回值 | 参数 | 描述 |
+| ------------- | - | - | - |
+| ToSql | string | | 返回即将执行的SQL语句 |
+| ToList | List | | 执行SQL查询,返回 T1 实体所有字段的记录,若存在导航属性则一起查询返回,记录不存在时返回 Count 为 0 的列表 |
+| ToList\ | List\ | Lambda | 执行SQL查询,返回指定字段的记录,记录不存在时返回 Count 为 0 的列表 |
+| ToList\ | List\ | string field | 执行SQL查询,返回 field 指定字段的记录,并以元组或基础类型(int,string,long)接收,记录不存在时返回 Count 为 0 的列表 |
+| ToOne | T1 | | 执行SQL查询,返回 T1 实体所有字段的第一条记录,记录不存在时返回 null |
+| Any | bool | | 执行SQL查询,是否有记录 |
+| Sum | T | Lambda | 指定一个列求和 |
+| Min | T | Lambda | 指定一个列求最小值 |
+| Max | T | Lambda | 指定一个列求最大值 |
+| Avg | T | Lambda | 指定一个列求平均值 |
+| 【分页】 |
+| Count | long | | 查询的记录数量 |
+| Count | \ | out long | 查询的记录数量,以参数out形式返回 |
+| Skip | \ | int offset | 查询向后偏移行数 |
+| Offset | \ | int offset | 查询向后偏移行数 |
+| Limit | \ | int limit | 查询多少条数据 |
+| Take | \ | int limit | 查询多少条数据 |
+| Page | \ | int pageIndex, int pageSize | 分页 |
+| 【条件】 |
+| Where | \ | Lambda | 支持多表查询表达式 |
+| WhereIf | \ | bool, Lambda | 支持多表查询表达式 |
+| Where | \ | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) |
+| WhereIf | \ | bool, string, parms | 原生sql语法条件,WhereIf(true, "id = ?id", new { id = 1 }) |
+| WhereLike | \ | Lambda, string, bool | like 查询条件,where title like '%xxx%' or content like '%xxx%' |
+| 【分组】 |
+| GroupBy | \ | Lambda | 按选择的列分组,GroupBy(a => a.Name) | GroupBy(a => new{a.Name,a.Time}) | GroupBy(a => new[]{"name","time"}) |
+| GroupBy | \ | string, parms | 按原生sql语法分组,GroupBy("concat(name, ?cc)", new { cc = 1 }) |
+| Having | \ | string, parms | 按原生sql语法聚合条件过滤,Having("count(name) = ?cc", new { cc = 1 }) |
+| 【排序】 |
+| OrderBy | \ | Lambda | 按列排序,OrderBy(a => a.Time) |
+| OrderByDescending | \ | Lambda | 按列倒向排序,OrderByDescending(a => a.Time) |
+| OrderBy | \ | string, parms | 按原生sql语法排序,OrderBy("count(name) + ?cc", new { cc = 1 }) |
+| 【联表】 |
+| LeftJoin | \ | Lambda | 左联查询,可使用导航属性,或指定关联的实体类型 |
+| InnerJoin | \ | Lambda | 联接查询,可使用导航属性,或指定关联的实体类型 |
+| RightJoin | \ | Lambda | 右联查询,可使用导航属性,或指定关联的实体类型 |
+| LeftJoin | \ | string, parms | 左联查询,使用原生sql语法,LeftJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) |
+| InnerJoin | \ | string, parms | 联接查询,使用原生sql语法,InnerJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) |
+| RightJoin | \ | string, parms | 右联查询,使用原生sql语法,RightJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 }) |
+| From | \ | Lambda | 多表查询,3个表以上使用非常方便,目前设计最大支持10个表 |
+| 【其他】 |
+| As | \ | string alias = "a" | 指定别名 |
+| Master | \ | | 指定从主库查询(默认查询从库) |
+| Caching | \ | int seconds, string key = null | 缓存查询结果 |
diff --git a/Docs/update.md b/Docs/update.md
new file mode 100644
index 00000000..7ba27d50
--- /dev/null
+++ b/Docs/update.md
@@ -0,0 +1,127 @@
+# 更新数据
+
+| 方法 | 返回值 | 参数 | 描述 |
+| - | - | - | - |
+| SetSource | \ | T1 \| IEnumerable | 更新数据,设置更新的实体 |
+| IgnoreColumns | \ | Lambda | 忽略的列 |
+| Set | \ | Lambda, value | 设置列的新值,Set(a => a.Name, "newvalue") |
+| Set | \ | Lambda | 设置列的的新值为基础上增加,Set(a => a.Clicks + 1),相当于 clicks=clicks+1; |
+| SetRaw | \ | string, parms | 设置值,自定义SQL语法,SetRaw("title = ?title", new { title = "newtitle" }) |
+| Where | \ | Lambda | 表达式条件,仅支持实体基础成员(不包含导航对象) |
+| Where | \ | string, parms | 原生sql语法条件,Where("id = ?id", new { id = 1 }) |
+| Where | \ | T1 \| IEnumerable | 传入实体或集合,将其主键作为条件 |
+| WhereExists | \ | ISelect | 子查询是否存在 |
+| ToSql | string | | 返回即将执行的SQL语句 |
+| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
+| ExecuteUpdated | List\ | | 执行SQL语句,返回更新后的记录 |
+
+### 列优先级
+
+> 全部列 < 指定列(Set/SetRaw) < 忽略列(IgnoreColumns)
+
+### 测试代码
+
+```csharp
+[Table(Name = "tb_topic")]
+class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+}
+
+var mysql = new MySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=3");
+IUpdate update => mysql.Update();
+```
+
+### 动态条件
+```csharp
+Update(object dywhere)
+```
+dywhere 支持
+
+* 主键值
+* new[] { 主键值1, 主键值2 }
+* Topic对象
+* new[] { Topic对象1, Topic对象2 }
+* new { id = 1 }
+
+### 更新指定列
+```csharp
+var t1 = mysql.Update(1).Set(a => a.CreateTime, DateTime.Now).ToSql();
+//UPDATE `tb_topic` SET `CreateTime` = '2018-12-08 00:04:59' WHERE (`Id` = 1)
+```
+
+### 更新指定列,累加
+```csharp
+var t2 = mysql.Update(1).Set(a => a.Clicks + 1).ToSql();
+//UPDATE `tb_topic` SET `Clicks` = ifnull(`Clicks`,0) + 1 WHERE (`Id` = 1)
+```
+
+### 保存实体
+```csharp
+var item = new Topic { Id = 1, Title = "newtitle" };
+var t3 = update.SetSource(item).ToSql();
+//UPDATE `tb_topic` SET `Clicks` = ?p_0, `Title` = ?p_1, `CreateTime` = ?p_2 WHERE (`Id` = 1)
+```
+
+### 保存实体,忽略一些列
+```csharp
+var t4 = update.SetSource(item).IgnoreColumns(a => a.Clicks).ToSql();
+//UPDATE `tb_topic` SET `Title` = ?p_0, `CreateTime` = ?p_1 WHERE (`Id` = 1)
+var t5 = update.SetSource(item).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql();
+//UPDATE `tb_topic` SET `Title` = ?p_0 WHERE (`Id` = 1)
+```
+
+### 批量保存
+```csharp
+var items = new List();
+for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+var t6 = update.SetSource(items).ToSql();
+//UPDATE `tb_topic` SET `Clicks` = CASE `Id` WHEN 1 THEN ?p_0 WHEN 2 THEN ?p_1 WHEN 3 THEN ?p_2 WHEN 4 THEN ?p_3 WHEN 5 THEN ?p_4 WHEN 6 THEN ?p_5 WHEN 7 THEN ?p_6 WHEN 8 THEN ?p_7 WHEN 9 THEN ?p_8 WHEN 10 THEN ?p_9 END, `Title` = CASE `Id` WHEN 1 THEN ?p_10 WHEN 2 THEN ?p_11 WHEN 3 THEN ?p_12 WHEN 4 THEN ?p_13 WHEN 5 THEN ?p_14 WHEN 6 THEN ?p_15 WHEN 7 THEN ?p_16 WHEN 8 THEN ?p_17 WHEN 9 THEN ?p_18 WHEN 10 THEN ?p_19 END, `CreateTime` = CASE `Id` WHEN 1 THEN ?p_20 WHEN 2 THEN ?p_21 WHEN 3 THEN ?p_22 WHEN 4 THEN ?p_23 WHEN 5 THEN ?p_24 WHEN 6 THEN ?p_25 WHEN 7 THEN ?p_26 WHEN 8 THEN ?p_27 WHEN 9 THEN ?p_28 WHEN 10 THEN ?p_29 END WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))
+```
+
+> 批量保存的场景,先查询20条记录,根据本地很复杂的规则把集合的值改完后
+
+> 传统做法是循环20次保存,用 case when 只要一次就行
+
+### 批量保存,忽略一些列
+```csharp
+var t7 = update.SetSource(items).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql();
+//UPDATE `tb_topic` SET `Title` = CASE `Id` WHEN 1 THEN ?p_0 WHEN 2 THEN ?p_1 WHEN 3 THEN ?p_2 WHEN 4 THEN ?p_3 WHEN 5 THEN ?p_4 WHEN 6 THEN ?p_5 WHEN 7 THEN ?p_6 WHEN 8 THEN ?p_7 WHEN 9 THEN ?p_8 WHEN 10 THEN ?p_9 END WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))
+```
+
+### 批量更新指定列
+```csharp
+var t8 = update.SetSource(items).Set(a => a.CreateTime, DateTime.Now).ToSql();
+//UPDATE `tb_topic` SET `CreateTime` = ?p_0 WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))
+```
+
+> 指定列更新后,批量保存将失效
+
+### 更新条件
+
+> 除了顶上介绍的 dywhere 构造参数外,还支持 Where lambda/sql 方法
+
+```csharp
+var t9 = update.Set(a => a.Title, "新标题").Where(a => a.Id == 1).ToSql();
+//UPDATE `tb_topic` SET `Title` = '新标题' WHERE (Id = 1)
+```
+
+### 自定义SQL
+
+```csharp
+var t10 = update.SetRaw("Title = {0}", "新标题").Where("Id = {0}", 1).ToSql();
+//UPDATE `tb_topic` SET Title = '新标题' WHERE (Id = 1)
+//sql语法条件,参数使用 {0},与 string.Format 保持一致,无须加单引号,错误的用法:'{0}'
+```
+
+### 执行命令
+
+| 方法 | 返回值 | 参数 | 描述 |
+| - | - | - | - |
+| ExecuteAffrows | long | | 执行SQL语句,返回影响的行数 |
+| ExecuteUpdated | List\ | | 执行SQL语句,返回更新后的记录 |
\ No newline at end of file
diff --git a/FreeSql.Tests/Class1.cs b/FreeSql.Tests/Class1.cs
new file mode 100644
index 00000000..da9da817
--- /dev/null
+++ b/FreeSql.Tests/Class1.cs
@@ -0,0 +1,126 @@
+
+//using System;
+//using System.Collections;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Text;
+//using System.Text.RegularExpressions;
+//using FreeSql;
+//using FreeSql.DatabaseModel;
+
+////namespace TplDynamicCodeGenerate {
+//public class TplDynamicCodeGenerate_view1 : FreeSql.Generator.TemplateEngin.ITemplateOutput {
+// public FreeSql.Generator.TemplateEngin.TemplateReturnInfo OuTpUt(StringBuilder tOuTpUt, IDictionary oPtIoNs, string rEfErErFiLeNaMe, FreeSql.Generator.TemplateEngin tEmPlAtEsEnDeR) {
+// FreeSql.Generator.TemplateEngin.TemplateReturnInfo rTn = tOuTpUt == null ?
+// new FreeSql.Generator.TemplateEngin.TemplateReturnInfo { Sb = (tOuTpUt = new StringBuilder()), Blocks = new Dictionary() } :
+// new FreeSql.Generator.TemplateEngin.TemplateReturnInfo { Sb = tOuTpUt, Blocks = new Dictionary() };
+// Dictionary TPL__blocks = rTn.Blocks;
+// Stack TPL__blocks_stack = new Stack();
+// int[] TPL__blocks_stack_peek;
+// List TPL__forc = new List();
+// Func pRoCeSsOpTiOnS = new Func(delegate () {
+// IDictionary nEwoPtIoNs = new Hashtable();
+// foreach (DictionaryEntry oPtIoNs_dE in oPtIoNs)
+// nEwoPtIoNs[oPtIoNs_dE.Key] = oPtIoNs_dE.Value;
+// foreach (IDictionary TPL__forc_dIc in TPL__forc)
+// foreach (DictionaryEntry TPL__forc_dIc_dE in TPL__forc_dIc)
+// nEwoPtIoNs[TPL__forc_dIc_dE.Key] = TPL__forc_dIc_dE.Value;
+// return nEwoPtIoNs;
+// });
+// FreeSql.Generator.TemplateEngin.TemplateIf tPlIf = delegate (object exp) {
+// if (exp is bool) return (bool)exp;
+// if (exp == null) return false;
+// if (exp is int && (int)exp == 0) return false;
+// if (exp is string && (string)exp == string.Empty) return false;
+// if (exp is long && (long)exp == 0) return false;
+// if (exp is short && (short)exp == 0) return false;
+// if (exp is byte && (byte)exp == 0) return false;
+// if (exp is double && (double)exp == 0) return false;
+// if (exp is float && (float)exp == 0) return false;
+// if (exp is decimal && (decimal)exp == 0) return false;
+// return true;
+// };
+// FreeSql.Generator.TemplateEngin.TemplatePrint print = delegate (object[] pArMs) {
+// if (pArMs == null || pArMs.Length == 0) return;
+// foreach (object pArMs_A in pArMs) if (pArMs_A != null) tOuTpUt.Append(pArMs_A);
+// };
+// FreeSql.Generator.TemplateEngin.TemplatePrint Print = prin
+// dynamic index = oPtIoNs["index"];
+// dynamic col = oPtIoNs["col"];
+// dynamic table = oPtIoNs["table"];
+// dynamic dbfirst = oPtIoNs["dbfirst"]; t;
+// tOuTpUt.Append("using System;\r\nusing System.Collections;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Threading.Tasks;\r\nusing Newtonsoft.Json;\r\nusing FreeSql.DataAnnotations;\r\n");
+
+// var dbf = dbfirst as FreeSql.IDbFirst;
+// var cols = (table.Columns as List);
+
+// Func UString = stra => stra.Substring(0, 1).ToUpper() + stra.Substring(1);
+// Func GetCsType = cola3 => {
+// if (cola3.DbType == (int)MySql.Data.MySqlClient.MySqlDbType.Enum || cola3.DbType == (int)MySql.Data.MySqlClient.MySqlDbType.Set) {
+// return $"{UString(cola3.Table.Name)}{cola3.Name.ToUpper()}{(cola3.IsNullable ? "?" : "")}";
+// }
+// return dbf.GetCsType(cola3);
+// };
+
+// tOuTpUt.Append("\r\nnamespace test.Model {\r\n\r\n [JsonObject(MemberSerialization.OptIn), Table(Name = \"");
+// Print(!string.IsNullOrEmpty(table.Schema) ? table.Schema + "." : "");
+// tOuTpUt.Append("");
+// Print(table.Name);
+// tOuTpUt.Append("\"");
+// if (tPlIf(cols.Where(cola003 => cola003.Name.ToLower() == "is_deleted" || cola003.Name.ToLower() == "isdeleted").Any())) {
+// tOuTpUt.Append(", SelectFilter = \"a.IsDeleted = 1\"");
+// }
+// tOuTpUt.Append(")]\r\n public partial class ");
+// Print(UString(table.Name));
+// tOuTpUt.Append(" {");
+// //new Action(delegate () {
+// IDictionary TPL__tmp1 = new Hashtable();
+// TPL__forc.Add(TPL__tmp1);
+// var TPL__tmp2 = table.Columns;
+// var TPL__tmp3 = col;
+// var TPL__tmp4 = index;
+// index = 0;
+// if (TPL__tmp2 != null)
+// foreach (var TPL__tmp5 in TPL__tmp2) {
+// TPL__tmp1["index"] = ++index;
+// TPL__tmp1["col"] = TPL__tmp5;
+// col = TPL__tmp5;
+// tOuTpUt.Append("\r\n ");
+// if (tPlIf(string.IsNullOrEmpty(col.Coment) == false)) {
+// tOuTpUt.Append("/// \r\n /// ");
+// Print(col.Coment.Replace("\r\n", "\n").Replace("\n", "\r\n /// "));
+// tOuTpUt.Append("\r\n /// ");
+// }
+// tOuTpUt.Append("\r\n [JsonProperty, Column(Name = \"");
+// Print(col.Name);
+// tOuTpUt.Append("\", DbType = \"");
+// Print(col.DbTypeTextFull);
+// tOuTpUt.Append("\"");
+// if (tPlIf(col.IsPrimary == true)) {
+// tOuTpUt.Append(", IsPrimary = true");
+// }
+// tOuTpUt.Append("");
+// if (tPlIf(col.IsIdentity == true)) {
+// tOuTpUt.Append(", IsIdentity = true");
+// }
+// tOuTpUt.Append("");
+// if (tPlIf(col.IsNullable == true)) {
+// tOuTpUt.Append(", IsNullable = true");
+// }
+// tOuTpUt.Append(")]\r\n public ");
+// Print(GetCsType(col));
+// tOuTpUt.Append(" ");
+// Print(UString(col.Name));
+// tOuTpUt.Append(" { get; set; }\r\n ");
+// }
+// col = TPL__tmp3;
+// index = TPL__tmp4;
+// TPL__forc.RemoveAt(TPL__forc.Count - 1);
+// //})();
+// tOuTpUt.Append("\r\n }\r\n");
+// tEmPlAtEsEnDeR.RenderFile2(tOuTpUt, pRoCeSsOpTiOnS(), "../../include/enumtype.tpl", rEfErErFiLeNaMe);
+// tOuTpUt.Append("\r\n}");
+// return rTn;
+// }
+//}
+////}
diff --git a/FreeSql.Tests/Extensions/StringExtensionsTest.cs b/FreeSql.Tests/Extensions/StringExtensionsTest.cs
new file mode 100644
index 00000000..c7352c24
--- /dev/null
+++ b/FreeSql.Tests/Extensions/StringExtensionsTest.cs
@@ -0,0 +1,65 @@
+using Xunit;
+
+namespace FreeSql.Tests.Extensions {
+ public class StringExtensionsTest {
+ [Fact]
+ public void FormatMySql() {
+
+ Assert.Empty(((string)null).FormatMySql("11"));
+ Assert.Equal("a=1", "a={0}".FormatMySql(1));
+ Assert.Equal("a =1", "a ={0}".FormatMySql(1));
+ Assert.Equal("a = 1", "a = {0}".FormatMySql(1));
+ Assert.Equal("a='a'", "a={0}".FormatMySql('a'));
+ Assert.Equal("a ='a'", "a ={0}".FormatMySql('a'));
+ Assert.Equal("a = 'a'", "a = {0}".FormatMySql('a'));
+
+ Assert.Equal("a=1 and b IS NULL", "a={0} and b={1}".FormatMySql(1, null));
+ Assert.Equal("a =1 and b IS NULL", "a ={0} and b ={1}".FormatMySql(1, null));
+ Assert.Equal("a = 1 and b IS NULL", "a = {0} and b = {1}".FormatMySql(1, null));
+
+ Assert.Equal("a=1 and b IS NULL and c in (1,2,3,4)", "a={0} and b={1} and c in {2}".FormatMySql(1, null, new[] { 1, 2, 3, 4 }));
+ Assert.Equal("a=1 and b IS NULL and c IS NULL", "a={0} and b={1} and c in {2}".FormatMySql(1, null, null));
+ Assert.Equal("a=1 and b IS NULL and c not IS NULL", "a={0} and b={1} and c not in {2}".FormatMySql(1, null, null));
+ }
+
+ [Fact]
+ public void FormatSqlServer() {
+
+ Assert.Empty(((string)null).FormatSqlServer("11"));
+ Assert.Equal("a=1", "a={0}".FormatSqlServer(1));
+ Assert.Equal("a =1", "a ={0}".FormatSqlServer(1));
+ Assert.Equal("a = 1", "a = {0}".FormatSqlServer(1));
+ Assert.Equal("a='a'", "a={0}".FormatSqlServer('a'));
+ Assert.Equal("a ='a'", "a ={0}".FormatSqlServer('a'));
+ Assert.Equal("a = 'a'", "a = {0}".FormatSqlServer('a'));
+
+ Assert.Equal("a=1 and b IS NULL", "a={0} and b={1}".FormatSqlServer(1, null));
+ Assert.Equal("a =1 and b IS NULL", "a ={0} and b ={1}".FormatSqlServer(1, null));
+ Assert.Equal("a = 1 and b IS NULL", "a = {0} and b = {1}".FormatSqlServer(1, null));
+
+ Assert.Equal("a=1 and b IS NULL and c in (1,2,3,4)", "a={0} and b={1} and c in {2}".FormatSqlServer(1, null, new[] { 1, 2, 3, 4 }));
+ Assert.Equal("a=1 and b IS NULL and c IS NULL", "a={0} and b={1} and c in {2}".FormatSqlServer(1, null, null));
+ Assert.Equal("a=1 and b IS NULL and c not IS NULL", "a={0} and b={1} and c not in {2}".FormatSqlServer(1, null, null));
+ }
+
+ [Fact]
+ public void FormatPostgreSQL() {
+
+ Assert.Empty(((string)null).FormatPostgreSQL("11"));
+ Assert.Equal("a=1", "a={0}".FormatPostgreSQL(1));
+ Assert.Equal("a =1", "a ={0}".FormatPostgreSQL(1));
+ Assert.Equal("a = 1", "a = {0}".FormatPostgreSQL(1));
+ Assert.Equal("a='a'", "a={0}".FormatPostgreSQL('a'));
+ Assert.Equal("a ='a'", "a ={0}".FormatPostgreSQL('a'));
+ Assert.Equal("a = 'a'", "a = {0}".FormatPostgreSQL('a'));
+
+ Assert.Equal("a=1 and b IS NULL", "a={0} and b={1}".FormatPostgreSQL(1, null));
+ Assert.Equal("a =1 and b IS NULL", "a ={0} and b ={1}".FormatPostgreSQL(1, null));
+ Assert.Equal("a = 1 and b IS NULL", "a = {0} and b = {1}".FormatPostgreSQL(1, null));
+
+ Assert.Equal("a=1 and b IS NULL and c in (1,2,3,4)", "a={0} and b={1} and c in {2}".FormatPostgreSQL(1, null, new[] { 1, 2, 3, 4 }));
+ Assert.Equal("a=1 and b IS NULL and c IS NULL", "a={0} and b={1} and c in {2}".FormatSqlServer(1, null, null));
+ Assert.Equal("a=1 and b IS NULL and c not IS NULL", "a={0} and b={1} and c not in {2}".FormatSqlServer(1, null, null));
+ }
+ }
+}
diff --git a/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests.csproj
new file mode 100644
index 00000000..f8c639cb
--- /dev/null
+++ b/FreeSql.Tests/FreeSql.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netcoreapp2.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FreeSql.Tests/Generator/MySqlTemplateGeneratorTest.cs b/FreeSql.Tests/Generator/MySqlTemplateGeneratorTest.cs
new file mode 100644
index 00000000..ff0e085b
--- /dev/null
+++ b/FreeSql.Tests/Generator/MySqlTemplateGeneratorTest.cs
@@ -0,0 +1,27 @@
+using FreeSql.DataAnnotations;
+using FreeSql.Generator;
+using System;
+using Xunit;
+
+namespace FreeSql.Tests.Generator {
+ public class MySqlTemplateGeneratorTest {
+
+ [Fact]
+ public void BuildSimpleEntity() {
+ var gen = new TemplateGenerator();
+ gen.Build(g.mysql.DbFirst, @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity", @"C:\Users\28810\Desktop\新建文件夹 (9)", "cccddd");
+ }
+
+ [Fact]
+ public void BuildSimpleEntityNavigationObject () {
+ var gen = new TemplateGenerator();
+ gen.Build(g.mysql.DbFirst, @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity-navigation-object", @"C:\Users\28810\Desktop\新建文件夹 (9)", "cccddd");
+ }
+
+ [Fact]
+ public void BuildRichEntityNavigationObject() {
+ var gen = new TemplateGenerator();
+ gen.Build(g.mysql.DbFirst, @"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\rich-entity-navigation-object", @"C:\Users\28810\Desktop\新建文件夹 (9)", "cccddd");
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/Curd/MySqlDeleteTest.cs b/FreeSql.Tests/MySql/Curd/MySqlDeleteTest.cs
new file mode 100644
index 00000000..33b4c443
--- /dev/null
+++ b/FreeSql.Tests/MySql/Curd/MySqlDeleteTest.cs
@@ -0,0 +1,72 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlDeleteTest {
+
+ IDelete delete => g.mysql.Delete(); //��������
+
+ [Table(Name = "tb_topic")]
+ class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ }
+
+ [Fact]
+ public void Dywhere() {
+ Assert.Null(g.mysql.Delete().ToSql());
+ var sql = g.mysql.Delete(new[] { 1, 2 }).ToSql();
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2)", sql);
+
+ sql = g.mysql.Delete(new Topic { Id = 1, Title = "test" }).ToSql();
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` = 1)", sql);
+
+ sql = g.mysql.Delete(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).ToSql();
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` = 1 OR `Id` = 2)", sql);
+
+ sql = g.mysql.Delete(new { id = 1 }).ToSql();
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` = 1)", sql);
+ }
+
+ [Fact]
+ public void Where() {
+ var sql = delete.Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` = 1)", sql);
+
+ sql = delete.Where("id = ?id", new { id = 1 }).ToSql().Replace("\r\n", "");
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (id = ?id)", sql);
+
+ var item = new Topic { Id = 1, Title = "newtitle" };
+ sql = delete.Where(item).ToSql().Replace("\r\n", "");
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` = 1)", sql);
+
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ sql = delete.Where(items).ToSql().Replace("\r\n", "");
+ Assert.Equal("DELETE FROM `tb_topic` WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))", sql);
+ }
+ [Fact]
+ public void WhereExists() {
+
+ }
+ [Fact]
+ public void ExecuteAffrows() {
+
+ var id = g.mysql.Insert(new Topic { Title = "xxxx" }).ExecuteIdentity();
+ Assert.Equal(1, delete.Where(a => a.Id == id).ExecuteAffrows());
+ }
+ [Fact]
+ public void ExecuteDeleted() {
+
+ Assert.Throws(() => delete.Where(a => a.Id > 0).ExecuteDeleted());
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/Curd/MySqlInsertTest.cs b/FreeSql.Tests/MySql/Curd/MySqlInsertTest.cs
new file mode 100644
index 00000000..639af244
--- /dev/null
+++ b/FreeSql.Tests/MySql/Curd/MySqlInsertTest.cs
@@ -0,0 +1,85 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlInsertTest {
+
+ IInsert insert => g.mysql.Insert(); //��������
+
+ [Table(Name = "tb_topic")]
+ class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ }
+
+ [Fact]
+ public void AppendData() {
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ var sql = insert.AppendData(items.First()).ToSql();
+ Assert.Equal("INSERT INTO `tb_topic`(`Clicks`, `Title`, `CreateTime`) VALUES(?Clicks0, ?Title0, ?CreateTime0)", sql);
+
+ sql = insert.AppendData(items).ToSql();
+ Assert.Equal("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)", sql);
+
+ sql = insert.AppendData(items).InsertColumns(a => a.Title).ToSql();
+ Assert.Equal("INSERT INTO `tb_topic`(`Title`) VALUES(?Title0), (?Title1), (?Title2), (?Title3), (?Title4), (?Title5), (?Title6), (?Title7), (?Title8), (?Title9)", sql);
+
+ sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql();
+ Assert.Equal("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)", sql);
+ }
+
+ [Fact]
+ public void InsertColumns() {
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ var sql = insert.AppendData(items).InsertColumns(a => a.Title).ToSql();
+ Assert.Equal("INSERT INTO `tb_topic`(`Title`) VALUES(?Title0), (?Title1), (?Title2), (?Title3), (?Title4), (?Title5), (?Title6), (?Title7), (?Title8), (?Title9)", sql);
+
+ sql = insert.AppendData(items).InsertColumns(a =>new { a.Title, a.Clicks }).ToSql();
+ Assert.Equal("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)", sql);
+ }
+ [Fact]
+ public void IgnoreColumns() {
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ var sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql();
+ Assert.Equal("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)", sql);
+
+ sql = insert.AppendData(items).IgnoreColumns(a => new { a.Title, a.CreateTime }).ToSql();
+ Assert.Equal("INSERT INTO `tb_topic`(`Clicks`) VALUES(?Clicks0), (?Clicks1), (?Clicks2), (?Clicks3), (?Clicks4), (?Clicks5), (?Clicks6), (?Clicks7), (?Clicks8), (?Clicks9)", sql);
+ }
+ [Fact]
+ public void ExecuteAffrows() {
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ Assert.Equal(1, insert.AppendData(items.First()).ExecuteAffrows());
+ Assert.Equal(10, insert.AppendData(items).ExecuteAffrows());
+ }
+ [Fact]
+ public void ExecuteIdentity() {
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ Assert.NotEqual(0, insert.AppendData(items.First()).ExecuteIdentity());
+ }
+ [Fact]
+ public void ExecuteInserted() {
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ Assert.Throws(() => insert.AppendData(items.First()).ExecuteInserted());
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs b/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs
new file mode 100644
index 00000000..5e2f3e84
--- /dev/null
+++ b/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs
@@ -0,0 +1,501 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlSelectTest {
+
+ ISelect select => g.mysql.Select();
+
+ [Table(Name = "tb_topic")]
+ class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public int TestTypeInfoGuid { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ }
+ class TestTypeInfo {
+ public int Guid { get; set; }
+ public int ParentId { get; set; }
+ public TestTypeParentInfo Parent { get; set; }
+ public string Name { get; set; }
+ }
+ class TestTypeParentInfo {
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ public List Types { get; set; }
+ }
+
+ [Fact]
+ public void ToList() {
+ }
+ [Fact]
+ public void ToOne() {
+ }
+ [Fact]
+ public void ToSql() {
+ }
+ [Fact]
+ public void Any() {
+ var count = select.Where(a => 1 == 1).Count();
+ Assert.False(select.Where(a => 1 == 2).Any());
+ Assert.Equal(count > 0, select.Where(a => 1 == 1).Any());
+ }
+ [Fact]
+ public void Count() {
+ var count = select.Where(a => 1 == 1).Count();
+ select.Where(a => 1 == 1).Count(out var count2);
+ Assert.Equal(count, count2);
+ Assert.Equal(0, select.Where(a => 1 == 2).Count());
+ }
+ [Fact]
+ public void Master() {
+ Assert.StartsWith(" SELECT", select.Master().Where(a => 1 == 1).ToSql());
+ }
+ [Fact]
+ public void Caching() {
+ var result1 = select.Where(a => 1 == 1).Caching(20, "testcaching").ToList();
+ var testcaching1 = g.mysql.Cache.Get("testcaching");
+ Assert.NotNull(testcaching1);
+ var result2 = select.Where(a => 1 == 1).Caching(20, "testcaching").ToList();
+ var testcaching2 = g.mysql.Cache.Get("testcaching");
+ Assert.NotNull(testcaching2);
+ Assert.Equal(result1.Count, result1.Count);
+ }
+ [Fact]
+ public void From() {
+ //�������
+ var query2 = select.From((s, b, c) => s
+ .LeftJoin(a => a.TestTypeInfoGuid == b.Guid)
+ .LeftJoin(a => b.ParentId == c.Id)
+ );
+ var sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON a.`TestTypeInfoGuid` = b.`Guid` LEFT JOIN `TestTypeParentInfo` c ON b.`ParentId` = c.`Id`", sql);
+ query2.ToList();
+ }
+ [Fact]
+ public void LeftJoin() {
+ //����е�������a.Type��a.Type.Parent ���ǵ�������
+ var query = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid);
+ var sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid`", sql);
+ query.ToList();
+
+ query = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid && a.Type.Name == "xxx");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` AND a__Type.`Name` = 'xxx'", sql);
+ query.ToList();
+
+ query = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeParentInfo` a__Type__Parent ON 1 = 1 LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` AND a__Type.`Name` = 'xxx' WHERE (a__Type__Parent.`Id` = 10)", sql);
+ query.ToList();
+
+ //���û�е�������
+ query = select.LeftJoin((a, b) => b.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid`", sql);
+ query.ToList();
+
+ query = select.LeftJoin((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "xxx");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'xxx'", sql);
+ query.ToList();
+
+ query = select.LeftJoin((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "xxx").Where(a => a.Type.Parent.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeParentInfo` b__Parent ON 1 = 1 LEFT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'xxx' WHERE (b__Parent.`Id` = 10)", sql);
+ query.ToList();
+
+ //�������
+ query = select
+ .LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid)
+ .LeftJoin(a => a.Type.Parent.Id == a.Type.ParentId);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 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`", sql);
+ query.ToList();
+
+ query = select
+ .LeftJoin((a, b) => b.Guid == a.TestTypeInfoGuid)
+ .LeftJoin((a, c) => c.Id == a.Type.ParentId);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` LEFT JOIN `TestTypeParentInfo` c ON c.`Id` = b.`ParentId`", sql);
+ query.ToList();
+
+ //���û�е�������b��c������ϵ
+ var query2 = select.From((s, b, c) => s
+ .LeftJoin(a => a.TestTypeInfoGuid == b.Guid)
+ .LeftJoin(a => b.ParentId == c.Id));
+ sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeInfo` b ON a.`TestTypeInfoGuid` = b.`Guid` LEFT JOIN `TestTypeParentInfo` c ON b.`ParentId` = c.`Id`", sql);
+ query2.ToList();
+
+ //������϶����㲻��
+ query = select.LeftJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a LEFT JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid", sql);
+ query.ToList();
+
+ query = select.LeftJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", new { bname = "xxx" });
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a LEFT JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", sql);
+ query.ToList();
+ }
+ [Fact]
+ public void InnerJoin() {
+ //����е�������a.Type��a.Type.Parent ���ǵ�������
+ var query = select.InnerJoin(a => a.Type.Guid == a.TestTypeInfoGuid);
+ var sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid`", sql);
+ query.ToList();
+
+ query = select.InnerJoin(a => a.Type.Guid == a.TestTypeInfoGuid && a.Type.Name == "xxx");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` AND a__Type.`Name` = 'xxx'", sql);
+ query.ToList();
+
+ query = select.InnerJoin(a => a.Type.Guid == a.TestTypeInfoGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeParentInfo` a__Type__Parent ON 1 = 1 INNER JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` AND a__Type.`Name` = 'xxx' WHERE (a__Type__Parent.`Id` = 10)", sql);
+ query.ToList();
+
+ //���û�е�������
+ query = select.InnerJoin((a, b) => b.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid`", sql);
+ query.ToList();
+
+ query = select.InnerJoin((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "xxx");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'xxx'", sql);
+ query.ToList();
+
+ query = select.InnerJoin((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "xxx").Where(a => a.Type.Parent.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeParentInfo` b__Parent ON 1 = 1 INNER JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'xxx' WHERE (b__Parent.`Id` = 10)", sql);
+ query.ToList();
+
+ //�������
+ query = select
+ .InnerJoin(a => a.Type.Guid == a.TestTypeInfoGuid)
+ .InnerJoin(a => a.Type.Parent.Id == a.Type.ParentId);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` INNER JOIN `TestTypeParentInfo` a__Type__Parent ON a__Type__Parent.`Id` = a__Type.`ParentId`", sql);
+ query.ToList();
+
+ query = select
+ .InnerJoin((a, b) => b.Guid == a.TestTypeInfoGuid)
+ .InnerJoin((a, c) => c.Id == a.Type.ParentId);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` INNER JOIN `TestTypeParentInfo` c ON c.`Id` = b.`ParentId`", sql);
+ query.ToList();
+
+ //���û�е�������b��c������ϵ
+ var query2 = select.From((s, b, c) => s
+ .InnerJoin(a => a.TestTypeInfoGuid == b.Guid)
+ .InnerJoin(a => b.ParentId == c.Id));
+ sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a INNER JOIN `TestTypeInfo` b ON a.`TestTypeInfoGuid` = b.`Guid` INNER JOIN `TestTypeParentInfo` c ON b.`ParentId` = c.`Id`", sql);
+ query2.ToList();
+
+ //������϶����㲻��
+ query = select.InnerJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a INNER JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid", sql);
+ query.ToList();
+
+ query = select.InnerJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", new { bname = "xxx" });
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a INNER JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", sql);
+ query.ToList();
+
+ }
+ [Fact]
+ public void RightJoin() {
+ //����е�������a.Type��a.Type.Parent ���ǵ�������
+ var query = select.RightJoin(a => a.Type.Guid == a.TestTypeInfoGuid);
+ var sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid`", sql);
+ query.ToList();
+
+ query = select.RightJoin(a => a.Type.Guid == a.TestTypeInfoGuid && a.Type.Name == "xxx");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` AND a__Type.`Name` = 'xxx'", sql);
+ query.ToList();
+
+ query = select.RightJoin(a => a.Type.Guid == a.TestTypeInfoGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeParentInfo` a__Type__Parent ON 1 = 1 RIGHT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` AND a__Type.`Name` = 'xxx' WHERE (a__Type__Parent.`Id` = 10)", sql);
+ query.ToList();
+
+ //���û�е�������
+ query = select.RightJoin((a, b) => b.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid`", sql);
+ query.ToList();
+
+ query = select.RightJoin((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "xxx");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'xxx'", sql);
+ query.ToList();
+
+ query = select.RightJoin((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "xxx").Where(a => a.Type.Parent.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a LEFT JOIN `TestTypeParentInfo` b__Parent ON 1 = 1 RIGHT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'xxx' WHERE (b__Parent.`Id` = 10)", sql);
+ query.ToList();
+
+ //�������
+ query = select
+ .RightJoin(a => a.Type.Guid == a.TestTypeInfoGuid)
+ .RightJoin(a => a.Type.Parent.Id == a.Type.ParentId);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` RIGHT JOIN `TestTypeParentInfo` a__Type__Parent ON a__Type__Parent.`Id` = a__Type.`ParentId`", sql);
+ query.ToList();
+
+ query = select
+ .RightJoin((a, b) => b.Guid == a.TestTypeInfoGuid)
+ .RightJoin((a, c) => c.Id == a.Type.ParentId);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` b ON b.`Guid` = a.`TestTypeInfoGuid` RIGHT JOIN `TestTypeParentInfo` c ON c.`Id` = b.`ParentId`", sql);
+ query.ToList();
+
+ //���û�е�������b��c������ϵ
+ var query2 = select.From((s, b, c) => s
+ .RightJoin(a => a.TestTypeInfoGuid == b.Guid)
+ .RightJoin(a => b.ParentId == c.Id));
+ sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a RIGHT JOIN `TestTypeInfo` b ON a.`TestTypeInfoGuid` = b.`Guid` RIGHT JOIN `TestTypeParentInfo` c ON b.`ParentId` = c.`Id`", sql);
+ query2.ToList();
+
+ //������϶����㲻��
+ query = select.RightJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a RIGHT JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid", sql);
+ query.ToList();
+
+ query = select.RightJoin("TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", new { bname = "xxx" });
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a RIGHT JOIN TestTypeInfo b on b.Guid = a.TestTypeInfoGuid and b.Name = ?bname", sql);
+ query.ToList();
+
+ }
+ [Fact]
+ public void Where() {
+ //����е�������a.Type��a.Type.Parent ���ǵ�������
+ var query = select.Where(a => a.Id == 10);
+ var sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.`Id` = 10)", sql);
+ query.ToList();
+
+ query = select.Where(a => a.Id == 10 && a.Id > 10 || a.Clicks > 100);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.`Id` = 10 AND a.`Id` > 10 OR a.`Clicks` > 100)", sql);
+ query.ToList();
+
+ query = select.Where(a => a.Id == 10).Where(a => a.Clicks > 100);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.`Id` = 10) AND (a.`Clicks` > 100)", sql);
+ query.ToList();
+
+ query = select.Where(a => a.Type.Name == "typeTitle");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` a__Type WHERE (a__Type.`Name` = 'typeTitle')", sql);
+ query.ToList();
+
+ query = select.Where(a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` a__Type WHERE (a__Type.`Name` = 'typeTitle' AND a__Type.`Guid` = a.`TestTypeInfoGuid`)", sql);
+ query.ToList();
+
+ query = select.Where(a => a.Type.Parent.Name == "tparent");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` a__Type, `TestTypeParentInfo` a__Type__Parent WHERE (a__Type__Parent.`Name` = 'tparent')", sql);
+ query.ToList();
+
+ //���û�е������ԣ��������
+ query = select.Where((a, b) => b.Guid == a.TestTypeInfoGuid && b.Name == "typeTitle");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` b WHERE (b.`Guid` = a.`TestTypeInfoGuid` AND b.`Name` = 'typeTitle')", sql);
+ query.ToList();
+
+ query = select.Where((a, b) => b.Name == "typeTitle" && b.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` b WHERE (b.`Name` = 'typeTitle' AND b.`Guid` = a.`TestTypeInfoGuid`)", sql);
+ query.ToList();
+
+ query = select.Where((a, b, c) => c.Name == "tparent");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a, `TestTypeParentInfo` c WHERE (c.`Name` = 'tparent')", sql);
+ query.ToList();
+
+ //����һ�� From ��Ķ������
+ var query2 = select.From((s, b, c) => s
+ .Where(a => a.Id == 10 && c.Name == "xxx")
+ .Where(a => b.ParentId == 20));
+ sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeParentInfo` c, `TestTypeInfo` b WHERE (a.`Id` = 10 AND c.`Name` = 'xxx') AND (b.`ParentId` = 20)", sql);
+ query2.ToList();
+
+ //������϶����㲻��
+ query = select.Where("a.clicks > 100 && a.id = ?id", new { id = 10 });
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.clicks > 100 && a.id = ?id)", sql);
+ query.ToList();
+ }
+ [Fact]
+ public void WhereIf() {
+ //����е�������a.Type��a.Type.Parent ���ǵ�������
+ var query = select.WhereIf(true, a => a.Id == 10);
+ var sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.`Id` = 10)", sql);
+ query.ToList();
+
+ query = select.WhereIf(true, a => a.Id == 10 && a.Id > 10 || a.Clicks > 100);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.`Id` = 10 AND a.`Id` > 10 OR a.`Clicks` > 100)", sql);
+ query.ToList();
+
+ query = select.WhereIf(true, a => a.Id == 10).WhereIf(true, a => a.Clicks > 100);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.`Id` = 10) AND (a.`Clicks` > 100)", sql);
+ query.ToList();
+
+ query = select.WhereIf(true, a => a.Type.Name == "typeTitle");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` a__Type WHERE (a__Type.`Name` = 'typeTitle')", sql);
+ query.ToList();
+
+ query = select.WhereIf(true, a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` a__Type WHERE (a__Type.`Name` = 'typeTitle' AND a__Type.`Guid` = a.`TestTypeInfoGuid`)", sql);
+ query.ToList();
+
+ query = select.WhereIf(true, a => a.Type.Parent.Name == "tparent");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeInfo` a__Type, `TestTypeParentInfo` a__Type__Parent WHERE (a__Type__Parent.`Name` = 'tparent')", sql);
+ query.ToList();
+
+ //����һ�� From ��Ķ������
+ var query2 = select.From((s, b, c) => s
+ .WhereIf(true, a => a.Id == 10 && c.Name == "xxx")
+ .WhereIf(true, a => b.ParentId == 20));
+ sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, b.`Guid` as4, b.`ParentId` as5, b.`Name` as6, a.`Title` as7, a.`CreateTime` as8 FROM `tb_topic` a, `TestTypeParentInfo` c, `TestTypeInfo` b WHERE (a.`Id` = 10 AND c.`Name` = 'xxx') AND (b.`ParentId` = 20)", sql);
+ query2.ToList();
+
+ //������϶����㲻��
+ query = select.WhereIf(true, "a.clicks > 100 && a.id = ?id", new { id = 10 });
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a WHERE (a.clicks > 100 && a.id = ?id)", sql);
+ query.ToList();
+
+ // ==========================================WhereIf(false)
+
+ //����е�������a.Type��a.Type.Parent ���ǵ�������
+ query = select.WhereIf(false, a => a.Id == 10);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+
+ query = select.WhereIf(false, a => a.Id == 10 && a.Id > 10 || a.Clicks > 100);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+
+ query = select.WhereIf(false, a => a.Id == 10).WhereIf(false, a => a.Clicks > 100);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+
+ query = select.WhereIf(false, a => a.Type.Name == "typeTitle");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+
+ query = select.WhereIf(false, a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TestTypeInfoGuid);
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+
+ query = select.WhereIf(false, a => a.Type.Parent.Name == "tparent");
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+
+ //����һ�� From ��Ķ������
+ query2 = select.From((s, b, c) => s
+ .WhereIf(false, a => a.Id == 10 && c.Name == "xxx")
+ .WhereIf(false, a => b.ParentId == 20));
+ sql = query2.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query2.ToList();
+
+ //������϶����㲻��
+ query = select.WhereIf(false, "a.clicks > 100 && a.id = ?id", new { id = 10 });
+ sql = query.ToSql().Replace("\r\n", "");
+ Assert.Equal("SELECT a.`Id` as1, a.`Clicks` as2, a.`TestTypeInfoGuid` as3, a.`Title` as4, a.`CreateTime` as5 FROM `tb_topic` a", sql);
+ query.ToList();
+ }
+ [Fact]
+ public void WhereLike() {
+ //ģ����ѯ��WhereLike(a => a.Title, "%sql")
+ var query = select.Where(a => a.Title.StartsWith("ss")).Where(a => a.Type.Name.Contains("sss"));
+ var sql = query.ToSql().Replace("\r\n", "");
+
+ query = select.Where(a => a.Title.EndsWith("ss"));
+ sql = query.ToSql().Replace("\r\n", "");
+
+ query = select.Where(a => a.Title.Contains("ss"));
+ sql = query.ToSql().Replace("\r\n", "");
+
+ query = select.WhereLike(a => a.Title, "%ss");
+ sql = query.ToSql().Replace("\r\n", "");
+
+ query = select.WhereLike(a => a.Title, "%ss").WhereLike(a => a.Title, "%aa");
+ sql = query.ToSql().Replace("\r\n", "");
+
+ //ģ����ѯ��ѡ������ OR��WhereLike(a => new[] { a.Title, a.Content }, "%sql%")
+ query = select.WhereLike(a => new[] { a.Title, a.Type.Name, a.Type.Parent.Name }, "%aa");
+ sql = query.ToSql().Replace("\r\n", "");
+ }
+ [Fact]
+ public void GroupBy() {
+ }
+ [Fact]
+ public void Having() {
+ }
+ [Fact]
+ public void OrderBy() {
+ }
+ [Fact]
+ public void Skip_Offset() {
+ }
+ [Fact]
+ public void Take_Limit() {
+ }
+ [Fact]
+ public void Page() {
+ }
+ [Fact]
+ public void Sum() {
+ }
+ [Fact]
+ public void Min() {
+ }
+ [Fact]
+ public void Max() {
+ }
+ [Fact]
+ public void Avg() {
+ }
+ [Fact]
+ public void As() {
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/Curd/MySqlUpdateTest.cs b/FreeSql.Tests/MySql/Curd/MySqlUpdateTest.cs
new file mode 100644
index 00000000..ca433072
--- /dev/null
+++ b/FreeSql.Tests/MySql/Curd/MySqlUpdateTest.cs
@@ -0,0 +1,107 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlUpdateTest {
+ IUpdate update => g.mysql.Update();
+
+ [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; }
+ }
+
+ [Fact]
+ public void Dywhere() {
+ Assert.Null(g.mysql.Update().ToSql());
+ Assert.Equal("UPDATE `tb_topic` SET title='test' \r\nWHERE (`Id` = 1 OR `Id` = 2)", g.mysql.Update(new[] { 1, 2 }).SetRaw("title='test'").ToSql());
+ Assert.Equal("UPDATE `tb_topic` SET title='test1' \r\nWHERE (`Id` = 1)", g.mysql.Update(new Topic { Id = 1, Title = "test" }).SetRaw("title='test1'").ToSql());
+ Assert.Equal("UPDATE `tb_topic` SET title='test1' \r\nWHERE (`Id` = 1 OR `Id` = 2)", g.mysql.Update(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).SetRaw("title='test1'").ToSql());
+ Assert.Equal("UPDATE `tb_topic` SET title='test1' \r\nWHERE (`Id` = 1)", g.mysql.Update(new { id = 1 }).SetRaw("title='test1'").ToSql());
+ }
+
+ [Fact]
+ public void SetSource() {
+ var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Clicks` = ?p_0, `Title` = ?p_1, `CreateTime` = ?p_2 WHERE (`Id` = 1)", sql);
+
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+
+ sql = update.SetSource(items).ToSql().Replace("\r\n", "");
+ Assert.Equal("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))", sql);
+
+ sql = update.SetSource(items).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql().Replace("\r\n", "");
+ Assert.Equal("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))", sql);
+
+ sql = update.SetSource(items).Set(a => a.CreateTime, new DateTime(2020,1,1)).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `CreateTime` = ?p_0 WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))", sql);
+ }
+ [Fact]
+ public void IgnoreColumns() {
+ var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Title` = ?p_0 WHERE (`Id` = 1)", sql);
+ }
+ [Fact]
+ public void Set() {
+ var sql = update.Where(a => a.Id == 1).Set(a => a.Title, "newtitle").ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Title` = ?p_0 WHERE (`Id` = 1)", sql);
+
+ sql = update.Where(a => a.Id == 1).Set(a => a.Title, "newtitle").Set(a => a.CreateTime, new DateTime(2020, 1, 1)).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Title` = ?p_0, `CreateTime` = ?p_1 WHERE (`Id` = 1)", sql);
+
+ sql = update.Set(a => a.Clicks * 10 / 1).Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Clicks` = ifnull(`Clicks`, 0) * 10 / 1 WHERE (`Id` = 1)", sql);
+
+ sql = update.Set(a => a.Id - 10).Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Id` = `Id` - 10 WHERE (`Id` = 1)", sql);
+
+ int incrv = 10;
+ sql = update.Set(a => a.Clicks * incrv / 1).Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Clicks` = ifnull(`Clicks`, 0) * 10 / 1 WHERE (`Id` = 1)", sql);
+
+ sql = update.Set(a => a.Id - incrv).Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET `Id` = `Id` - 10 WHERE (`Id` = 1)", sql);
+ }
+ [Fact]
+ public void SetRaw() {
+ var sql = update.Where(a => a.Id == 1).SetRaw("clicks = clicks + ?incrClick", new { incrClick = 1 }).ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET clicks = clicks + ?incrClick WHERE (`Id` = 1)", sql);
+ }
+ [Fact]
+ public void Where() {
+ var sql = update.Where(a => a.Id == 1).SetRaw("title='newtitle'").ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET title='newtitle' WHERE (`Id` = 1)", sql);
+
+ sql = update.Where("id = ?id", new { id = 1 }).SetRaw("title='newtitle'").ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET title='newtitle' WHERE (id = ?id)", sql);
+
+ var item = new Topic { Id = 1, Title = "newtitle" };
+ sql = update.Where(item).SetRaw("title='newtitle'").ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET title='newtitle' WHERE (`Id` = 1)", sql);
+
+ var items = new List();
+ for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
+ sql = update.Where(items).SetRaw("title='newtitle'").ToSql().Replace("\r\n", "");
+ Assert.Equal("UPDATE `tb_topic` SET title='newtitle' WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))", sql);
+ }
+ [Fact]
+ public void WhereExists() {
+
+ }
+ [Fact]
+ public void ExecuteAffrows() {
+
+ }
+ [Fact]
+ public void ExecuteUpdated() {
+
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/MySqlAdo/MySqlAdoTest.cs b/FreeSql.Tests/MySql/MySqlAdo/MySqlAdoTest.cs
new file mode 100644
index 00000000..83812d4f
--- /dev/null
+++ b/FreeSql.Tests/MySql/MySqlAdo/MySqlAdoTest.cs
@@ -0,0 +1,54 @@
+using FreeSql.DataAnnotations;
+using System;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlAdoTest {
+ [Fact]
+ public void Pool() {
+ var t1 = g.mysql.Ado.MasterPool.StatisticsFullily;
+ }
+
+ [Fact]
+ public void SlavePools() {
+ var t2 = g.mysql.Ado.SlavePools.Count;
+ }
+
+ [Fact]
+ public void IsTracePerformance() {
+ Assert.True(g.mysql.Ado.IsTracePerformance);
+ }
+
+ [Fact]
+ public void ExecuteReader() {
+
+ }
+ [Fact]
+ public void ExecuteArray() {
+
+ }
+ [Fact]
+ public void ExecuteNonQuery() {
+
+ }
+ [Fact]
+ public void ExecuteScalar() {
+
+ }
+
+ [Fact]
+ public void Query() {
+ var t3 = g.mysql.Ado.Query("select * from song");
+
+ var t4 = g.mysql.Ado.Query<(int, string, string)>("select * from song");
+
+ var t5 = g.mysql.Ado.Query("select * from song");
+ }
+
+ class xxx {
+ public int Id { get; set; }
+ public string Path { get; set; }
+ public string Title2 { get; set; }
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/MySqlCodeFirstTest.cs b/FreeSql.Tests/MySql/MySqlCodeFirstTest.cs
new file mode 100644
index 00000000..4245a27d
--- /dev/null
+++ b/FreeSql.Tests/MySql/MySqlCodeFirstTest.cs
@@ -0,0 +1,350 @@
+using FreeSql.DataAnnotations;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlCodeFirstTest {
+ [Fact]
+ public void GetComparisonDDLStatements() {
+
+ var sql = g.mysql.CodeFirst.GetComparisonDDLStatements();
+ if (string.IsNullOrEmpty(sql) == false) {
+ Assert.Equal(@"CREATE TABLE IF NOT EXISTS `cccddd`.`tb_alltype` (
+ `Id` INT(11) NOT NULL AUTO_INCREMENT,
+ `testFieldBool` BIT(1) NOT NULL,
+ `testFieldSByte` TINYINT(3) NOT NULL,
+ `testFieldShort` SMALLINT(6) NOT NULL,
+ `testFieldInt` INT(11) NOT NULL,
+ `testFieldLong` BIGINT(20) NOT NULL,
+ `testFieldByte` TINYINT(3) UNSIGNED NOT NULL,
+ `testFieldUShort` SMALLINT(5) UNSIGNED NOT NULL,
+ `testFieldUInt` INT(10) UNSIGNED NOT NULL,
+ `testFieldULong` BIGINT(20) UNSIGNED NOT NULL,
+ `testFieldDouble` DOUBLE NOT NULL,
+ `testFieldFloat` FLOAT NOT NULL,
+ `testFieldDecimal` DECIMAL(10,2) NOT NULL,
+ `testFieldTimeSpan` TIME NOT NULL,
+ `testFieldDateTime` DATETIME NOT NULL,
+ `testFieldBytes` VARBINARY(255),
+ `testFieldString` VARCHAR(255),
+ `testFieldGuid` VARCHAR(36),
+ `testFieldBoolNullable` BIT(1),
+ `testFieldSByteNullable` TINYINT(3),
+ `testFieldShortNullable` SMALLINT(6),
+ `testFieldIntNullable` INT(11),
+ `testFielLongNullable` BIGINT(20),
+ `testFieldByteNullable` TINYINT(3) UNSIGNED,
+ `testFieldUShortNullable` SMALLINT(5) UNSIGNED,
+ `testFieldUIntNullable` INT(10) UNSIGNED,
+ `testFieldULongNullable` BIGINT(20) UNSIGNED,
+ `testFieldDoubleNullable` DOUBLE,
+ `testFieldFloatNullable` FLOAT,
+ `testFieldDecimalNullable` DECIMAL(10,2),
+ `testFieldTimeSpanNullable` TIME,
+ `testFieldDateTimeNullable` DATETIME,
+ `testFieldGuidNullable` VARCHAR(36),
+ `testFieldPoint` POINT,
+ `testFieldLineString` LINESTRING,
+ `testFieldPolygon` POLYGON,
+ `testFieldMultiPoint` MULTIPOINT,
+ `testFieldMultiLineString` MULTILINESTRING,
+ `testFieldMultiPolygon` MULTIPOLYGON,
+ `testFieldEnum1` ENUM('E1','E2','E3') NOT NULL,
+ `testFieldEnum1Nullable` ENUM('E1','E2','E3'),
+ `testFieldEnum2` SET('F1','F2','F3') NOT NULL,
+ `testFieldEnum2Nullable` SET('F1','F2','F3'),
+ PRIMARY KEY (`Id`)
+) Engine=InnoDB CHARACTER SET utf8;
+", sql);
+ }
+
+ sql = g.mysql.CodeFirst.GetComparisonDDLStatements();
+ }
+
+
+ [JsonObject(MemberSerialization.OptIn), Table(Name = "tb_alltype")]
+ public partial class Tb_alltype {
+
+ [JsonProperty, Column(Name = "Id", DbType = "int(11)", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldBool", DbType = "bit(1)")]
+ public bool TestFieldBool { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldBoolNullable", DbType = "bit(1)", IsNullable = true)]
+ public bool? TestFieldBoolNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldByte", DbType = "tinyint(3) unsigned")]
+ public byte TestFieldByte { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldByteNullable", DbType = "tinyint(3) unsigned", IsNullable = true)]
+ public byte? TestFieldByteNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldBytes", DbType = "varbinary(255)", IsNullable = true)]
+ public byte[] TestFieldBytes { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldDateTime", DbType = "datetime")]
+ public DateTime TestFieldDateTime { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldDateTimeNullable", DbType = "datetime", IsNullable = true)]
+ public DateTime? TestFieldDateTimeNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldDecimal", DbType = "decimal(10,2)")]
+ public decimal TestFieldDecimal { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldDecimalNullable", DbType = "decimal(10,2)", IsNullable = true)]
+ public decimal? TestFieldDecimalNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldDouble", DbType = "double")]
+ public double TestFieldDouble { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldDoubleNullable", DbType = "double", IsNullable = true)]
+ public double? TestFieldDoubleNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldEnum1", DbType = "enum('E1','E2','E3','E5')")]
+ public Tb_alltypeTESTFIELDENUM1 TestFieldEnum1 { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldEnum1Nullable", DbType = "enum('E1','E2','E3','E5')", IsNullable = true)]
+ public Tb_alltypeTESTFIELDENUM1NULLABLE? TestFieldEnum1Nullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldEnum2", DbType = "set('F1','F2','F3')")]
+ public Tb_alltypeTESTFIELDENUM2 TestFieldEnum2 { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldEnum2Nullable", DbType = "set('F1','F2','F3')", IsNullable = true)]
+ public Tb_alltypeTESTFIELDENUM2NULLABLE? TestFieldEnum2Nullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldFloat", DbType = "float")]
+ public float TestFieldFloat { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldFloatNullable", DbType = "float", IsNullable = true)]
+ public float? TestFieldFloatNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldGuid", DbType = "char(36)")]
+ public Guid TestFieldGuid { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldGuidNullable", DbType = "char(36)", IsNullable = true)]
+ public Guid? TestFieldGuidNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldInt", DbType = "int(11)")]
+ public int TestFieldInt { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldIntNullable", DbType = "int(11)", IsNullable = true)]
+ public int? TestFieldIntNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldLineString", DbType = "linestring", IsNullable = true)]
+ public MygisGeometry TestFieldLineString { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldLong", DbType = "bigint(20)")]
+ public long TestFieldLong { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldMultiLineString", DbType = "multilinestring", IsNullable = true)]
+ public MygisGeometry TestFieldMultiLineString { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldMultiPoint", DbType = "multipoint", IsNullable = true)]
+ public MygisGeometry TestFieldMultiPoint { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldMultiPolygon", DbType = "multipolygon", IsNullable = true)]
+ public MygisGeometry TestFieldMultiPolygon { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldPoint", DbType = "point", IsNullable = true)]
+ public MygisGeometry TestFieldPoint { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldPolygon", DbType = "polygon", IsNullable = true)]
+ public MygisGeometry TestFieldPolygon { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldSByte", DbType = "tinyint(3)")]
+ public sbyte TestFieldSByte { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldSByteNullable", DbType = "tinyint(3)", IsNullable = true)]
+ public sbyte? TestFieldSByteNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldShort", DbType = "smallint(6)")]
+ public short TestFieldShort { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldShortNullable", DbType = "smallint(6)", IsNullable = true)]
+ public short? TestFieldShortNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldString", DbType = "varchar(255)", IsNullable = true)]
+ public string TestFieldString { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldTimeSpan", DbType = "time")]
+ public TimeSpan TestFieldTimeSpan { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldTimeSpanNullable", DbType = "time", IsNullable = true)]
+ public TimeSpan? TestFieldTimeSpanNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldUInt", DbType = "int(10) unsigned")]
+ public uint TestFieldUInt { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldUIntNullable", DbType = "int(10) unsigned", IsNullable = true)]
+ public uint? TestFieldUIntNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldULong", DbType = "bigint(20) unsigned")]
+ public ulong TestFieldULong { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldULongNullable", DbType = "bigint(20) unsigned", IsNullable = true)]
+ public ulong? TestFieldULongNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldUShort", DbType = "smallint(5) unsigned")]
+ public ushort TestFieldUShort { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFieldUShortNullable", DbType = "smallint(5) unsigned", IsNullable = true)]
+ public ushort? TestFieldUShortNullable { get; set; }
+
+
+ [JsonProperty, Column(Name = "testFielLongNullable", DbType = "bigint(20)", IsNullable = true)]
+ public long? TestFielLongNullable { get; set; }
+
+ internal static IFreeSql mysql => null;
+ public static FreeSql.ISelect Select => mysql.Select();
+
+ public static int ItemCacheTimeout = 180;
+ public static Tb_alltype GetItem(int Id) => Select.Where(a => a.Id == Id).Caching(ItemCacheTimeout, string.Concat("test:tb_alltype:", Id)).ToOne();
+
+ public static long Delete(int Id) {
+ var affrows = mysql.Delete().Where(a => a.Id == Id).ExecuteAffrows();
+ if (ItemCacheTimeout > 0) RemoveCache(new Tb_alltype { Id = Id });
+ return affrows;
+ }
+
+ internal static void RemoveCache(Tb_alltype item) => RemoveCache(item == null ? null : new[] { item });
+ internal static void RemoveCache(IEnumerable items) {
+ if (ItemCacheTimeout <= 0 || items == null || items.Any() == false) return;
+ var keys = new string[items.Count() * 1];
+ var keysIdx = 0;
+ foreach (var item in items) {
+ keys[keysIdx++] = string.Concat("test:tb_alltype:", item.Id);
+ }
+ if (mysql.Ado.TransactionCurrentThread != null) mysql.Ado.TransactionPreRemoveCache(keys);
+ else mysql.Cache.Remove(keys);
+ }
+
+ ///
+ /// ӣֵ UpdateӰΪ 0 Insert
+ ///
+ public void Save() {
+ if (this.Id != default(int)) {
+ var affrows = mysql.Update().Where(a => a.Id == Id).ExecuteAffrows();
+ if (affrows > 0) return;
+ }
+ this.Id = (int)mysql.Insert().AppendData(this).ExecuteIdentity();
+ }
+
+ }
+
+ public enum Tb_alltypeTESTFIELDENUM1 {
+ E1 = 1, E2, E3, E5
+ }
+ public enum Tb_alltypeTESTFIELDENUM1NULLABLE {
+ E1 = 1, E2, E3, E5
+ }
+ [Flags]
+ public enum Tb_alltypeTESTFIELDENUM2 : long {
+ F1 = 1, F2 = 2, F3 = 4
+ }
+ [Flags]
+ public enum Tb_alltypeTESTFIELDENUM2NULLABLE : long {
+ F1 = 1, F2 = 2, F3 = 4
+ }
+
+
+ [Table(Name = "tb_alltype")]
+ class TableAllType {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+
+ public bool testFieldBool { get; set; }
+ public sbyte testFieldSByte { get; set; }
+ public short testFieldShort { get; set; }
+ public int testFieldInt { get; set; }
+ public long testFieldLong { get; set; }
+ public byte testFieldByte { get; set; }
+ public ushort testFieldUShort { get; set; }
+ public uint testFieldUInt { get; set; }
+ public ulong testFieldULong { get; set; }
+ public double testFieldDouble { get; set; }
+ public float testFieldFloat { get; set; }
+ public decimal testFieldDecimal { get; set; }
+ public TimeSpan testFieldTimeSpan { get; set; }
+ public DateTime testFieldDateTime { get; set; }
+ public byte[] testFieldBytes { get; set; }
+ public string testFieldString { get; set; }
+ public Guid testFieldGuid { get; set; }
+
+ public bool? testFieldBoolNullable { get; set; }
+ public sbyte? testFieldSByteNullable { get; set; }
+ public short? testFieldShortNullable { get; set; }
+ public int? testFieldIntNullable { get; set; }
+ public long? testFielLongNullable { get; set; }
+ public byte? testFieldByteNullable { get; set; }
+ public ushort? testFieldUShortNullable { get; set; }
+ public uint? testFieldUIntNullable { get; set; }
+ public ulong? testFieldULongNullable { get; set; }
+ public double? testFieldDoubleNullable { get; set; }
+ public float? testFieldFloatNullable { get; set; }
+ public decimal? testFieldDecimalNullable { get; set; }
+ public TimeSpan? testFieldTimeSpanNullable { get; set; }
+ public DateTime? testFieldDateTimeNullable { get; set; }
+ public Guid? testFieldGuidNullable { get; set; }
+
+ public MygisPoint testFieldPoint { get; set; }
+ public MygisLineString testFieldLineString { get; set; }
+ public MygisPolygon testFieldPolygon { get; set; }
+ public MygisMultiPoint testFieldMultiPoint { get; set; }
+ public MygisMultiLineString testFieldMultiLineString { get; set; }
+ public MygisMultiPolygon testFieldMultiPolygon { get; set; }
+
+ public TableAllTypeEnumType1 testFieldEnum1 { get; set; }
+ public TableAllTypeEnumType1? testFieldEnum1Nullable { get; set; }
+ public TableAllTypeEnumType2 testFieldEnum2 { get; set; }
+ public TableAllTypeEnumType2? testFieldEnum2Nullable { get; set; }
+ }
+
+ public enum TableAllTypeEnumType1 { e1, e2, e3, e5 }
+ [Flags] public enum TableAllTypeEnumType2 { f1, f2, f3 }
+ }
+}
diff --git a/FreeSql.Tests/MySql/MySqlDbFirstTest.cs b/FreeSql.Tests/MySql/MySqlDbFirstTest.cs
new file mode 100644
index 00000000..cff6585d
--- /dev/null
+++ b/FreeSql.Tests/MySql/MySqlDbFirstTest.cs
@@ -0,0 +1,21 @@
+using FreeSql.DataAnnotations;
+using System;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlDbFirstTest {
+ [Fact]
+ public void GetDatabases() {
+
+ var t1 = g.mysql.DbFirst.GetDatabases();
+
+ }
+
+ [Fact]
+ public void GetTablesByDatabase() {
+
+ var t2 = g.mysql.DbFirst.GetTablesByDatabase(g.mysql.DbFirst.GetDatabases()[0]);
+
+ }
+ }
+}
diff --git a/FreeSql.Tests/MySql/MySqlExpressionTest.cs b/FreeSql.Tests/MySql/MySqlExpressionTest.cs
new file mode 100644
index 00000000..b0fdfd10
--- /dev/null
+++ b/FreeSql.Tests/MySql/MySqlExpressionTest.cs
@@ -0,0 +1,82 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace FreeSql.Tests.MySql {
+ public class MySqlExpressionTest {
+
+ ISelect select => g.mysql.Select();
+
+ [Table(Name = "tb_topic")]
+ class Topic {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int Clicks { get; set; }
+ public int TestTypeInfoGuid { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ }
+ class TestTypeInfo {
+ public int Guid { get; set; }
+ public int ParentId { get; set; }
+ public TestTypeParentInfo Parent { get; set; }
+ public string Name { get; set; }
+ }
+ class TestTypeParentInfo {
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ public List Types { get; set; }
+ }
+
+ [Fact]
+ public void StartsWith() {
+ }
+ [Fact]
+ public void EndsWith() {
+ }
+ [Fact]
+ public void Contains() {
+ }
+ [Fact]
+ public void ToLower() {
+ }
+ [Fact]
+ public void ToUpper() {
+
+ }
+ [Fact]
+ public void Substring() {
+ }
+ [Fact]
+ public void Length() {
+ }
+ [Fact]
+ public void IndexOf() {
+ }
+ [Fact]
+ public void PadLeft() {
+ }
+ [Fact]
+ public void PadRight() {
+ }
+ [Fact]
+ public void Trim() {
+ }
+ [Fact]
+ public void TrimStart() {
+ }
+ [Fact]
+ public void TrimEnd() {
+ }
+ [Fact]
+ public void Replace() {
+ }
+ [Fact]
+ public void CompareTo() {
+ }
+ }
+}
diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs
new file mode 100644
index 00000000..c60368d0
--- /dev/null
+++ b/FreeSql.Tests/UnitTest1.cs
@@ -0,0 +1,150 @@
+using FreeSql.DataAnnotations;
+using FreeSql;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace FreeSql.Tests {
+ public class UnitTest1 {
+
+ ISelect select => g.mysql.Select();
+ [Fact]
+ public void Test1() {
+
+ var t1 = g.mysql.Select().Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql();
+ var t2 = g.mysql.Select().As("b").Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql();
+
+
+ var sql1 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).ToSql();
+ var sql2 = select.LeftJoin((a, b) => a.TypeGuid == b.Guid && b.Name == "111").ToSql();
+ var sql3 = select.LeftJoin("TestTypeInfo b on b.Guid = a.TypeGuid").ToSql();
+
+ //g.mysql.Select().Join((a, b, c) => new Model.JoinResult3(
+ // Model.JoinType.LeftJoin, a.TypeGuid == b.Guid,
+ // Model.JoinType.InnerJoin, c.Id == b.ParentId && c.Name == "xxx")
+ //);
+
+ //var sql4 = select.From((a, b, c) => new SelectFrom()
+ // .InnerJoin(a.TypeGuid == b.Guid)
+ // .LeftJoin(c.Id == b.ParentId)
+ // .Where(b.Name == "xxx"))
+ //.Where(a => a.Id == 1).ToSql();
+
+ var sql4 = select.From((s, b, c) => s
+ .InnerJoin(a => a.TypeGuid == b.Guid)
+ .LeftJoin(a => c.Id == b.ParentId)
+ .Where(a => b.Name == "xxx"));
+ //.Where(a => a.Id == 1).ToSql();
+
+
+ var list111 = select.From((s, b, c) => s
+ .InnerJoin(a => a.TypeGuid == b.Guid)
+ .LeftJoin(a => c.Id == b.ParentId)
+ .Where(a => b.Name != "xxx")).ToList((a, b, c) => new {
+ a.Id,
+ a.Title,
+ a.Type,
+ ccc = new { a.Id, a.Title },
+ tp = a.Type,
+ tp2 = new {
+ a.Id,
+ tp2 = a.Type.Name
+ },
+ tp3 = new {
+ a.Id,
+ tp33 = new {
+ a.Id
+ }
+ }
+ });
+
+ var ttt122 = g.mysql.Select().Where(a => a.Id > 0).ToSql();
+
+
+
+
+ var sql5 = g.mysql.Select().From((s, b, c) => s).Where((a, b, c) => a.Id == b.ParentId).ToSql();
+
+
+
+
+
+ //((JoinType.LeftJoin, a.TypeGuid == b.Guid), (JoinType.InnerJoin, b.ParentId == c.Id)
+
+ var t11112 = g.mysql.Select().ToList(a => new {
+ a.Id, a.Title, a.Type,
+ ccc = new { a.Id, a.Title },
+ tp = a.Type,
+ tp2 = new {
+ a.Id, tp2 = a.Type.Name
+ },
+ tp3 = new {
+ a.Id,
+ tp33 = new {
+ a.Id
+ }
+ }
+
+ });
+
+ var t100 = g.mysql.Select().Where("").Where(a => a.Id > 0).Skip(100).Limit(200).Caching(50).ToList();
+ var t101 = g.mysql.Select().As("b").Where("").Where(a => a.Id > 0).Skip(100).Limit(200).Caching(50).ToList();
+
+
+ var t1111 = g.mysql.Select().ToList(a => new { a.Id, a.Title, a.Type });
+
+ var t2222 = g.mysql.Select().ToList(a => new { a.Id, a.Title, a.Type.Name });
+
+ var t3 = g.mysql.Insert(new[] { new TestInfo { }, new TestInfo { } }).IgnoreColumns(a => a.Title).ToSql();
+ var t4 = g.mysql.Insert(new[] { new TestInfo { }, new TestInfo { } }).IgnoreColumns(a => new { a.Title, a.CreateTime }).ToSql();
+ var t5 = g.mysql.Insert(new[] { new TestInfo { }, new TestInfo { } }).IgnoreColumns(a => new { a.Title, a.TypeGuid, a.CreateTime }).ToSql();
+ var t6 = g.mysql.Insert(new[] { new TestInfo { }, new TestInfo { } }).InsertColumns(a => new { a.Title }).ToSql();
+
+ var t7 = g.mysql.Update().ToSql();
+ var t8 = g.mysql.Update().Where(new TestInfo { }).ToSql();
+ var t9 = g.mysql.Update().Where(new[] { new TestInfo { Id = 1 }, new TestInfo { Id = 2 } }).ToSql();
+ var t10 = g.mysql.Update().Where(new[] { new TestInfo { Id = 1 }, new TestInfo { Id = 2 } }).Where(a => a.Title == "111").ToSql();
+ var t11 = g.mysql.Update().SetSource(new[] { new TestInfo { Id = 1, Title = "111" }, new TestInfo { Id = 2, Title = "222" } }).ToSql();
+ var t12 = g.mysql.Update().SetSource(new[] { new TestInfo { Id = 1, Title = "111" }, new TestInfo { Id = 2, Title = "222" } }).Where(a => a.Title == "111").ToSql();
+
+ var t13 = g.mysql.Update().Set(a => a.Title, "222111").ToSql();
+ var t14 = g.mysql.Update().Set(a => a.Title, "222111").Where(new TestInfo { }).ToSql();
+ var t15 = g.mysql.Update().Set(a => a.Title, "222111").Where(new[] { new TestInfo { Id = 1 }, new TestInfo { Id = 2 } }).ToSql();
+ var t16 = g.mysql.Update().Set(a => a.Title, "222111").Where(new[] { new TestInfo { Id = 1 }, new TestInfo { Id = 2 } }).Where(a => a.Title == "111").ToSql();
+ var t17 = g.mysql.Update().SetSource(new[] { new TestInfo { Id = 1, Title = "111" }, new TestInfo { Id = 2, Title = "222" } }).Set(a => a.Title, "222111").ToSql();
+ var t18 = g.mysql.Update().SetSource(new[] { new TestInfo { Id = 1, Title = "111" }, new TestInfo { Id = 2, Title = "222" } }).Set(a => a.Title, "222111").Where(a => a.Title == "111").ToSql();
+
+ var t19 = g.mysql.Update().Set(a => a.TypeGuid + 222111).ToSql();
+ var t20 = g.mysql.Update().Set(a => a.TypeGuid + 222111).Where(new TestInfo { }).ToSql();
+ var t21 = g.mysql.Update().Set(a => a.TypeGuid + 222111).Where(new[] { new TestInfo { Id = 1 }, new TestInfo { Id = 2 } }).ToSql();
+ var t22 = g.mysql.Update().Set(a => a.TypeGuid + 222111).Where(new[] { new TestInfo { Id = 1 }, new TestInfo { Id = 2 } }).Where(a => a.Title == "111").ToSql();
+ var t23 = g.mysql.Update().SetSource(new[] { new TestInfo { Id = 1, Title = "111" }, new TestInfo { Id = 2, Title = "222" } }).Set(a => a.TypeGuid + 222111).ToSql();
+ var t24 = g.mysql.Update().SetSource(new[] { new TestInfo { Id = 1, Title = "111" }, new TestInfo { Id = 2, Title = "222" } }).Set(a => a.TypeGuid + 222111).Where(a => a.Title == "111").ToSql();
+
+ }
+ }
+
+ [Table(Name = "xxx", SelectFilter = " a.id > 0")]
+ class TestInfo {
+ [Column(IsIdentity = true, IsPrimary = true)]
+ public int Id { get; set; }
+ public int TypeGuid { get; set; }
+ public TestTypeInfo Type { get; set; }
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ }
+
+ class TestTypeInfo {
+ public int Guid { get; set; }
+ public int ParentId { get; set; }
+ public TestTypeParentInfo Parent { get; set; }
+ public string Name { get; set; }
+ }
+
+ class TestTypeParentInfo {
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ public List Types { get; set; }
+ }
+}
diff --git a/FreeSql.Tests/g.cs b/FreeSql.Tests/g.cs
new file mode 100644
index 00000000..1f7f39b0
--- /dev/null
+++ b/FreeSql.Tests/g.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+
+public class g {
+
+ public static IFreeSql mysql = 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();
+
+ public static IFreeSql sqlserver = new FreeSql.FreeSqlBuilder()
+ .UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=shop;Pooling=true;Max Pool Size=10")
+ .Build();
+}
diff --git a/FreeSql.sln b/FreeSql.sln
new file mode 100644
index 00000000..dd642cc8
--- /dev/null
+++ b/FreeSql.sln
@@ -0,0 +1,76 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql", "FreeSql\FreeSql.csproj", "{AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests", "FreeSql.Tests\FreeSql.Tests.csproj", "{AA88EB04-4788-4180-AE68-7FA5ED17D98C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{C6A74E2A-6660-473D-8852-B1D8348DB4E9}"
+ ProjectSection(SolutionItems) = preProject
+ Docs\codefirst.md = Docs\codefirst.md
+ Docs\dbfirst.md = Docs\dbfirst.md
+ Docs\delete.md = Docs\delete.md
+ Docs\insert.md = Docs\insert.md
+ readme.md = readme.md
+ Docs\select.md = Docs\select.md
+ Docs\update.md = Docs\update.md
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xxx", "..\..\新建文件夹 (9)\xxx.csproj", "{6DC39740-0B26-4029-AB75-D436A7F666A2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Debug|x64.Build.0 = Debug|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Debug|x86.Build.0 = Debug|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Release|x64.ActiveCfg = Release|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Release|x64.Build.0 = Release|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Release|x86.ActiveCfg = Release|Any CPU
+ {AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}.Release|x86.Build.0 = Release|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Debug|x64.Build.0 = Debug|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Debug|x86.Build.0 = Debug|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Release|x64.ActiveCfg = Release|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Release|x64.Build.0 = Release|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Release|x86.ActiveCfg = Release|Any CPU
+ {AA88EB04-4788-4180-AE68-7FA5ED17D98C}.Release|x86.Build.0 = Release|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Debug|x64.Build.0 = Debug|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Debug|x86.Build.0 = Debug|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Release|x64.ActiveCfg = Release|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Release|x64.Build.0 = Release|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Release|x86.ActiveCfg = Release|Any CPU
+ {6DC39740-0B26-4029-AB75-D436A7F666A2}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98}
+ EndGlobalSection
+EndGlobal
diff --git a/FreeSql/DataAnnotations/ColumnAttribute.cs b/FreeSql/DataAnnotations/ColumnAttribute.cs
new file mode 100644
index 00000000..0fcabff9
--- /dev/null
+++ b/FreeSql/DataAnnotations/ColumnAttribute.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace FreeSql.DataAnnotations {
+ public class ColumnAttribute : Attribute {
+
+ ///
+ /// 数据库列名
+ ///
+ public string Name { get; set; }
+ ///
+ /// 指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】
+ ///
+ public string OldName { get; set; }
+ ///
+ /// 数据库类型,如: varchar(255)
+ ///
+ public string DbType { get; set; }
+
+ ///
+ /// 主键
+ ///
+ public bool IsPrimary { get; set; }
+ ///
+ /// 自增标识
+ ///
+ public bool IsIdentity { get; set; }
+ ///
+ /// 是否可DBNull
+ ///
+ public bool IsNullable { get; set; }
+ }
+}
diff --git a/FreeSql/DataAnnotations/TableAttribute.cs b/FreeSql/DataAnnotations/TableAttribute.cs
new file mode 100644
index 00000000..c2773633
--- /dev/null
+++ b/FreeSql/DataAnnotations/TableAttribute.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace FreeSql.DataAnnotations {
+ public class TableAttribute : Attribute {
+
+ ///
+ /// 数据库表名
+ ///
+ public string Name { get; set; }
+ ///
+ /// 指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】
+ ///
+ public string OldName { get; set; }
+ ///
+ /// 查询过滤SQL,实现类似 a.IsDeleted = 1 功能
+ ///
+ public string SelectFilter { get; set; }
+ }
+}
diff --git a/FreeSql/DatabaseModel/DBColumnInfo.cs b/FreeSql/DatabaseModel/DBColumnInfo.cs
new file mode 100644
index 00000000..b4351f7c
--- /dev/null
+++ b/FreeSql/DatabaseModel/DBColumnInfo.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace FreeSql.DatabaseModel {
+ public class DbColumnInfo {
+ ///
+ /// 所属表
+ ///
+ public DbTableInfo Table { get; internal set; }
+ ///
+ /// 列名
+ ///
+ public string Name { get; internal set; }
+ ///
+ /// 映射到 C# 类型
+ ///
+ public Type CsType { get; internal set; }
+ ///
+ /// 数据库枚举类型int值
+ ///
+ public int DbType { get; internal set; }
+ ///
+ /// 数据库类型,字符串,varchar
+ ///
+ public string DbTypeText { get; internal set; }
+ ///
+ /// 数据库类型,字符串,varchar(255)
+ ///
+ public string DbTypeTextFull { get; internal set; }
+ ///
+ /// 最大长度
+ ///
+ public int MaxLength { get; internal set; }
+ ///
+ /// 主键
+ ///
+ public bool IsPrimary { get; internal set; }
+ ///
+ /// 自增标识
+ ///
+ public bool IsIdentity { get; internal set; }
+ ///
+ /// 是否可DBNull
+ ///
+ public bool IsNullable { get; internal set; }
+ ///
+ /// 备注
+ ///
+ public string Coment { get; internal set; }
+ }
+}
diff --git a/FreeSql/DatabaseModel/DBTableInfo.cs b/FreeSql/DatabaseModel/DBTableInfo.cs
new file mode 100644
index 00000000..f5251e64
--- /dev/null
+++ b/FreeSql/DatabaseModel/DBTableInfo.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace FreeSql.DatabaseModel {
+ public class DbTableInfo {
+ ///
+ /// 唯一标识
+ ///
+ public string Id { get; internal set; }
+ ///
+ /// SqlServer下是Owner、PostgreSQL下是Schema、MySql下是数据库名
+ ///
+ public string Schema { get; internal set; }
+ ///
+ /// 表名
+ ///
+ public string Name { get; internal set; }
+ ///
+ /// 表/视图
+ ///
+ public DbTableType Type { get; set; }
+ ///
+ /// 列
+ ///
+ public List Columns { get; internal set; } = new List();
+ ///
+ /// 自增列
+ ///
+ public List Identitys { get; internal set; } = new List();
+ ///
+ /// 主键/组合
+ ///
+ public List Primarys { get; internal set; } = new List();
+ ///
+ /// 唯一键/组合
+ ///
+ public List> Uniques { get; internal set; } = new List>();
+ ///
+ /// 索引/组合
+ ///
+ public List> Indexes { get; internal set; } = new List>();
+ ///
+ /// 外键
+ ///
+ public List Foreigns { get; internal set; } = new List();
+ }
+
+ public enum DbTableType {
+ TABLE, VIEW, StoreProcedure
+ }
+}
diff --git a/FreeSql/DatabaseModel/DbForeignInfo.cs b/FreeSql/DatabaseModel/DbForeignInfo.cs
new file mode 100644
index 00000000..ef805e24
--- /dev/null
+++ b/FreeSql/DatabaseModel/DbForeignInfo.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace FreeSql.DatabaseModel {
+ public class DbForeignInfo {
+ public DbTableInfo Table { get; internal set; }
+ public List Columns { get; internal set; } = new List();
+ public DbTableInfo ReferencedTable { get; internal set; }
+ public List ReferencedColumns { get; internal set; } = new List();
+
+ }
+}
diff --git a/FreeSql/Extensions/NpgsqlTypesExtensions.cs b/FreeSql/Extensions/NpgsqlTypesExtensions.cs
new file mode 100644
index 00000000..2ca57bf7
--- /dev/null
+++ b/FreeSql/Extensions/NpgsqlTypesExtensions.cs
@@ -0,0 +1,39 @@
+using NpgsqlTypes;
+using System;
+using System.Collections;
+
+namespace NpgsqlTypes {
+ public static class FreeSqlExtensions {
+
+ public static string To1010(this BitArray ba) {
+ char[] ret = new char[ba.Length];
+ for (int a = 0; a < ba.Length; a++) ret[a] = ba[a] ? '1' : '0';
+ return new string(ret);
+ }
+
+ ///
+ /// 将 1010101010 这样的二进制字符串转换成 BitArray
+ ///
+ /// 1010101010
+ ///
+ public static BitArray ToBitArray(this string _1010Str) {
+ if (_1010Str == null) return null;
+ BitArray ret = new BitArray(_1010Str.Length);
+ for (int a = 0; a < _1010Str.Length; a++) ret[a] = _1010Str[a] == '1';
+ return ret;
+ }
+
+ public static NpgsqlRange ToNpgsqlRange(this string that) {
+ var s = that;
+ if (string.IsNullOrEmpty(s) || s == "empty") return NpgsqlRange.Empty;
+ string s1 = s.Trim('(', ')', '[', ']');
+ string[] ss = s1.Split(new char[] { ',' }, 2);
+ if (ss.Length != 2) return NpgsqlRange.Empty;
+ T t1 = default(T);
+ T t2 = default(T);
+ if (!string.IsNullOrEmpty(ss[0])) t1 = (T)Convert.ChangeType(ss[0], typeof(T));
+ if (!string.IsNullOrEmpty(ss[1])) t2 = (T)Convert.ChangeType(ss[1], typeof(T));
+ return new NpgsqlRange(t1, s[0] == '[', s[0] == '(', t2, s[s.Length - 1] == ']', s[s.Length - 1] == ')');
+ }
+ }
+}
\ No newline at end of file
diff --git a/FreeSql/Extensions/StringExtensions.cs b/FreeSql/Extensions/StringExtensions.cs
new file mode 100644
index 00000000..a43aecee
--- /dev/null
+++ b/FreeSql/Extensions/StringExtensions.cs
@@ -0,0 +1,31 @@
+public static class StringExtensions {
+
+ ///
+ /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
+ ///
+ ///
+ ///
+ ///
+ public static string FormatMySql(this string that, params object[] args) => _mysqlAdo.Addslashes(that, args);
+ static FreeSql.MySql.MySqlAdo _mysqlAdo = new FreeSql.MySql.MySqlAdo();
+ ///
+ /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
+ ///
+ ///
+ ///
+ ///
+ public static string FormatSqlServer(this string that, params object[] args) => _sqlserverAdo.Addslashes(that, args);
+ static FreeSql.SqlServer.SqlServerAdo _sqlserverAdo = new FreeSql.SqlServer.SqlServerAdo();
+ ///
+ /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
+ ///
+ ///
+ ///
+ ///
+ public static string FormatPostgreSQL(this string that, params object[] args) => _postgresqlAdo.Addslashes(that, args);
+ static FreeSql.PostgreSQL.PostgreSQLAdo _postgresqlAdo = new FreeSql.PostgreSQL.PostgreSQLAdo();
+}
+
+namespace System.Runtime.CompilerServices {
+ public class ExtensionAttribute : Attribute { }
+}
\ No newline at end of file
diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj
new file mode 100644
index 00000000..e4e0aab1
--- /dev/null
+++ b/FreeSql/FreeSql.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FreeSql/FreeSqlBuilder.cs b/FreeSql/FreeSqlBuilder.cs
new file mode 100644
index 00000000..3a90ea62
--- /dev/null
+++ b/FreeSql/FreeSqlBuilder.cs
@@ -0,0 +1,66 @@
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace FreeSql {
+ public class FreeSqlBuilder {
+ IDistributedCache _cache;
+ ILogger _logger;
+ DataType _dataType;
+ string _masterConnectionString;
+ string[] _slaveConnectionString;
+
+ ///
+ /// 使用缓存,不指定默认使用内存
+ ///
+ /// 缓存现实
+ ///
+ public FreeSqlBuilder UseCache(IDistributedCache cache) {
+ _cache = cache;
+ return this;
+ }
+
+ ///
+ /// 使用日志,不指定默认输出控制台
+ ///
+ ///
+ ///
+ public FreeSqlBuilder UseLogger(ILogger logger) {
+ _logger = logger;
+ return this;
+ }
+ ///
+ /// 使用连接串
+ ///
+ /// 数据库类型
+ /// 数据库连接串
+ ///
+ public FreeSqlBuilder UseConnectionString(DataType dataType, string connectionString) {
+ _dataType = dataType;
+ _masterConnectionString = connectionString;
+ return this;
+ }
+ ///
+ /// 使用从数据库,支持多个
+ ///
+ /// 从数据库连接串
+ ///
+ public FreeSqlBuilder UseSlave(params string[] slaveConnectionString) {
+ _slaveConnectionString = slaveConnectionString;
+ return this;
+ }
+
+ public IFreeSql Build() {
+ switch(_dataType) {
+ case DataType.MySql: return new MySql.MySqlProvider(_cache, null, _masterConnectionString, _slaveConnectionString, _logger);
+ case DataType.SqlServer: return new SqlServer.SqlServerProvider(_cache, null, _masterConnectionString, _slaveConnectionString, _logger);
+ case DataType.PostgreSQL: return new MySql.MySqlProvider(_cache, null, _masterConnectionString, _slaveConnectionString, _logger);
+ }
+ return null;
+ }
+ }
+
+ public enum DataType { MySql, SqlServer, PostgreSQL }
+}
diff --git a/FreeSql/FreeUtil.cs b/FreeSql/FreeUtil.cs
new file mode 100644
index 00000000..fefe2ba6
--- /dev/null
+++ b/FreeSql/FreeUtil.cs
@@ -0,0 +1,34 @@
+using NpgsqlTypes;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Threading;
+
+public static class FreeUtil {
+
+ private static DateTime dt1970 = new DateTime(1970, 1, 1);
+ private static ThreadLocal rnd = new ThreadLocal();
+ private static readonly int __staticMachine = ((0x00ffffff & Environment.MachineName.GetHashCode()) +
+#if NETSTANDARD1_5 || NETSTANDARD1_6
+ 1
+#else
+ AppDomain.CurrentDomain.Id
+#endif
+ ) & 0x00ffffff;
+ private static readonly int __staticPid = Process.GetCurrentProcess().Id;
+ private static int __staticIncrement = rnd.Value.Next();
+ ///
+ /// 生成类似Mongodb的ObjectId有序、不重复Guid
+ ///
+ ///
+ public static Guid NewMongodbId() {
+ var now = DateTime.Now;
+ var uninxtime = (int)now.Subtract(dt1970).TotalSeconds;
+ int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff;
+ var rand = rnd.Value.Next(0, int.MaxValue);
+ var guid = $"{uninxtime.ToString("x8").PadLeft(8, '0')}{__staticMachine.ToString("x8").PadLeft(8, '0').Substring(2, 6)}{__staticPid.ToString("x8").PadLeft(8, '0').Substring(6, 2)}{increment.ToString("x8").PadLeft(8, '0')}{rand.ToString("x8").PadLeft(8, '0')}";
+ return Guid.Parse(guid);
+ }
+}
\ No newline at end of file
diff --git a/FreeSql/Generator/TemplateEngin.cs b/FreeSql/Generator/TemplateEngin.cs
new file mode 100644
index 00000000..4760937d
--- /dev/null
+++ b/FreeSql/Generator/TemplateEngin.cs
@@ -0,0 +1,640 @@
+using Microsoft.CSharp;
+using System;
+using System.CodeDom.Compiler;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+
+namespace FreeSql.Generator {
+ public class TemplateEngin : IDisposable {
+ public interface ITemplateOutput {
+ ///
+ ///
+ ///
+ /// 返回内容
+ /// 渲染对象
+ /// 当前文件路径
+ ///
+ ///
+ TemplateReturnInfo OuTpUt(StringBuilder tOuTpUt, IDictionary oPtIoNs, string rEfErErFiLeNaMe, TemplateEngin tEmPlAtEsEnDeR);
+ }
+ public class TemplateReturnInfo {
+ public Dictionary Blocks;
+ public StringBuilder Sb;
+ }
+ public delegate bool TemplateIf(object exp);
+ public delegate void TemplatePrint(params object[] parms);
+
+ private static int _view = 0;
+ private static Regex _reg = new Regex(@"\{(\$TEMPLATE__CODE|\/\$TEMPLATE__CODE|import\s+|module\s+|extends\s+|block\s+|include\s+|for\s+|if\s+|#|\/for|elseif|else|\/if|\/block|\/module)([^\}]*)\}", RegexOptions.Compiled);
+ private static Regex _reg_forin = new Regex(@"^([\w_]+)\s*,?\s*([\w_]+)?\s+in\s+(.+)", RegexOptions.Compiled);
+ private static Regex _reg_foron = new Regex(@"^([\w_]+)\s*,?\s*([\w_]+)?,?\s*([\w_]+)?\s+on\s+(.+)", RegexOptions.Compiled);
+ private static Regex _reg_forab = new Regex(@"^([\w_]+)\s+([^,]+)\s*,\s*(.+)", RegexOptions.Compiled);
+ private static Regex _reg_miss = new Regex(@"\{\/?miss\}", RegexOptions.Compiled);
+ private static Regex _reg_code = new Regex(@"(\{%|%\})", RegexOptions.Compiled);
+ private static Regex _reg_syntax = new Regex(@"<(\w+)\s+@(if|for|else)\s*=""([^""]*)""", RegexOptions.Compiled);
+ private static Regex _reg_htmltag = new Regex(@"<\/?\w+[^>]*>", RegexOptions.Compiled);
+ private static Regex _reg_blank = new Regex(@"\s+", RegexOptions.Compiled);
+ private static Regex _reg_complie_undefined = new Regex(@"(当前上下文中不存在名称)?“(\w+)”", RegexOptions.Compiled);
+
+ private Dictionary _cache = new Dictionary();
+ private object _cache_lock = new object();
+ private string _viewDir;
+ private string[] _usings;
+ private FileSystemWatcher _fsw = new FileSystemWatcher();
+
+ public TemplateEngin(string viewDir, params string[] usings) {
+ _viewDir = Utils.TranslateUrl(viewDir);
+ _usings = usings;
+ _fsw = new FileSystemWatcher(_viewDir);
+ _fsw.IncludeSubdirectories = true;
+ _fsw.Changed += ViewDirChange;
+ _fsw.Renamed += ViewDirChange;
+ _fsw.EnableRaisingEvents = true;
+ }
+ public void Dispose() {
+ _fsw.Dispose();
+ }
+ void ViewDirChange(object sender, FileSystemEventArgs e) {
+ string filename = e.FullPath.ToLower();
+ lock (_cache_lock) {
+ _cache.Remove(filename);
+ }
+ }
+ public TemplateReturnInfo RenderFile2(StringBuilder sb, IDictionary options, string filename, string refererFilename) {
+ if (filename[0] == '/' || string.IsNullOrEmpty(refererFilename)) refererFilename = _viewDir;
+ //else refererFilename = Path.GetDirectoryName(refererFilename);
+ string filename2 = Utils.TranslateUrl(filename, refererFilename);
+ ITemplateOutput tpl;
+ if (_cache.TryGetValue(filename2, out tpl) == false) {
+ string tplcode = File.Exists(filename2) == false ? string.Concat("文件不存在 ", filename) : Utils.ReadTextFile(filename2);
+ tpl = Parser(tplcode, _usings, options);
+ lock (_cache_lock) {
+ if (_cache.ContainsKey(filename2) == false) {
+ _cache.Add(filename2, tpl);
+ }
+ }
+ }
+ try {
+ return tpl.OuTpUt(sb, options, filename2, this);
+ } catch (Exception ex) {
+ TemplateReturnInfo ret = sb == null ?
+ new TemplateReturnInfo { Sb = new StringBuilder(), Blocks = new Dictionary() } :
+ new TemplateReturnInfo { Sb = sb, Blocks = new Dictionary() };
+ ret.Sb.Append(refererFilename);
+ ret.Sb.Append(" -> ");
+ ret.Sb.Append(filename);
+ ret.Sb.Append("\r\n");
+ ret.Sb.Append(ex.Message);
+ ret.Sb.Append("\r\n");
+ ret.Sb.Append(ex.StackTrace);
+ return ret;
+ }
+ }
+ public string RenderFile(string filename, IDictionary options) {
+ TemplateReturnInfo ret = this.RenderFile2(null, options, filename, null);
+ return ret.Sb.ToString();
+ }
+ private static ITemplateOutput Parser(string tplcode, string[] usings, IDictionary options) {
+ int view = Interlocked.Increment(ref _view);
+ StringBuilder sb = new StringBuilder();
+ IDictionary options_copy = new Hashtable();
+ foreach (DictionaryEntry options_de in options) options_copy[options_de.Key] = options_de.Value;
+ sb.AppendFormat(@"
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;{1}
+
+//namespace TplDynamicCodeGenerate {{
+ public class TplDynamicCodeGenerate_view{0} : FreeSql.Generator.TemplateEngin.ITemplateOutput {{
+ public FreeSql.Generator.TemplateEngin.TemplateReturnInfo OuTpUt(StringBuilder tOuTpUt, IDictionary oPtIoNs, string rEfErErFiLeNaMe, FreeSql.Generator.TemplateEngin tEmPlAtEsEnDeR) {{
+ FreeSql.Generator.TemplateEngin.TemplateReturnInfo rTn = tOuTpUt == null ?
+ new FreeSql.Generator.TemplateEngin.TemplateReturnInfo {{ Sb = (tOuTpUt = new StringBuilder()), Blocks = new Dictionary() }} :
+ new FreeSql.Generator.TemplateEngin.TemplateReturnInfo {{ Sb = tOuTpUt, Blocks = new Dictionary() }};
+ Dictionary TPL__blocks = rTn.Blocks;
+ Stack TPL__blocks_stack = new Stack();
+ int[] TPL__blocks_stack_peek;
+ List TPL__forc = new List();
+ Func pRoCeSsOpTiOnS = new Func(delegate () {{
+ IDictionary nEwoPtIoNs = new Hashtable();
+ foreach (DictionaryEntry oPtIoNs_dE in oPtIoNs)
+ nEwoPtIoNs[oPtIoNs_dE.Key] = oPtIoNs_dE.Value;
+ foreach (IDictionary TPL__forc_dIc in TPL__forc)
+ foreach (DictionaryEntry TPL__forc_dIc_dE in TPL__forc_dIc)
+ nEwoPtIoNs[TPL__forc_dIc_dE.Key] = TPL__forc_dIc_dE.Value;
+ return nEwoPtIoNs;
+ }});
+ FreeSql.Generator.TemplateEngin.TemplateIf tPlIf = delegate(object exp) {{
+ if (exp is bool) return (bool)exp;
+ if (exp == null) return false;
+ if (exp is int && (int)exp == 0) return false;
+ if (exp is string && (string)exp == string.Empty) return false;
+ if (exp is long && (long)exp == 0) return false;
+ if (exp is short && (short)exp == 0) return false;
+ if (exp is byte && (byte)exp == 0) return false;
+ if (exp is double && (double)exp == 0) return false;
+ if (exp is float && (float)exp == 0) return false;
+ if (exp is decimal && (decimal)exp == 0) return false;
+ return true;
+ }};
+ FreeSql.Generator.TemplateEngin.TemplatePrint print = delegate(object[] pArMs) {{
+ if (pArMs == null || pArMs.Length == 0) return;
+ foreach (object pArMs_A in pArMs) if (pArMs_A != null) tOuTpUt.Append(pArMs_A);
+ }};
+ FreeSql.Generator.TemplateEngin.TemplatePrint Print = print;", view, usings?.Any() == true ? $"\r\nusing {string.Join(";\r\nusing ", usings)};" : "");
+
+ #region {miss}...{/miss}块内容将不被解析
+ string[] tmp_content_arr = _reg_miss.Split(tplcode);
+ if (tmp_content_arr.Length > 1) {
+ sb.AppendFormat(@"
+ string[] TPL__MISS = new string[{0}];", Math.Ceiling(1.0 * (tmp_content_arr.Length - 1) / 2));
+ int miss_len = -1;
+ for (int a = 1; a < tmp_content_arr.Length; a += 2) {
+ sb.Append(string.Concat(@"
+ TPL__MISS[", ++miss_len, @"] = """, Utils.GetConstString(tmp_content_arr[a]), @""";"));
+ tmp_content_arr[a] = string.Concat("{#TPL__MISS[", miss_len, "]}");
+ }
+ tplcode = string.Join("", tmp_content_arr);
+ }
+ #endregion
+ #region 扩展语法如
+ tplcode = htmlSyntax(tplcode, 3); //
+ //处理 {% %} 块 c#代码
+ tmp_content_arr = _reg_code.Split(tplcode);
+ if (tmp_content_arr.Length == 1) {
+ tplcode = Utils.GetConstString(tplcode)
+ .Replace("{%", "{$TEMPLATE__CODE}")
+ .Replace("%}", "{/$TEMPLATE__CODE}");
+ } else {
+ tmp_content_arr[0] = Utils.GetConstString(tmp_content_arr[0]);
+ for (int a = 1; a < tmp_content_arr.Length; a += 4) {
+ tmp_content_arr[a] = "{$TEMPLATE__CODE}";
+ tmp_content_arr[a + 2] = "{/$TEMPLATE__CODE}";
+ tmp_content_arr[a + 3] = Utils.GetConstString(tmp_content_arr[a + 3]);
+ }
+ tplcode = string.Join("", tmp_content_arr);
+ }
+ #endregion
+ sb.Append(@"
+ tOuTpUt.Append(""");
+
+ string error = null;
+ int tpl_tmpid = 0;
+ int forc_i = 0;
+ string extends = null;
+ Stack codeTree = new Stack();
+ Stack forEndRepl = new Stack();
+ sb.Append(_reg.Replace(tplcode, delegate (Match m) {
+ string _0 = m.Groups[0].Value;
+ if (!string.IsNullOrEmpty(error)) return _0;
+
+ string _1 = m.Groups[1].Value.Trim(' ', '\t');
+ string _2 = m.Groups[2].Value
+ .Replace("\\\\", "\\")
+ .Replace("\\\"", "\"");
+ _2 = Utils.ReplaceSingleQuote(_2);
+
+ switch (_1) {
+ #region $TEMPLATE__CODE--------------------------------------------------
+ case "$TEMPLATE__CODE":
+ codeTree.Push(_1);
+ return @""");
+";
+ case "/$TEMPLATE__CODE":
+ string pop = codeTree.Pop();
+ if (pop != "$TEMPLATE__CODE") {
+ codeTree.Push(pop);
+ error = "编译出错,{% 与 %} 并没有配对";
+ return _0;
+ }
+ return @"
+ tOuTpUt.Append(""";
+ #endregion
+ case "include":
+ return string.Format(@""");
+tEmPlAtEsEnDeR.RenderFile2(tOuTpUt, pRoCeSsOpTiOnS(), ""{0}"", rEfErErFiLeNaMe);
+ tOuTpUt.Append(""", _2);
+ case "import":
+ return _0;
+ case "module":
+ return _0;
+ case "/module":
+ return _0;
+ case "extends":
+ //{extends ../inc/layout.html}
+ if (string.IsNullOrEmpty(extends) == false) return _0;
+ extends = _2;
+ return string.Empty;
+ case "block":
+ codeTree.Push("block");
+ return string.Format(@""");
+TPL__blocks_stack_peek = new int[] {{ tOuTpUt.Length, 0 }};
+TPL__blocks_stack.Push(TPL__blocks_stack_peek);
+TPL__blocks.Add(""{0}"", TPL__blocks_stack_peek);
+tOuTpUt.Append(""", _2.Trim(' ', '\t'));
+ case "/block":
+ codeTreeEnd(codeTree, "block");
+ return @""");
+TPL__blocks_stack_peek = TPL__blocks_stack.Pop();
+TPL__blocks_stack_peek[1] = tOuTpUt.Length - TPL__blocks_stack_peek[0];
+tOuTpUt.Append(""";
+
+ #region ##---------------------------------------------------------
+ case "#":
+ if (_2[0] == '#')
+ return string.Format(@""");
+ try {{ Print({0}); }} catch {{ }}
+ tOuTpUt.Append(""", _2.Substring(1));
+ return string.Format(@""");
+ Print({0});
+ tOuTpUt.Append(""", _2);
+ #endregion
+ #region for--------------------------------------------------------
+ case "for":
+ forc_i++;
+ int cur_tpl_tmpid = tpl_tmpid;
+ string sb_endRepl = string.Empty;
+ StringBuilder sbfor = new StringBuilder();
+ sbfor.Append(@""");");
+ Match mfor = _reg_forin.Match(_2);
+ if (mfor.Success) {
+ string mfor1 = mfor.Groups[1].Value.Trim(' ', '\t');
+ string mfor2 = mfor.Groups[2].Value.Trim(' ', '\t');
+ sbfor.AppendFormat(@"
+//new Action(delegate () {{
+ IDictionary TPL__tmp{0} = new Hashtable();
+ TPL__forc.Add(TPL__tmp{0});
+ var TPL__tmp{1} = {3};
+ var TPL__tmp{2} = {4};", ++tpl_tmpid, ++tpl_tmpid, ++tpl_tmpid, mfor.Groups[3].Value, mfor1);
+ sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
+ {0} = TPL__tmp{1};", mfor1, cur_tpl_tmpid + 3));
+ if (options_copy.Contains(mfor1) == false) options_copy[mfor1] = null;
+ if (!string.IsNullOrEmpty(mfor2)) {
+ sbfor.AppendFormat(@"
+ var TPL__tmp{1} = {0};
+ {0} = 0;", mfor2, ++tpl_tmpid);
+ sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
+ {0} = TPL__tmp{1};", mfor2, tpl_tmpid));
+ if (options_copy.Contains(mfor2) == false) options_copy[mfor2] = null;
+ }
+ sbfor.AppendFormat(@"
+ if (TPL__tmp{1} != null)
+ foreach (var TPL__tmp{0} in TPL__tmp{1}) {{", ++tpl_tmpid, cur_tpl_tmpid + 2);
+ if (!string.IsNullOrEmpty(mfor2))
+ sbfor.AppendFormat(@"
+ TPL__tmp{1}[""{0}""] = ++ {0};", mfor2, cur_tpl_tmpid + 1);
+ sbfor.AppendFormat(@"
+ TPL__tmp{1}[""{0}""] = TPL__tmp{2};
+ {0} = TPL__tmp{2};
+ tOuTpUt.Append(""", mfor1, cur_tpl_tmpid + 1, tpl_tmpid);
+ codeTree.Push("for");
+ forEndRepl.Push(sb_endRepl);
+ return sbfor.ToString();
+ }
+ mfor = _reg_foron.Match(_2);
+ if (mfor.Success) {
+ string mfor1 = mfor.Groups[1].Value.Trim(' ', '\t');
+ string mfor2 = mfor.Groups[2].Value.Trim(' ', '\t');
+ string mfor3 = mfor.Groups[3].Value.Trim(' ', '\t');
+ sbfor.AppendFormat(@"
+//new Action(delegate () {{
+ IDictionary TPL__tmp{0} = new Hashtable();
+ TPL__forc.Add(TPL__tmp{0});
+ var TPL__tmp{1} = {3};
+ var TPL__tmp{2} = {4};", ++tpl_tmpid, ++tpl_tmpid, ++tpl_tmpid, mfor.Groups[4].Value, mfor1);
+ sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
+ {0} = TPL__tmp{1};", mfor1, cur_tpl_tmpid + 3));
+ if (options_copy.Contains(mfor1) == false) options_copy[mfor1] = null;
+ if (!string.IsNullOrEmpty(mfor2)) {
+ sbfor.AppendFormat(@"
+ var TPL__tmp{1} = {0};", mfor2, ++tpl_tmpid);
+ sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
+ {0} = TPL__tmp{1};", mfor2, tpl_tmpid));
+ if (options_copy.Contains(mfor2) == false) options_copy[mfor2] = null;
+ }
+ if (!string.IsNullOrEmpty(mfor3)) {
+ sbfor.AppendFormat(@"
+ var TPL__tmp{1} = {0};
+ {0} = 0;", mfor3, ++tpl_tmpid);
+ sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
+ {0} = TPL__tmp{1};", mfor3, tpl_tmpid));
+ if (options_copy.Contains(mfor3) == false) options_copy[mfor3] = null;
+ }
+ sbfor.AppendFormat(@"
+ if (TPL__tmp{2} != null)
+ foreach (DictionaryEntry TPL__tmp{1} in TPL__tmp{2}) {{
+ {0} = TPL__tmp{1}.Key;
+ TPL__tmp{3}[""{0}""] = {0};", mfor1, ++tpl_tmpid, cur_tpl_tmpid + 2, cur_tpl_tmpid + 1);
+ if (!string.IsNullOrEmpty(mfor2))
+ sbfor.AppendFormat(@"
+ {0} = TPL__tmp{1}.Value;
+ TPL__tmp{2}[""{0}""] = {0};", mfor2, tpl_tmpid, cur_tpl_tmpid + 1);
+ if (!string.IsNullOrEmpty(mfor3))
+ sbfor.AppendFormat(@"
+ TPL__tmp{1}[""{0}""] = ++ {0};", mfor3, cur_tpl_tmpid + 1);
+ sbfor.AppendFormat(@"
+ tOuTpUt.Append(""");
+ codeTree.Push("for");
+ forEndRepl.Push(sb_endRepl);
+ return sbfor.ToString();
+ }
+ mfor = _reg_forab.Match(_2);
+ if (mfor.Success) {
+ string mfor1 = mfor.Groups[1].Value.Trim(' ', '\t');
+ sbfor.AppendFormat(@"
+//new Action(delegate () {{
+ IDictionary TPL__tmp{0} = new Hashtable();
+ TPL__forc.Add(TPL__tmp{0});
+ var TPL__tmp{1} = {5};
+ {5} = {3} - 1;
+ if ({5} == null) {5} = 0;
+ var TPL__tmp{2} = {4} + 1;
+ while (++{5} < TPL__tmp{2}) {{
+ TPL__tmp{0}[""{5}""] = {5};
+ tOuTpUt.Append(""", ++tpl_tmpid, ++tpl_tmpid, ++tpl_tmpid, mfor.Groups[2].Value, mfor.Groups[3].Value, mfor1);
+ sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
+ {0} = TPL__tmp{1};", mfor1, cur_tpl_tmpid + 1));
+ if (options_copy.Contains(mfor1) == false) options_copy[mfor1] = null;
+ codeTree.Push("for");
+ forEndRepl.Push(sb_endRepl);
+ return sbfor.ToString();
+ }
+ return _0;
+ case "/for":
+ if (--forc_i < 0) return _0;
+ codeTreeEnd(codeTree, "for");
+ return string.Format(@""");
+ }}{0}
+ TPL__forc.RemoveAt(TPL__forc.Count - 1);
+//}})();
+ tOuTpUt.Append(""", forEndRepl.Pop());
+ #endregion
+ #region if---------------------------------------------------------
+ case "if":
+ codeTree.Push("if");
+ return string.Format(@""");
+ if ({1}tPlIf({0})) {{
+ tOuTpUt.Append(""", _2[0] == '!' ? _2.Substring(1) : _2, _2[0] == '!' ? '!' : ' ');
+ case "elseif":
+ codeTreeEnd(codeTree, "if");
+ codeTree.Push("if");
+ return string.Format(@""");
+ }} else if ({1}tPlIf({0})) {{
+ tOuTpUt.Append(""", _2[0] == '!' ? _2.Substring(1) : _2, _2[0] == '!' ? '!' : ' ');
+ case "else":
+ codeTreeEnd(codeTree, "if");
+ codeTree.Push("if");
+ return @""");
+ } else {
+ tOuTpUt.Append(""";
+ case "/if":
+ codeTreeEnd(codeTree, "if");
+ return @""");
+ }
+ tOuTpUt.Append(""";
+ #endregion
+ }
+ return _0;
+ }));
+
+ sb.Append(@""");");
+ if (string.IsNullOrEmpty(extends) == false) {
+ sb.AppendFormat(@"
+FreeSql.Generator.TemplateEngin.TemplateReturnInfo eXtEnDs_ReT = tEmPlAtEsEnDeR.RenderFile2(null, pRoCeSsOpTiOnS(), ""{0}"", rEfErErFiLeNaMe);
+string rTn_Sb_string = rTn.Sb.ToString();
+foreach(string eXtEnDs_ReT_blocks_key in eXtEnDs_ReT.Blocks.Keys) {{
+ if (rTn.Blocks.ContainsKey(eXtEnDs_ReT_blocks_key)) {{
+ int[] eXtEnDs_ReT_blocks_value = eXtEnDs_ReT.Blocks[eXtEnDs_ReT_blocks_key];
+ eXtEnDs_ReT.Sb.Remove(eXtEnDs_ReT_blocks_value[0], eXtEnDs_ReT_blocks_value[1]);
+ int[] rTn_blocks_value = rTn.Blocks[eXtEnDs_ReT_blocks_key];
+ eXtEnDs_ReT.Sb.Insert(eXtEnDs_ReT_blocks_value[0], rTn_Sb_string.Substring(rTn_blocks_value[0], rTn_blocks_value[1]));
+ foreach(string eXtEnDs_ReT_blocks_keyb in eXtEnDs_ReT.Blocks.Keys) {{
+ if (eXtEnDs_ReT_blocks_keyb == eXtEnDs_ReT_blocks_key) continue;
+ int[] eXtEnDs_ReT_blocks_valueb = eXtEnDs_ReT.Blocks[eXtEnDs_ReT_blocks_keyb];
+ if (eXtEnDs_ReT_blocks_valueb[0] >= eXtEnDs_ReT_blocks_value[0])
+ eXtEnDs_ReT_blocks_valueb[0] = eXtEnDs_ReT_blocks_valueb[0] - eXtEnDs_ReT_blocks_value[1] + rTn_blocks_value[1];
+ }}
+ eXtEnDs_ReT_blocks_value[1] = rTn_blocks_value[1];
+ }}
+}}
+return eXtEnDs_ReT;
+", extends);
+ } else {
+ sb.Append(@"
+return rTn;");
+ }
+ sb.Append(@"
+ }
+ }
+//}
+");
+ var str = "FreeSql.Generator.TemplateEngin.TemplatePrint Print = print;";
+ int dim_idx = sb.ToString().IndexOf(str) + str.Length;
+ foreach (string dic_name in options_copy.Keys) {
+ sb.Insert(dim_idx, string.Format(@"
+ dynamic {0} = oPtIoNs[""{0}""];", dic_name));
+ }
+ //Console.WriteLine(sb.ToString());
+ return Complie(sb.ToString(), @"TplDynamicCodeGenerate_view" + view);
+ }
+ private static string codeTreeEnd(Stack codeTree, string tag) {
+ string ret = string.Empty;
+ Stack pop = new Stack();
+ foreach (string ct in codeTree) {
+ if (ct == "import" ||
+ ct == "include") {
+ pop.Push(1);
+ } else if (ct == tag) {
+ pop.Push(2);
+ break;
+ } else {
+ if (string.IsNullOrEmpty(tag) == false) pop.Clear();
+ break;
+ }
+ }
+ if (pop.Count == 0 && string.IsNullOrEmpty(tag) == false)
+ return string.Concat("语法错误,{", tag, "} {/", tag, "} 并没配对");
+ while (pop.Count > 0 && pop.Pop() > 0) codeTree.Pop();
+ return ret;
+ }
+ #region htmlSyntax
+ private static string htmlSyntax(string tplcode, int num) {
+
+ while (num-- > 0) {
+ string[] arr = _reg_syntax.Split(tplcode);
+
+ if (arr.Length == 1) break;
+ for (int a = 1; a < arr.Length; a += 4) {
+ string tag = string.Concat('<', arr[a]);
+ string end = string.Concat("", arr[a], '>');
+ int fc = 1;
+ for (int b = a; fc > 0 && b < arr.Length; b += 4) {
+ if (b > a && arr[a].ToLower() == arr[b].ToLower()) fc++;
+ int bpos = 0;
+ while (true) {
+ int fa = arr[b + 3].IndexOf(tag, bpos);
+ int fb = arr[b + 3].IndexOf(end, bpos);
+ if (b == a) {
+ var z = arr[b + 3].IndexOf("/>");
+ if ((fb == -1 || z < fb) && z != -1) {
+ var y = arr[b + 3].Substring(0, z + 2);
+ if (_reg_htmltag.IsMatch(y) == false)
+ fb = z - end.Length + 2;
+ }
+ }
+ if (fa == -1 && fb == -1) break;
+ if (fa != -1 && (fa < fb || fb == -1)) {
+ fc++;
+ bpos = fa + tag.Length;
+ continue;
+ }
+ if (fb != -1) fc--;
+ if (fc <= 0) {
+ var a1 = arr[a + 1];
+ var end3 = string.Concat("{/", a1, "}");
+ if (a1.ToLower() == "else") {
+ if (_reg_blank.Replace(arr[a - 4 + 3], "").EndsWith("{/if}", StringComparison.CurrentCultureIgnoreCase) == true) {
+ var idx = arr[a - 4 + 3].IndexOf("{/if}");
+ arr[a - 4 + 3] = string.Concat(arr[a - 4 + 3].Substring(0, idx), arr[a - 4 + 3].Substring(idx + 5));
+ //如果 @else="有条件内容",则变换成 elseif 条件内容
+ if (_reg_blank.Replace(arr[a + 2], "").Length > 0) a1 = "elseif";
+ end3 = "{/if}";
+ } else {
+ arr[a] = string.Concat("指令 @", arr[a + 1], "='", arr[a + 2], "' 没紧接着 if/else 指令之后,无效. <", arr[a]);
+ arr[a + 1] = arr[a + 2] = string.Empty;
+ }
+ }
+ if (arr[a + 1].Length > 0) {
+ if (_reg_blank.Replace(arr[a + 2], "").Length > 0 || a1.ToLower() == "else") {
+ arr[b + 3] = string.Concat(arr[b + 3].Substring(0, fb + end.Length), end3, arr[b + 3].Substring(fb + end.Length));
+ arr[a] = string.Concat("{", a1, " ", arr[a + 2], "}<", arr[a]);
+ arr[a + 1] = arr[a + 2] = string.Empty;
+ } else {
+ arr[a] = string.Concat('<', arr[a]);
+ arr[a + 1] = arr[a + 2] = string.Empty;
+ }
+ }
+ break;
+ }
+ bpos = fb + end.Length;
+ }
+ }
+ if (fc > 0) {
+ arr[a] = string.Concat("不严谨的html格式,请检查 ", arr[a], " 的结束标签, @", arr[a + 1], "='", arr[a + 2], "' 指令无效. <", arr[a]);
+ arr[a + 1] = arr[a + 2] = string.Empty;
+ }
+ }
+ if (arr.Length > 0) tplcode = string.Join(string.Empty, arr);
+ }
+ return tplcode;
+ }
+ #endregion
+ #region Complie
+ private static ITemplateOutput Complie(string cscode, string typename) {
+ var assemly = _compiler.Value.CompileCode(cscode);
+ return assemly.CreateObject(typename) as ITemplateOutput;
+ }
+ static ConcurrentDictionary _compiler_objs = new ConcurrentDictionary();
+ static Lazy _compiler = new Lazy(() => {
+ var dlls = Directory.GetFiles(Directory.GetParent(Type.GetType("IFreeSql, FreeSql").Assembly.Location).FullName, "*.dll");
+ var compiler = new CSScriptLib.RoslynEvaluator();
+ compiler.DisableReferencingFromCode = false;
+ compiler.DebugBuild = true;
+ foreach (var dll in dlls) {
+ var ass = Assembly.LoadFile(dll);
+ compiler.ReferenceAssembly(ass);
+ }
+ return compiler;
+ });
+
+ #endregion
+
+ #region Utils
+ public class Utils {
+ public static string ReplaceSingleQuote(object exp) {
+ //将 ' 转换成 "
+ string exp2 = string.Concat(exp);
+ int quote_pos = -1;
+ while (true) {
+ int first_pos = quote_pos = exp2.IndexOf('\'', quote_pos + 1);
+ if (quote_pos == -1) break;
+ while (true) {
+ quote_pos = exp2.IndexOf('\'', quote_pos + 1);
+ if (quote_pos == -1) break;
+ int r_cout = 0;
+ for (int p = 1; true; p++) {
+ if (exp2[quote_pos - p] == '\\') r_cout++;
+ else break;
+ }
+ if (r_cout % 2 == 0/* && quote_pos - first_pos > 2*/) {
+ string str1 = exp2.Substring(0, first_pos);
+ string str2 = exp2.Substring(first_pos + 1, quote_pos - first_pos - 1);
+ string str3 = exp2.Substring(quote_pos + 1);
+ string str4 = str2.Replace("\"", "\\\"");
+ quote_pos += str4.Length - str2.Length;
+ exp2 = string.Concat(str1, "\"", str4, "\"", str3);
+ break;
+ }
+ }
+ if (quote_pos == -1) break;
+ }
+ return exp2;
+ }
+ public static string GetConstString(object obj) {
+ return string.Concat(obj)
+ .Replace("\\", "\\\\")
+ .Replace("\"", "\\\"")
+ .Replace("\r", "\\r")
+ .Replace("\n", "\\n");
+ }
+
+ public static string ReadTextFile(string path) {
+ byte[] bytes = ReadFile(path);
+ return Encoding.UTF8.GetString(bytes).TrimStart((char)65279);
+ }
+ public static byte[] ReadFile(string path) {
+ if (File.Exists(path)) {
+ string destFileName = Path.GetTempFileName();
+ File.Copy(path, destFileName, true);
+ int read = 0;
+ byte[] data = new byte[1024];
+ using (MemoryStream ms = new MemoryStream()) {
+ using (FileStream fs = new FileStream(destFileName, FileMode.OpenOrCreate, FileAccess.Read)) {
+ do {
+ read = fs.Read(data, 0, data.Length);
+ if (read <= 0) break;
+ ms.Write(data, 0, read);
+ } while (true);
+ }
+ File.Delete(destFileName);
+ data = ms.ToArray();
+ }
+ return data;
+ }
+ return new byte[] { };
+ }
+ public static string TranslateUrl(string url) {
+ return TranslateUrl(url, null);
+ }
+ public static string TranslateUrl(string url, string baseDir) {
+ if (string.IsNullOrEmpty(baseDir)) baseDir = AppContext.BaseDirectory + "/";
+ if (string.IsNullOrEmpty(url)) return Path.GetDirectoryName(baseDir);
+ if (url.StartsWith("~/")) url = url.Substring(1);
+ if (url.StartsWith("/")) return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(baseDir), url.TrimStart('/')));
+ if (url.StartsWith("\\")) return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(baseDir), url.TrimStart('\\')));
+ if (url.IndexOf(":\\") != -1) return url;
+ return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(baseDir), url));
+ }
+ }
+ #endregion
+ }
+}
diff --git a/FreeSql/Generator/TemplateGenerator.cs b/FreeSql/Generator/TemplateGenerator.cs
new file mode 100644
index 00000000..4b0281f3
--- /dev/null
+++ b/FreeSql/Generator/TemplateGenerator.cs
@@ -0,0 +1,71 @@
+using FreeSql.DatabaseModel;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace FreeSql.Generator {
+ public class TemplateGenerator {
+
+ public void Build(IDbFirst dbfirst, string templateDirectory, string outputDirectory, params string[] database) {
+ if (dbfirst == null) throw new ArgumentException("dbfirst 参数不能为 null");
+ if (string.IsNullOrEmpty(templateDirectory) || Directory.Exists(templateDirectory) == false) throw new ArgumentException("templateDirectory 目录不存在");
+ if (string.IsNullOrEmpty(templateDirectory)) throw new ArgumentException("outputDirectory 不能为 null");
+ if (database == null || database.Any() == false) throw new ArgumentException("database 参数不能为空");
+ if (Directory.Exists(outputDirectory) == false) Directory.CreateDirectory(outputDirectory);
+ templateDirectory = new DirectoryInfo(templateDirectory).FullName;
+ outputDirectory = new DirectoryInfo(outputDirectory).FullName;
+ if (templateDirectory.IndexOf(outputDirectory, StringComparison.CurrentCultureIgnoreCase) != -1) throw new ArgumentException("outputDirectory 目录不能设置在 templateDirectory 目录内");
+ var tables = dbfirst.GetTablesByDatabase(database);
+ var tpl = new TemplateEngin(templateDirectory, "FreeSql", "FreeSql.DatabaseModel");
+ BuildEachDirectory(templateDirectory, outputDirectory, tpl, dbfirst, tables);
+ tpl.Dispose();
+ }
+
+ void BuildEachDirectory(string templateDirectory, string outputDirectory, TemplateEngin tpl, IDbFirst dbfirst, List tables) {
+ if (Directory.Exists(outputDirectory) == false) Directory.CreateDirectory(outputDirectory);
+ var files = Directory.GetFiles(templateDirectory);
+ foreach (var file in files) {
+ var fi = new FileInfo(file);
+ if (string.Compare(fi.Extension, ".FreeSql", true) == 0) {
+ var outputExtension = "." + fi.Name.Split('.')[1];
+ if (fi.Name.StartsWith("for-table.")) {
+ foreach (var table in tables) {
+ var result = tpl.RenderFile(file, new Dictionary() { { "table", table }, { "dbfirst", dbfirst } });
+ var outputName = table.Name + outputExtension;
+ var mcls = Regex.Match(result, @"\s+class\s+(\w+)");
+ if (mcls.Success) outputName = mcls.Groups[1].Value + outputExtension;
+ var outputStream = Encoding.UTF8.GetBytes(result);
+ var fullname = outputDirectory + "/" + outputName;
+ if (File.Exists(fullname)) File.Delete(fullname);
+ using (var outfs = File.Open(fullname, FileMode.OpenOrCreate, FileAccess.Write)) {
+ outfs.Write(outputStream, 0, outputStream.Length);
+ outfs.Close();
+ }
+ }
+ continue;
+ } else {
+ var result = tpl.RenderFile(file, new Dictionary() { { "tables", tables }, { "dbfirst", dbfirst } });
+ var outputName = fi.Name;
+ var mcls = Regex.Match(result, @"\s+class\s+(\w+)");
+ if (mcls.Success) outputName = mcls.Groups[1].Value + outputExtension;
+ var outputStream = Encoding.UTF8.GetBytes(result);
+ var fullname = outputDirectory + "/" + outputName;
+ if (File.Exists(fullname)) File.Delete(fullname);
+ using (var outfs = File.Open(fullname, FileMode.OpenOrCreate, FileAccess.Write)) {
+ outfs.Write(outputStream, 0, outputStream.Length);
+ outfs.Close();
+ }
+ }
+ }
+ File.Copy(file, outputDirectory + file.Replace(templateDirectory, ""), true);
+ }
+ var dirs = Directory.GetDirectories(templateDirectory);
+ foreach(var dir in dirs) {
+ BuildEachDirectory(dir, outputDirectory + dir.Replace(templateDirectory, ""), tpl, dbfirst, tables);
+ }
+ }
+ }
+}
diff --git a/FreeSql/Interface/Curd/IDelete.cs b/FreeSql/Interface/Curd/IDelete.cs
new file mode 100644
index 00000000..ebb31e0e
--- /dev/null
+++ b/FreeSql/Interface/Curd/IDelete.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace FreeSql {
+ public interface IDelete where T1 : class {
+ ///
+ /// lambda表达式条件,仅支持实体基础成员(不包含导航对象)
+ ///
+ /// lambda表达式条件
+ ///
+ IDelete Where(Expression> exp);
+ ///
+ /// 原生sql语法条件,Where("id = ?id", new { id = 1 })
+ ///
+ /// sql语法条件
+ /// 参数
+ ///
+ IDelete Where(string sql, object parms = null);
+ ///