Lith 11 months ago
commit
42593f188f
70 changed files with 4480 additions and 0 deletions
  1. 77 0
      .github/workflows/ki_devops3.yml
  2. 201 0
      LICENSE
  3. 41 0
      Publish/DevOps3/build-bash/19.get-app-version.bash
  4. 46 0
      Publish/DevOps3/build-bash/20.change-app-version.bash
  5. 47 0
      Publish/DevOps3/build-bash/21.change-to-next-version.bash
  6. 41 0
      Publish/DevOps3/build-bash/22.add-suffix-to-app-version.bash
  7. 44 0
      Publish/DevOps3/build-bash/30.nuget-pack.sh
  8. 93 0
      Publish/DevOps3/build-bash/40.Station-publish.sh
  9. 23 0
      Publish/DevOps3/build-bash/41.extra-publish.sh
  10. 46 0
      Publish/DevOps3/build-bash/50.docker-image-copy.sh
  11. 27 0
      Publish/DevOps3/build-bash/51.docker-deploy-copy.sh
  12. 23 0
      Publish/DevOps3/build-bash/52.docker-extra-copy.sh
  13. 32 0
      Publish/DevOps3/build-bash/startup.bash
  14. 23 0
      Publish/DevOps3/build-cmd/30.nuget-pack.bat
  15. 31 0
      Publish/DevOps3/build-cmd/40.Station-publish(net5.0).bat
  16. 31 0
      Publish/DevOps3/build-cmd/40.Station-publish(netcoreapp2.1).bat
  17. 57 0
      Publish/DevOps3/build-cmd/40.Station-publish.bat
  18. 97 0
      Publish/DevOps3/build-cmd/41.StressTest-publish.bat
  19. 36 0
      Publish/DevOps3/build-cmd/50.docker-image-create.bat
  20. 36 0
      Publish/DevOps3/build-cmd/51.docker-deploy-copy.bat
  21. 9 0
      Publish/DevOps3/build-cmd/Clean-All.bat
  22. 1 0
      Publish/DevOps3/build-cmd/Clean-ReleaseFiles.bat
  23. 21 0
      Publish/DevOps3/build-cmd/OneKey Release.bat
  24. 46 0
      Publish/DevOps3/build-cmd/Version-To-NextTemp.bat
  25. 46 0
      Publish/DevOps3/build-cmd/Version-To-Preview.bat
  26. 43 0
      Publish/DevOps3/build-cmd/Version-To-Release.bat
  27. 32 0
      Publish/DevOps3/build-cmd/Version-getVersion.bat
  28. BIN
      Publish/DevOps3/build-cmd/VsTool.exe
  29. 1 0
      Publish/DevOps3/environment/env.APPNAME.txt
  30. 1 0
      Publish/DevOps3/environment/env.envName.txt
  31. 13 0
      Publish/DevOps3/environment/readme.md
  32. 30 0
      Publish/DevOps3/github-bash/74.github-push-to-webdav.sh
  33. 60 0
      Publish/DevOps3/github-bash/76.github-push-release.sh
  34. 70 0
      Publish/DevOps3/github-bash/startup.bash
  35. 168 0
      Publish/DevOps3/jenkins-bash/CICD.ki.git_Multibranch.deploy.jenkinsfile
  36. 43 0
      Publish/DevOps3/release-bash/71.file-zip.sh
  37. 38 0
      Publish/DevOps3/release-bash/72.nuget-push.sh
  38. 39 0
      Publish/DevOps3/release-bash/73.docker-image-build-push.sh
  39. 41 0
      Publish/DevOps3/release-bash/74.docker-image-build-push_amd64.bash
  40. 71 0
      Publish/DevOps3/release-bash/75.docker-image-build-push_cross.bash
  41. 47 0
      Publish/DevOps3/release-bash/78.push-releaseFiles-to-webdav.bash
  42. 57 0
      Publish/DevOps3/release-bash/startup.bash
  43. 29 0
      README.md
  44. 10 0
      clean-temp.bat
  45. 28 0
      doc/demo.sql
  46. BIN
      doc/vitorm_logo_v1.png
  47. 62 0
      src/Vitorm.ClickHouse/DbContext_Extensions.cs
  48. 335 0
      src/Vitorm.ClickHouse/SqlTranslateService.cs
  49. 48 0
      src/Vitorm.ClickHouse/TranslateService/ExecuteDeleteTranslateService.cs
  50. 28 0
      src/Vitorm.ClickHouse/Vitorm.ClickHouse.csproj
  51. 46 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Orm_Extensions_ToExecuteString_Test.cs
  52. 182 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Group_Test.cs
  53. 218 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_InnerJoin_ByJoin_Test.cs
  54. 135 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_InnerJoin_BySelectMany_Test.cs
  55. 107 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LeftJoin_ByGroupJoin_Test.cs
  56. 222 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LeftJoin_BySelectMany_Test.cs
  57. 51 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LinqMethods_Distinct_Test.cs
  58. 301 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LinqMethods_Test.cs
  59. 58 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_DateTime_Test.cs
  60. 37 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_Numric_Caculate_Test.cs
  61. 122 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_Numric_Test.cs
  62. 39 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_String_Caculate_Test.cs
  63. 51 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_String_Like_Test.cs
  64. 86 0
      test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_String_Test.cs
  65. 105 0
      test/Vitorm.ClickHouse.MsTest/CustomTest/CRUD_Test.cs
  66. 43 0
      test/Vitorm.ClickHouse.MsTest/CustomTest/DbFunction_Test.cs
  67. 56 0
      test/Vitorm.ClickHouse.MsTest/CustomTest/Orm_Extensions_ExecuteDelete_Test.cs
  68. 103 0
      test/Vitorm.ClickHouse.MsTest/DataSource.cs
  69. 42 0
      test/Vitorm.ClickHouse.MsTest/Vitorm.ClickHouse.MsTest.csproj
  70. 7 0
      test/Vitorm.ClickHouse.MsTest/appsettings.json

+ 77 - 0
.github/workflows/ki_devops3.yml

@@ -0,0 +1,77 @@
+# This is a basic workflow to help you get started with Actions
+
+name: ki_devops3
+
+# Controls when the action will run. 
+on:
+  
+  push:
+    # Triggers the workflow on push events for the branches start with release
+    branches:
+      - 'release/**'
+
+    # Triggers the workflow on push events but only for the master branch
+    #branches: [ master ]
+
+    # Triggers the workflow on push tag
+    #tags: ['*']
+    
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+  # This workflow contains a single job called "build"
+  build:
+    # The type of runner that the job will run on
+    runs-on: ubuntu-latest
+
+    # Steps represent a sequence of tasks that will be executed as part of the job
+    steps:
+      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+      - uses: actions/checkout@v2
+
+      # Runs a set of commands using the runners shell
+      - name: Run startup.bash
+        run: |
+           set -e
+           echo start build
+
+           export DOCKER_ImagePrefix="serset/"
+           export DOCKER_USERNAME="${{ secrets.DOCKER_USERNAME  }}"
+           export DOCKER_PASSWORD="${{ secrets.DOCKER_PASSWORD }}"
+           export NUGET_SERVER="${{ secrets.NUGET_SERVER  }}"
+           export NUGET_KEY="${{ secrets.NUGET_KEY }}"
+           export WebDav_BaseUrl="${{ secrets.WebDav_BaseUrl }}"
+           export WebDav_User="${{ secrets.WebDav_User }}"
+           cd ./Publish/DevOps3/github-bash
+           bash startup.bash
+           echo build succeed!
+           
+           # echo "appName=${APPNAME}" >> $GITHUB_ENV
+           
+           
+      - name: release_create
+        id: release_create
+        uses: actions/create-release@v1
+        #if: hashFiles(env.release_assetPath)
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          release_name: ${{ env.release_name }}
+          tag_name: ${{ env.release_tag }}
+          draft: ${{ env.release_draft }}
+          prerelease: ${{ env.release_prerelease }}
+          body: ${{ env.release_body }}
+
+
+      # release_upload_asset
+
+      - uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ steps.release_create.outputs.upload_url }}
+          asset_path: ${{ env.release_dirPath }}/${{ env.appName }}-nuget-${{ env.release_version }}.zip
+          asset_name: ${{ env.appName }}-nuget-${{ env.release_version }}.zip
+          asset_content_type: application/zip

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 41 - 0
Publish/DevOps3/build-bash/19.get-app-version.bash

@@ -0,0 +1,41 @@
+set -e
+
+# export versionSuffix='.1234.preview'
+# bash 19.get-app-version.bash
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export versionSuffix='  '
+
+# "
+
+# remove spaces
+versionSuffix=${versionSuffix// /}
+
+#----------------------------------------------
+# basePath
+if [ -z "$basePath" ]; then basePath=$PWD/../../..; fi
+
+
+
+#----------------------------------------------
+echo "#1 get appVersion"
+# get csproj file with appVersion tag, if not exist get file with pack or publish tag
+csprojPath=$(find ${basePath} -name *.csproj -exec grep '<appVersion>' -l {} \; | head -n 1);
+if [ ! -f "$csprojPath" ]; then csprojPath=$(find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \; | head -n 1);  fi
+if [ -f "$csprojPath" ]; then export appVersion=`grep '<Version>' "$csprojPath" | grep -oE '\>(.*)\<' | tr -d '<>/'`;  fi
+echo "appVersion from csproj: $appVersion"
+
+# get v1 v2 v3
+v1=$(echo $appVersion | tr '.' '\n' | sed -n 1p)
+v2=$(echo $appVersion | tr '.' '\n' | sed -n 2p)
+v3=$(echo $appVersion | tr '.-' '\n' | sed -n 3p)
+
+
+#export nextAppVersion="${appVersion%%-*}$versionSuffix"
+export nextAppVersion="$v1.$v2.$v3$versionSuffix"
+echo "nextAppVersion: $nextAppVersion"
+

+ 46 - 0
Publish/DevOps3/build-bash/20.change-app-version.bash

@@ -0,0 +1,46 @@
+set -e
+
+# export versionSuffix='.1234.preview'
+# bash 20.change-app-version.bash
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export versionSuffix='  '
+
+# "
+
+# remove spaces
+versionSuffix=${versionSuffix// /}
+
+#----------------------------------------------
+# basePath
+if [ -z "$basePath" ]; then basePath=$PWD/../../..; fi
+
+
+
+#----------------------------------------------
+echo "#1 get appVersion"
+# get csproj file with appVersion tag, if not exist get file with pack or publish tag
+csprojPath=$(find ${basePath} -name *.csproj -exec grep '<appVersion>' -l {} \; | head -n 1);
+if [ ! -f "$csprojPath" ]; then csprojPath=$(find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \; | head -n 1);  fi
+if [ -f "$csprojPath" ]; then export appVersion=`grep '<Version>' "$csprojPath" | grep -oE '\>(.*)\<' | tr -d '<>/'`;  fi
+echo "appVersion from csproj: $appVersion"
+
+# get v1 v2 v3
+v1=$(echo $appVersion | tr '.' '\n' | sed -n 1p)
+v2=$(echo $appVersion | tr '.' '\n' | sed -n 2p)
+v3=$(echo $appVersion | tr '.-' '\n' | sed -n 3p)
+
+
+#export nextAppVersion="${appVersion%%-*}$versionSuffix"
+export nextAppVersion="$v1.$v2.$v3$versionSuffix"
+echo "nextAppVersion: $nextAppVersion"
+
+
+#----------------------------------------------
+echo "#2 change app version from [$appVersion] to [$nextAppVersion]" 
+sed -i 's/'"<Version>$appVersion<\/Version>"'/'"<Version>$nextAppVersion<\/Version>"'/g'  `find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \;`
+

+ 47 - 0
Publish/DevOps3/build-bash/21.change-to-next-version.bash

@@ -0,0 +1,47 @@
+set -e
+
+# export versionSuffix='.1234.preview'
+# bash 21.change-to-next-version.bash
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export versionSuffix='  '
+
+# "
+
+# remove spaces
+versionSuffix=${versionSuffix// /}
+
+#----------------------------------------------
+# basePath
+if [ -z "$basePath" ]; then basePath=$PWD/../../..; fi
+
+
+
+#----------------------------------------------
+echo "#1 get appVersion"
+# get csproj file with appVersion tag, if not exist get file with pack or publish tag
+csprojPath=$(find ${basePath} -name *.csproj -exec grep '<appVersion>' -l {} \; | head -n 1);
+if [ ! -f "$csprojPath" ]; then csprojPath=$(find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \; | head -n 1);  fi
+if [ -f "$csprojPath" ]; then export appVersion=`grep '<Version>' "$csprojPath" | grep -oE '\>(.*)\<' | tr -d '<>/'`;  fi
+echo "appVersion from csproj: $appVersion"
+
+# get v1 v2 v3
+v1=$(echo $appVersion | tr '.' '\n' | sed -n 1p)
+v2=$(echo $appVersion | tr '.' '\n' | sed -n 2p)
+v3=$(echo $appVersion | tr '.-' '\n' | sed -n 3p)
+((v3++));
+
+
+#export nextAppVersion="${appVersion%%-*}$versionSuffix"
+export nextAppVersion="$v1.$v2.$v3$versionSuffix"
+echo "nextAppVersion: $nextAppVersion"
+
+#----------------------------------------------
+echo "#2 change app version from [$appVersion] to [$nextAppVersion]" 
+sed -i 's/'"<Version>$appVersion<\/Version>"'/'"<Version>$nextAppVersion<\/Version>"'/g'  `find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \;`
+
+

+ 41 - 0
Publish/DevOps3/build-bash/22.add-suffix-to-app-version.bash

@@ -0,0 +1,41 @@
+set -e
+
+# export versionSuffix='.1234.preview'
+# bash 22.add-suffix-to-app-version.bash
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export versionSuffix='  '
+
+# "
+
+# remove spaces
+versionSuffix=${versionSuffix// /}
+
+#----------------------------------------------
+# basePath
+if [ -z "$basePath" ]; then basePath=$PWD/../../..; fi
+
+
+
+#----------------------------------------------
+echo "#1 get appVersion"
+# get csproj file with appVersion tag, if not exist get file with pack or publish tag
+csprojPath=$(find ${basePath} -name *.csproj -exec grep '<appVersion>' -l {} \; | head -n 1);
+if [ ! -f "$csprojPath" ]; then csprojPath=$(find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \; | head -n 1);  fi
+if [ -f "$csprojPath" ]; then export appVersion=`grep '<Version>' "$csprojPath" | grep -oE '\>(.*)\<' | tr -d '<>/'`;  fi
+echo "appVersion from csproj: $appVersion"
+
+
+
+export nextAppVersion="${appVersion}${versionSuffix}"
+echo "nextAppVersion: $nextAppVersion"
+
+
+#----------------------------------------------
+echo "#2 change app version from [$appVersion] to [$nextAppVersion]" 
+sed -i 's/'"<Version>$appVersion<\/Version>"'/'"<Version>$nextAppVersion<\/Version>"'/g'  `find ${basePath} -name *.csproj -exec grep '<pack>\|<publish>' -l {} \;`
+

+ 44 - 0
Publish/DevOps3/build-bash/30.nuget-pack.sh

@@ -0,0 +1,44 @@
+set -e
+
+# bash 30.nuget-pack.sh
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+export NUGET_PATH=$basePath/Publish/release/.nuget
+
+# "
+
+if [ ! $NUGET_PATH ]; then NUGET_PATH=$basePath/Publish/release/.nuget; fi
+
+
+nugetPath=Publish/release/release/nuget
+mkdir -p $basePath/Publish/release/release
+
+
+
+#----------------------------------------------
+echo "30.nuget-pack.sh"
+docker run -i --rm \
+--env LANG=C.UTF-8 \
+-v $NUGET_PATH:/root/.nuget \
+-v $basePath:/root/code \
+serset/dotnet:sdk-6.0 \
+bash -c "
+cd /root/code
+for file in \$(grep -a '<pack>nuget</pack>' . -rl --include *.csproj)
+do
+	echo pack \$file
+	mkdir -p /root/code/$nugetPath
+	cd /root/code
+	cd \$(dirname \"\$file\")
+	dotnet build --configuration Release
+	dotnet pack --configuration Release --output '/root/code/$nugetPath'
+done
+"
+
+
+ 

+ 93 - 0
Publish/DevOps3/build-bash/40.Station-publish.sh

@@ -0,0 +1,93 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+export NUGET_PATH=$basePath/Publish/release/.nuget
+
+# "
+
+if [ ! $NUGET_PATH ]; then NUGET_PATH=$basePath/Publish/release/.nuget; fi
+
+
+#----------------------------------------------
+echo "#40.Station-publish.sh -> find projects and build"
+
+export devOpsPath="$PWD/.."
+
+docker run -i --rm \
+--env LANG=C.UTF-8 \
+-v $NUGET_PATH:/root/.nuget \
+-v "$basePath":/root/code \
+-v "$basePath":"$basePath" \
+serset/dotnet:sdk-6.0 \
+bash -c "
+set -e
+
+if grep '<publish>' -r --include *.csproj /root/code; then
+	echo '#40.Station-publish.sh -> got projects need to be built'
+else
+	echo '#40.Station-publish.sh -> skip for no project needs to be built'
+	exit 0
+fi
+
+echo '#1 get netVersion'
+export netVersion=\$(grep '<TargetFramework>' \$(grep '<publish>' -rl --include *.csproj /root/code | head -n 1) | grep -oP '>(.*)<' | tr -d '<>')
+echo netVersion: \$netVersion
+
+
+export basePath=/root/code
+export publishPath=\$basePath/Publish/release/release/Station\(\$netVersion\)
+mkdir -p \$publishPath
+
+echo '#2 publish station'
+cd \$basePath
+for file in \$(grep -a '<publish>' . -rl --include *.csproj)
+do
+	cd \$basePath
+	
+	#get publishName
+	publishName=\`grep '<publish>' \$file -r | grep -oP '>(.*)<' | tr -d '<>'\`
+
+	echo publish \$publishName
+
+	#publish
+	cd \$(dirname \"\$file\")
+	dotnet build --configuration Release
+	dotnet publish --configuration Release --output \"\$publishPath/\$publishName\"
+
+	#copy xml
+	for filePath in bin/Release/\$netVersion/*.xml ; do \\cp -rf \$filePath \"\$publishPath/\$publishName\";done
+done
+
+
+#3 copy station release files
+if [ -d \"\$basePath/Publish/ReleaseFile/Station\" ]; then
+	echo '#3 copy station release files'
+	\cp -rf \$basePath/Publish/ReleaseFile/Station/. \"\$publishPath\"
+fi
+
+
+#4 copy extra release files
+bashFile=\"$devOpsPath/environment/build-bash__40.Station-publish__#4_copyExtraReleaseFiles.sh\"
+if [ -f \"\$bashFile\" ]; then
+	echo '#4 copy extra release files'
+	sh \"\$bashFile\"
+fi
+
+
+
+
+"
+
+
+
+echo '#40.Station-publish.sh -> success!'
+
+
+
+
+

+ 23 - 0
Publish/DevOps3/build-bash/41.extra-publish.sh

@@ -0,0 +1,23 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+export NUGET_PATH=$basePath/Publish/release/.nuget
+
+# "
+
+
+#---------------------------------------------------------------------
+#41.extra-publish.sh
+bashFile="$PWD/../environment/build-bash__41.extra-publish.sh"
+if [ -f "$bashFile" ]; then
+	echo '#41.extra-publish.sh'
+	sh "$bashFile"
+fi
+
+
+

+ 46 - 0
Publish/DevOps3/build-bash/50.docker-image-copy.sh

@@ -0,0 +1,46 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+# "
+
+ 
+#---------------------------------------------------------------------
+#1 copy docker image from ReleaseFile
+
+publishPath="$basePath/Publish/release/release/Station(net6.0)"
+dockerPath=$basePath/Publish/release/release/docker-image
+
+
+if [ -d "$basePath/Publish/ReleaseFile/docker-image" ]; then
+	echo "50.docker-image-copy.sh -> #1 copy docker image from ReleaseFile"
+	\cp -rf "$basePath/Publish/ReleaseFile/docker-image/." "$dockerPath"
+fi
+
+
+
+
+#---------------------------------------------------------------------
+echo "50.docker-image-copy.sh -> #2 copy station"
+for file in $(find $basePath -name *.csproj -exec grep '<docker>' -l {} \;)
+do
+	cd $basePath
+	
+	publishName=`grep '<publish>' $file -r | grep -oE '\>(.*)\<' | tr -d '<>/'`
+	
+	dockerName=`grep '<docker>' $file -r | grep -oE '\>(.*)\<' | tr -d '<>/'`
+
+	echo "#2.* copy $dockerName, publishName:$publishName"
+	\cp -rf "$publishPath/$publishName/." "$dockerPath/$dockerName/app"
+done
+
+
+
+
+
+

+ 27 - 0
Publish/DevOps3/build-bash/51.docker-deploy-copy.sh

@@ -0,0 +1,27 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+# "
+
+ 
+#---------------------------------------------------------------------
+#2
+publishPath="$basePath/Publish/release/release/Station(net6.0)"
+deployPath="$basePath/Publish/release/release/docker-deploy"
+
+
+
+#----------------------------------------------
+#3 copy dir
+if [ -d "$basePath/Publish/ReleaseFile/docker-deploy" ]; then
+	echo "51.docker-deploy-copy.sh -> copy dir"
+	\cp -rf "$basePath/Publish/ReleaseFile/docker-deploy/." "$deployPath"
+fi
+
+

+ 23 - 0
Publish/DevOps3/build-bash/52.docker-extra-copy.sh

@@ -0,0 +1,23 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+export NUGET_PATH=$basePath/Publish/release/.nuget
+
+# "
+
+
+#---------------------------------------------------------------------
+#52.docker-extra-copy.sh
+bashFile="$PWD/../environment/build-bash__52.docker-extra-copy.sh"
+if [ -f "$bashFile" ]; then
+	echo '#52.docker-extra-copy.sh'
+	sh "$bashFile"
+fi
+
+
+

+ 32 - 0
Publish/DevOps3/build-bash/startup.bash

@@ -0,0 +1,32 @@
+set -e
+
+# cd build-bash; bash startup.bash;
+
+#----------------------------------------------
+# cur path
+curPath=$PWD
+
+cd $curPath/../../..
+export basePath="$PWD"
+cd $curPath
+
+
+
+
+
+#----------------------------------------------
+echo "build-bash/startup.bash"
+
+for file in *.sh
+do
+    echo "-----------------------------------------------------------------"
+    echo "[$(date "+%H:%M:%S")] sh $file"
+    sh $file
+done
+
+
+
+ 
+#----------------------------------------------
+#
+cd $curPath

+ 23 - 0
Publish/DevOps3/build-cmd/30.nuget-pack.bat

@@ -0,0 +1,23 @@
+@echo off
+
+::1 get basePath
+set curPath=%cd%
+cd /d "%~dp0"
+cd /d ../../..
+set basePath=%cd%
+set nugetPath=%basePath%/Publish/release/release/nuget
+
+::2 find nuget projects and pack
+for /f "delims=" %%f in ('findstr /M /s /i "<pack>nuget</pack>" *.csproj') do (
+	echo pack %basePath%\%%f\..
+	cd /d "%basePath%\%%f\.."
+	dotnet build --configuration Release
+	dotnet pack --configuration Release --output "%nugetPath%"
+	@if errorlevel 1 (echo . & echo .  & echo error & pause) 
+)
+
+
+echo %~n0.bat success
+
+
+cd /d "%curPath%"

+ 31 - 0
Publish/DevOps3/build-cmd/40.Station-publish(net5.0).bat

@@ -0,0 +1,31 @@
+@echo off
+
+
+:: #1 init
+set netVersion=net5.0
+
+
+:: #2 change netcore version
+VsTool.exe replace -r --path "../../.." --file "App.Gateway.csproj|App.Gover.Gateway.csproj|App.ServiceCenter.csproj|Did.SersLoader.Demo.csproj|App.Robot.Station.csproj" --old "<TargetFramework>net6.0</TargetFramework>" --new "<TargetFramework>%netVersion%</TargetFramework>"
+
+
+
+
+
+:: #3 publish
+call "40.Station-publish.bat"
+
+
+
+
+
+
+:: #4 revert netcore version
+VsTool.exe replace -r --path "../../.." --file "App.Gateway.csproj|App.Gover.Gateway.csproj|App.ServiceCenter.csproj|Did.SersLoader.Demo.csproj|App.Robot.Station.csproj" --old "<TargetFramework>%netVersion%</TargetFramework>" --new "<TargetFramework>net6.0</TargetFramework>"
+
+
+
+
+
+echo %~n0.bat success
+cd /d "%curPath%"

+ 31 - 0
Publish/DevOps3/build-cmd/40.Station-publish(netcoreapp2.1).bat

@@ -0,0 +1,31 @@
+@echo off
+
+
+:: #1 init
+set netVersion=netcoreapp2.1
+
+
+:: #2 change netcore version
+VsTool.exe replace -r --path "../../.." --file "App.Gateway.csproj|App.Gover.Gateway.csproj|App.ServiceCenter.csproj|Did.SersLoader.Demo.csproj|App.Robot.Station.csproj" --old "<TargetFramework>net6.0</TargetFramework>" --new "<TargetFramework>%netVersion%</TargetFramework>"
+
+
+
+
+
+:: #3 publish
+call "40.Station-publish.bat"
+
+
+
+
+
+
+:: #4 revert netcore version
+VsTool.exe replace -r --path "../../.." --file "App.Gateway.csproj|App.Gover.Gateway.csproj|App.ServiceCenter.csproj|Did.SersLoader.Demo.csproj|App.Robot.Station.csproj" --old "<TargetFramework>%netVersion%</TargetFramework>" --new "<TargetFramework>net6.0</TargetFramework>"
+
+
+
+
+
+echo %~n0.bat success
+cd /d "%curPath%"

+ 57 - 0
Publish/DevOps3/build-cmd/40.Station-publish.bat

@@ -0,0 +1,57 @@
+@echo off
+
+::enable delayed arguments
+setlocal EnableDelayedExpansion
+
+
+:: #1 get basePath
+set curPath=%cd%
+cd /d "%~dp0"
+cd /d ../../..
+set basePath=%cd%
+
+
+:: #2 get netVersion
+set netVersion=net6.0
+for /f "tokens=3 delims=><" %%a in ('type %basePath%\dotnet\ServiceCenter\App.ServiceCenter\App.ServiceCenter.csproj^|findstr "<TargetFramework>.*TargetFramework"') do set netVersion=%%a
+
+
+set publishPath=%basePath%/Publish/release/release/Station(%netVersion%)
+echo publish Station
+echo dotnet version: %netVersion%
+
+
+
+:: #3 find projects and publish
+for /f "delims=" %%f in ('findstr /M /s /i "<publish>" *.csproj') do (
+	::get name
+	for /f "tokens=3 delims=><" %%a in ('type "%basePath%\%%f"^|findstr "<publish>.*publish"') do set name=%%a
+	echo publish !name!
+
+	::publish
+	cd /d "%basePath%\%%f\.."
+	dotnet build --configuration Release
+	dotnet publish --configuration Release --output "%publishPath%\!name!"
+	@if errorlevel 1 (echo . & echo .  & echo error & pause) 
+
+	::copy xml
+	xcopy  "bin\Release\%netVersion%\*.xml" "%publishPath%\!name!" /i /r /y
+)
+
+
+
+:: #4 copy dir
+xcopy "%basePath%\Publish\ReleaseFile\Station" "%publishPath%" /e /i /r /y
+
+
+
+:: #5 copy ServiceCenter
+xcopy "%publishPath%\ServiceCenter" "%basePath%\Publish\release\release\ServiceCenter(%netVersion%)\ServiceCenter" /e /i /r /y
+xcopy "%publishPath%\01.ServiceCenter.bat" "%basePath%\Publish\release\release\ServiceCenter(%netVersion%)"
+xcopy "%publishPath%\01.Start-4580.bat" "%basePath%\Publish\release\release\ServiceCenter(%netVersion%)"
+
+
+
+
+echo %~n0.bat success
+cd /d "%curPath%"

+ 97 - 0
Publish/DevOps3/build-cmd/41.StressTest-publish.bat

@@ -0,0 +1,97 @@
+@echo off
+
+::enable delayed arguments
+setlocal EnableDelayedExpansion
+
+
+
+:: #1 get 获取basePath
+set curPath=%cd%
+cd /d "%~dp0"
+cd /d ../../..
+set basePath=%cd%
+
+
+
+:: #2
+set publishPath=%basePath%/Publish/release/release/StressTest
+
+
+
+
+
+echo ------------------------------------------------------------------
+echo "#3 publish CL stressTest"
+
+::Client
+cd /d "%basePath%\dotnet\Library\Sers\Sers.CL\Test\CommunicationManage\CmClient"
+dotnet build --configuration Release
+dotnet publish --configuration Release --output "%publishPath%\CL压测net6.0\CmClient"
+@if errorlevel 1 (echo . & echo .  & echo error & pause) 
+
+::Server
+cd /d "%basePath%\dotnet\Library\Sers\Sers.CL\Test\CommunicationManage\CmServer"
+dotnet build --configuration Release
+dotnet publish --configuration Release --output "%publishPath%\CL压测net6.0\CmServer"
+@if errorlevel 1 (echo . & echo .  & echo error & pause) 
+
+
+::copy bat
+xcopy  "%basePath%\Publish\ReleaseFile\StressTest\CL压测" "%publishPath%\CL压测net6.0" /e /i /r /y
+
+
+
+
+echo ------------------------------------------------------------------
+:: #4 publish Sers stressTest
+for %%i in (net6.0) do (  
+	set netVersion=%%i
+	set appPath=%basePath%/Publish/release/release/Station^(!netVersion!^)
+
+	echo 发布 压测-!netVersion!
+
+	::单体压测
+	set targetPath=%publishPath%/单体压测!netVersion!
+
+	::(x.x.1)copy ServiceCenter
+	xcopy "!appPath!\ServiceCenter" "!targetPath!\ServiceCenter" /e /i /r /y
+
+	::(x.x.2)copy demo
+	xcopy "!appPath!\Demo\wwwroot" "!targetPath!\ServiceCenter\wwwroot" /e /i /r /y
+	xcopy "!appPath!\Demo\Did.SersLoader.Demo.dll" "!targetPath!\ServiceCenter" /i /r /y
+	xcopy "!appPath!\Demo\Did.SersLoader.Demo.pdb" "!targetPath!\ServiceCenter" /i /r /y
+	xcopy "!appPath!\Demo\Did.SersLoader.Demo.xml" "!targetPath!\ServiceCenter" /i /r /y
+
+	::(x.x.3)copy Robot
+	xcopy "!appPath!\Robot\wwwroot" "!targetPath!\ServiceCenter\wwwroot" /e /i /r /y
+	xcopy "!appPath!\Robot\App.Robot.Station.dll" "!targetPath!\ServiceCenter" /i /r /y
+	xcopy "!appPath!\Robot\App.Robot.Station.pdb" "!targetPath!\ServiceCenter" /i /r /y
+	xcopy "!appPath!\Robot\App.Robot.Station.xml" "!targetPath!\ServiceCenter" /i /r /y
+
+	::(x.x.4)copy ReleaseFile
+	xcopy "%basePath%\Publish\ReleaseFile\StressTest\单体压测" "!targetPath!" /e /i /r /y
+
+
+
+	::分布式压测
+	set targetPath=%publishPath%/分布式压测!netVersion!
+
+	::(x.x.1)copy  station
+	xcopy "!appPath!\ServiceCenter" "!targetPath!\ServiceCenter" /e /i /r /y
+	xcopy "!appPath!\Demo" "!targetPath!\Demo" /e /i /r /y
+	xcopy "!appPath!\Robot" "!targetPath!\Robot" /e /i /r /y
+
+	::(x.x.2)copy ReleaseFile
+	xcopy  "%basePath%\Publish\ReleaseFile\StressTest\分布式压测" "!targetPath!" /e /i /r /y
+)
+
+
+
+ 
+
+
+echo %~n0.bat success
+cd /d "%curPath%"
+
+
+

+ 36 - 0
Publish/DevOps3/build-cmd/50.docker-image-create.bat

@@ -0,0 +1,36 @@
+@echo off
+
+
+echo %~n0.bat start...
+
+
+:: #1 get basePath
+set curPath=%cd%
+cd /d "%~dp0"
+cd /d ../../..
+set basePath=%cd%
+
+:: #2
+set publishPath=%basePath%/Publish/release/release/Station(net6.0)
+set dockerPath=%basePath%/Publish/release/release/docker-image
+rd /s /q "%dockerPath%"
+
+
+:: #3 copy dir
+xcopy "%basePath%/Publish/ReleaseFile/docker-image" "%dockerPath%" /e /i /r /y
+
+
+:: #4 copy station 
+xcopy  "%publishPath%/ServiceCenter" "%dockerPath%/sers/app" /e /i /r /y
+xcopy  "%publishPath%/Gateway" "%dockerPath%/sers-gateway/app" /e /i /r /y
+xcopy  "%publishPath%/Gover" "%dockerPath%/sers-gover/app" /e /i /r /y
+xcopy  "%publishPath%/Demo" "%dockerPath%/sers-demo/app" /e /i /r /y
+xcopy  "%publishPath%/Robot" "%dockerPath%/sers-demo-robot/app" /e /i /r /y
+xcopy  "%basePath%/Publish/release/release/压测/单体压测net6.0/ServiceCenter" "%dockerPath%/sers-demo-sersall/app" /e /i /r /y
+ 
+
+
+
+
+echo %~n0.bat success
+cd /d "%curPath%"

+ 36 - 0
Publish/DevOps3/build-cmd/51.docker-deploy-copy.bat

@@ -0,0 +1,36 @@
+@echo off
+
+
+echo %~n0.bat start...
+
+
+::#1 get basePath
+set curPath=%cd%
+cd /d "%~dp0"
+cd /d ..\..\..
+set basePath=%cd%
+
+
+:: #2
+set publishPath=%basePath%\Publish\release\release\Station(net6.0)
+set dockerPath=%basePath%\Publish\release\release\docker-deploy
+rd /s /q "%dockerPath%"
+
+
+:: #3 copy dir
+xcopy "%basePath%\Publish\ReleaseFile\docker-deploy" "%dockerPath%" /e /i /r /y
+
+
+:: #4 copy station 
+xcopy "%publishPath%\ServiceCenter\appsettings.json" "%dockerPath%\sers"
+xcopy "%publishPath%\Gateway\appsettings.json" "%dockerPath%\sers-gateway"
+xcopy "%publishPath%\Gover\appsettings.json" "%dockerPath%\sers-gover"
+xcopy "%publishPath%\Demo\appsettings.json" "%dockerPath%\sers-demo"
+xcopy "%publishPath%\Robot\appsettings.json" "%dockerPath%\sers-demo-robot"
+xcopy "%basePath%\Publish\release\release\压测\单体压测net6.0\ServiceCenter\appsettings.json" "%dockerPath%\sers-demo-sersall"
+
+
+
+
+echo %~n0.bat success
+cd /d "%curPath%"

+ 9 - 0
Publish/DevOps3/build-cmd/Clean-All.bat

@@ -0,0 +1,9 @@
+VsTool.exe delete --path "..\..\.." --file "*.suo|*.user" --directory "obj|bin|.vs|packages"
+
+
+rd /s/q ..\..\release
+
+
+echo %~n0.bat success
+
+pause

+ 1 - 0
Publish/DevOps3/build-cmd/Clean-ReleaseFiles.bat

@@ -0,0 +1 @@
+rd /s/q ..\..\release

+ 21 - 0
Publish/DevOps3/build-cmd/OneKey Release.bat

@@ -0,0 +1,21 @@
+
+echo %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    
+call "30.nuget-pack.bat"
+
+echo %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    
+call "40.Station-publish.bat"
+call "40.Station-publish(netcoreapp2.1).bat"
+call "40.Station-publish(net6.0).bat"
+
+call "41.StressTest-publish.bat"
+
+echo %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    
+call "50.docker-image-create.bat"
+call "51.docker-deploy-copy.bat"
+
+echo %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    
+
+
+echo %~n0.bat success
+
+pause

+ 46 - 0
Publish/DevOps3/build-cmd/Version-To-NextTemp.bat

@@ -0,0 +1,46 @@
+@echo off
+
+:: #1 get csproj
+for /f "delims=" %%a in ('findstr /M /s /i /r "<pack> <publish>" "..\..\..\*.csproj"') do set "csproj=%%~a"
+::echo %csproj%
+
+:: #2 get version
+for /f "tokens=3 delims=><" %%a in ('type %csproj%^|findstr "<Version>.*Version"') do set version=%%a
+
+:: set version=2.1.3
+:: echo  %version%
+
+
+:: #3 get v1 v2 v3
+for /f "tokens=1 delims=-" %%i in ("%version%") do set numVersion=%%i
+
+:: v1 v2 v3
+for /f "tokens=1 delims=." %%i in ("%numVersion%") do set v1=%%i
+for /f "tokens=2 delims=." %%i in ("%numVersion%") do set v2=%%i
+for /f "tokens=3 delims=." %%i in ("%numVersion%") do set v3=%%i
+
+
+:: #4 newVersion
+set /a v3=1+%v3%
+set newVersion=%v1%.%v2%.%v3%-temp
+:: echo %newVersion%
+
+
+ 
+:: #5 replace version in csproj
+echo replace verion [%version%]-^>[%newVersion%]
+echo.
+
+VsTool.exe replace -r --path "..\..\.." --file "*.csproj" --old "<Version>%version%</Version>" --new "<Version>%newVersion%</Version>"
+VsTool.exe replace -r --path "..\..\.." --file "packages.config" --old "%version%" --new "%newVersion%"
+
+
+:: #6 replace version in docker image file
+VsTool.exe replace -r --path "..\..\..\Publish\ReleaseFile\docker-image" --file "*.md" --old "%version%" --new "%newVersion%"
+
+
+echo.
+echo.
+echo.
+echo replace version success [%version%]-^>[%newVersion%]
+pause

+ 46 - 0
Publish/DevOps3/build-cmd/Version-To-Preview.bat

@@ -0,0 +1,46 @@
+@echo off
+
+:: #1 get csproj
+for /f "delims=" %%a in ('findstr /M /s /i /r "<pack> <publish>" "..\..\..\*.csproj"') do set "csproj=%%~a"
+::echo %csproj%
+
+:: #2 get version
+for /f "tokens=3 delims=><" %%a in ('type %csproj%^|findstr "<Version>.*Version"') do set version=%%a
+
+:: set version=2.1.3
+:: echo  %version%
+
+
+:: #3 get v1 v2 v3
+for /f "tokens=1 delims=-" %%i in ("%version%") do set numVersion=%%i
+
+:: v1 v2 v3
+for /f "tokens=1 delims=." %%i in ("%numVersion%") do set v1=%%i
+for /f "tokens=2 delims=." %%i in ("%numVersion%") do set v2=%%i
+for /f "tokens=3 delims=." %%i in ("%numVersion%") do set v3=%%i
+
+
+:: #4 newVersion
+:: set /a v3=1+%v3%
+set newVersion=%v1%.%v2%.%v3%-preview
+:: echo %newVersion%
+
+
+ 
+:: #5 replace version in csproj
+echo ×Ô¶¯Ð޸İ汾ºÅ [%version%]-^>[%newVersion%]
+echo.
+
+VsTool.exe replace -r --path "..\..\.." --file "*.csproj" --old "<Version>%version%</Version>" --new "<Version>%newVersion%</Version>"
+VsTool.exe replace -r --path "..\..\.." --file "packages.config" --old "%version%" --new "%newVersion%"
+
+
+:: #6 replace version in docker image file
+VsTool.exe replace -r --path "..\..\..\Publish\ReleaseFile\docker-image" --file "*.md" --old "%version%" --new "%newVersion%"
+
+
+echo.
+echo.
+echo.
+echo replace version success [%version%]-^>[%newVersion%]
+pause

+ 43 - 0
Publish/DevOps3/build-cmd/Version-To-Release.bat

@@ -0,0 +1,43 @@
+@echo off
+
+:: #1 get csproj
+for /f "delims=" %%a in ('findstr /M /s /i /r "<pack> <publish>" "..\..\..\*.csproj"') do set "csproj=%%~a"
+::echo %csproj%
+
+:: #2 get version
+for /f "tokens=3 delims=><" %%a in ('type %csproj%^|findstr "<Version>.*Version"') do set version=%%a
+
+:: set version=2.1.3
+:: echo  %version%
+
+
+:: #3 get v1 v2 v3
+for /f "tokens=1 delims=-" %%i in ("%version%") do set numVersion=%%i
+
+:: v1 v2 v3
+for /f "tokens=1 delims=." %%i in ("%numVersion%") do set v1=%%i
+for /f "tokens=2 delims=." %%i in ("%numVersion%") do set v2=%%i
+for /f "tokens=3 delims=." %%i in ("%numVersion%") do set v3=%%i
+
+
+:: #4 newVersion
+:: set /a v3=1+%v3%
+set newVersion=%v1%.%v2%.%v3%
+:: echo %newVersion%
+echo [%version%]-^>[%newVersion%]
+
+ 
+:: #5 replace version in csproj
+VsTool.exe replace -r --path "..\..\.." --file "*.csproj" --old "<Version>%version%</Version>" --new "<Version>%newVersion%</Version>"
+VsTool.exe replace -r --path "..\..\.." --file "packages.config" --old "%version%" --new "%newVersion%"
+
+
+:: #6 replace version in docker image file
+VsTool.exe replace -r --path "..\..\..\Publish\ReleaseFile\docker-image" --file "*.md" --old "%version%" --new "%newVersion%"
+
+
+echo.
+echo.
+echo.
+echo replace version success [%version%]-^>[%newVersion%]
+pause

+ 32 - 0
Publish/DevOps3/build-cmd/Version-getVersion.bat

@@ -0,0 +1,32 @@
+@echo off
+
+:: get current version
+
+
+:: #1 get csproj
+for /f "delims=" %%a in ('findstr /M /s /i /r "<pack> <publish>" "..\..\..\*.csproj"') do set "csproj=%%~a"
+echo %csproj%
+
+:: #2 get version
+for /f "tokens=3 delims=><" %%a in ('type %csproj%^|findstr "<Version>.*Version"') do set version=%%a
+
+:: set version=2.1.3
+:: echo  %version%
+
+
+:: #3 get v1 v2 v3
+for /f "tokens=1 delims=-" %%i in ("%version%") do set numVersion=%%i
+
+:: v1 v2 v3
+for /f "tokens=1 delims=." %%i in ("%numVersion%") do set v1=%%i
+for /f "tokens=2 delims=." %%i in ("%numVersion%") do set v2=%%i
+for /f "tokens=3 delims=." %%i in ("%numVersion%") do set v3=%%i
+
+
+:: #4 newVersion
+::set /a v3=1+%v3%
+set  newVersion=%v1%.%v2%.%v3%-preview
+echo [%version%]-^>[%newVersion%]
+
+
+pause

BIN
Publish/DevOps3/build-cmd/VsTool.exe


+ 1 - 0
Publish/DevOps3/environment/env.APPNAME.txt

@@ -0,0 +1 @@
+Vitorm.ClickHouse

+ 1 - 0
Publish/DevOps3/environment/env.envName.txt

@@ -0,0 +1 @@
+ki

+ 13 - 0
Publish/DevOps3/environment/readme.md

@@ -0,0 +1,13 @@
+
+# DevOps 3.3
+> 2024-06-18
+
+# build-bash
+extra steps when building, all sh files could be remove if not needed.
+
+
+
+
+
+
+

+ 30 - 0
Publish/DevOps3/github-bash/74.github-push-to-webdav.sh

@@ -0,0 +1,30 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export APPNAME=xxxx
+export appVersion=xxxx
+
+export WebDav_BaseUrl="https://nextcloud.xxx.com/remote.php/dav/files/release/releaseFiles/ki_jenkins"
+export WebDav_User="username:pwd"
+
+# "
+
+
+
+
+
+#----------------------------------------------
+if [ -z "$WebDav_BaseUrl" ]; then
+	echo "github skip pushing release file to WebDav because invalid WebDav endpoint"
+else
+	echo "github push release file to WebDav"
+	bash "$PWD/../release-bash/78.push-releaseFiles-to-webdav.bash";
+fi
+
+

+ 60 - 0
Publish/DevOps3/github-bash/76.github-push-release.sh

@@ -0,0 +1,60 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export appVersion=1.0-preview
+
+export APPNAME=xxxxxx
+
+# "
+
+
+ 
+
+
+#---------------------------------------------------------------------
+#2 init environment for github release
+
+
+
+echo "appName=${APPNAME}" >> $GITHUB_ENV
+
+echo "release_name=${APPNAME}-${appVersion}" >> $GITHUB_ENV
+echo "release_tag=${appVersion}" >> $GITHUB_ENV
+
+echo "release_draft=false" >> $GITHUB_ENV
+echo "release_prerelease=false" >> $GITHUB_ENV
+
+echo "release_body=${APPNAME} ${appVersion}" >> $GITHUB_ENV
+
+
+echo "release_dirPath=${basePath}/Publish/release/release-zip" >> $GITHUB_ENV
+echo "release_version=${appVersion}" >> $GITHUB_ENV
+
+#filePath=$basePath/Publish/release/release-zip/Sers-ServiceCenter(net6.0)-${appVersion}.zip
+#fileType="${filePath##*.}"
+#echo "release_assetPath=${filePath}" >> $GITHUB_ENV
+#echo "release_assetName=${APPNAME}-${appVersion}.${fileType}" >> $GITHUB_ENV
+#echo "release_contentType=application/zip" >> $GITHUB_ENV
+
+
+# draft or preivew
+if [ "preview" = "$(echo $appVersion | tr -d \"0-9\-\\.\")" ]
+then
+  echo preivew
+  echo "release_prerelease=true" >> $GITHUB_ENV
+else
+  if  [ "" = "$(echo $appVersion | tr -d \"0-9\-\\.\")" ]
+  then
+    echo release
+  else
+    echo draft
+    echo "release_draft=true" >> $GITHUB_ENV
+  fi
+fi
+

+ 70 - 0
Publish/DevOps3/github-bash/startup.bash

@@ -0,0 +1,70 @@
+set -e
+
+# cd github-bash; bash startup.bash;
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+#export APPNAME=xxxxxx
+
+export DOCKER_USERNAME=serset
+export DOCKER_PASSWORD=xxxxxx
+
+export NUGET_SERVER=https://api.nuget.org/v3/index.json
+export NUGET_KEY=xxxxxx
+
+export WebDav_BaseUrl="https://nextcloud.xxx.com/remote.php/dav/files/release/releaseFiles/ki_jenkins"
+export WebDav_User="username:pwd"
+
+# "
+
+#----------------------------------------------
+# cur path
+curPath=$PWD
+
+cd $curPath/../../..
+export basePath="$PWD"
+cd $curPath
+
+export devOpsPath="$PWD/.."
+
+# export basePath=/root/temp/svn
+
+if [ ! $APPNAME ]; then 
+	export APPNAME=$(cat "$devOpsPath/environment/env.APPNAME.txt" | tr -d '\n')
+	echo "APPNAME: [${APPNAME}]" 
+fi
+
+#---------------------------------------------- 
+echo '#1 build'
+cd "$devOpsPath/build-bash"; bash startup.bash;
+
+#---------------------------------------------- 
+echo '#2 release-bash'
+cd "$devOpsPath/release-bash"; bash startup.bash;
+ 
+
+
+#----------------------------------------------
+echo "#3 get appVersion" 
+cd "$devOpsPath/build-bash"; source 19.get-app-version.bash;
+
+
+
+#----------------------------------------------
+echo "#4 bash"
+cd $curPath
+for file in *.sh
+do
+    echo "-----------------------------------------------------------------"
+    echo "[$(date "+%H:%M:%S")]" sh $file
+    sh $file
+done
+
+
+
+
+
+
+

+ 168 - 0
Publish/DevOps3/jenkins-bash/CICD.ki.git_Multibranch.deploy.jenkinsfile

@@ -0,0 +1,168 @@
+def remote = [:]
+remote.name = "dind-ssh"
+remote.host = "dind"
+remote.port = 22
+remote.user = "  "
+remote.password = "  "
+remote.allowAnyHosts = true
+
+pipeline {
+    agent any
+
+    environment {
+        // get APPNAME
+        // APPNAME = "Sers"
+        APPNAME = readFile("Publish/DevOps3/environment/env.APPNAME.txt")
+
+        envName = readFile("Publish/DevOps3/environment/env.envName.txt")
+        versionSuffix = "-${envName}${env.build_number}"
+
+        // basePath = "/root/docker-cache/jenkins/jenkins_home/workspace/${APPNAME}/${envName}/${env.BRANCH_NAME}/${env.build_number}"
+        basePath = "${env.WORKSPACE}"
+
+        // NUGET_PATH = "/root/docker-cache/jenkins/jenkins_home/workspace/.nuget"
+        NUGET_PATH = credentials("NUGET_PATH")
+        NUGET_SERVER = credentials("NUGET_SERVER")
+        NUGET_KEY = credentials("NUGET_KEY")
+
+        DOCKER_SERVER = credentials("DOCKER_SERVER")
+        DOCKER_ImagePrefix = "${DOCKER_SERVER}/${envName}/"
+        DOCKER_Buildx = false
+        DOCKER_USERNAME = " "
+        DOCKER_PASSWORD = " "
+
+        // set to "  "  if want to skip save releaseFiles to WebDav
+        WebDav_BaseUrl = credentials("WebDav_BaseUrl")
+        // WebDav_User = "username:pwd"
+        WebDav_User = credentials("WebDav_User")
+
+        build_crossPlatform = "no"
+
+        dind_ssh_account = credentials("dind_ssh_account")
+    }
+
+    stages {
+        stage('#1 deploy ?') {
+            steps {
+                timeout(time:600,unit:'SECONDS') {
+                    script {
+                        remote.user = "${dind_ssh_account_USR}"
+                        remote.password = "${dind_ssh_account_PSW}"
+
+                        env.codePath = "/root/docker-cache/jenkins/" + basePath.substring(5, basePath.length()) 
+
+                        echo "-------- APPNAME: [$APPNAME]"
+                        echo "-------- basePath: [$basePath]"
+                        echo "-------- DOCKER_ImagePrefix: [$DOCKER_ImagePrefix]"
+                        echo "-------- codePath: [$codePath]"
+
+                        env.inputChoice = "no"
+                        env.inputChoice = input message: "deploy ?", 
+                            ok: 'Proceed?', 
+                            parameters: [choice(choices:["yes","no"], description: 'if not please select no', name: 'choice')]
+                    }
+                }
+            }
+        }
+
+        stage('#2 change version') {
+            when { expression { env.inputChoice == "yes" } }
+            steps {
+                script {
+                    echo "#2.1 change-app-version"
+                    sshCommand remote: remote, command:  "sh -c 'set -e; export versionSuffix=$versionSuffix;    cd $codePath/Publish/DevOps3/build-bash; source 22.add-suffix-to-app-version.bash;    echo -n \"\$nextAppVersion\" > $codePath/Publish/DevOps3/environment/env.appVersion.txt '"
+
+                    echo "#2.2 get app version"
+                    env.appVersion = readFile("Publish/DevOps3/environment/env.appVersion.txt")
+                    echo "-------- appVersion: [${env.appVersion}]"
+                }
+            }
+        }
+
+        stage('#3.1 build - single platflorm') {
+            when { expression { env.inputChoice == "yes" } }
+            steps {
+                script {
+                    sshCommand remote: remote, command:  "sh -c 'set -e; export APPNAME=$APPNAME; export NUGET_PATH=$NUGET_PATH;    cd $codePath/Publish/DevOps3/build-bash; sh startup.bash;  '"
+                }
+            }
+        }
+
+        stage('#3.2 build - cross platform') {
+            when { expression { env.inputChoice == "yes" && env.build_crossPlatform == "yes" } }
+            steps {
+                script {
+                    sshCommand remote: remote, command:  "sh -c 'set -e; export APPNAME=$APPNAME; export NUGET_PATH=$NUGET_PATH;    cd $codePath/Publish/DevOps3/build-bash; sh 40.Station-publish-multiple.bash;  '"
+                }
+            }
+        }
+
+        stage('#4 publish') {
+            when { expression { env.inputChoice == "yes" } }
+            steps {
+                script {
+                    sshCommand remote: remote, command:  "sh -c 'set -e; export APPNAME=$APPNAME;export NUGET_PATH=$NUGET_PATH;    export NUGET_SERVER=$NUGET_SERVER;export NUGET_KEY=$NUGET_KEY;  export DOCKER_Buildx=${env.DOCKER_Buildx};export DOCKER_ImagePrefix=${env.DOCKER_ImagePrefix};export DOCKER_USERNAME=${env.DOCKER_USERNAME};export DOCKER_PASSWORD=${env.DOCKER_PASSWORD};export DOCKER_BuildxExtArgs=\"--output=type=registry,registry.insecure=true\";    cd $codePath/Publish/DevOps3/release-bash; sh startup.bash;  '"
+                }
+            }
+        }
+
+        stage('#5 save releaseFiles') {
+            when { expression { env.inputChoice == "yes" && env.WebDav_BaseUrl != "  " } }
+            steps {
+                script {
+                    sshCommand remote: remote, command:  "sh -c 'set -e; export basePath=\"$codePath\"; export APPNAME=$APPNAME; export appVersion=\"$appVersion\";   export WebDav_BaseUrl=\"$WebDav_BaseUrl\"; export WebDav_User=\"$WebDav_User\";    cd $codePath/Publish/DevOps3/release-bash; sh 78.push-releaseFiles-to-webdav.bash;  '"
+                }
+            }
+        }
+
+        stage('#6 deploy') {
+            when { expression { env.inputChoice == "yes" && env.build_deploy == "yes" } }
+            steps {
+                script {
+
+                    remote.name = "k8s-ssh"
+                    remote.host = "k8s.lith.cloud"
+
+                    remote.user = "${k8s_ssh_account_USR}"
+                    remote.password = "${k8s_ssh_account_PSW}"
+
+                    sshCommand remote: remote, command:  "sh -c 'set -e;  cd /home/DataStore/Local/Data/2000/200.flowtea-prod/helm;    helm upgrade flowtea --set appNum=200 --set debug=true --set resources.limits=false --set image.host=\"\" --set image.tag=\"${env.appVersion}\" --set storageClass.ssd=nfs-prod-ssd --set storageClass.hdd=nfs-prod-hdd ./flowtea -n flowtea;  '"
+                }
+            }
+        }
+
+    }
+
+    post {
+        always {
+            timeout(time:3600,unit:'SECONDS') {
+                script {
+                    env.inputChoice = "yes"
+                    env.inputChoice = input message: "Clean temp files, \n will wait for 3600 seconds. \n click abort to skip clean.", 
+                            ok: 'Proceed', 
+                            parameters: [choice(choices:["yes","no"], name: 'choice')]
+               }
+            }
+            script {
+                if ( inputChoice == "yes" ) {
+                    echo "clean up workspace directory"
+                    cleanWs()
+
+                    // clean up tmp directory
+                    dir("${workspace}@tmp") {
+                        deleteDir()
+                    }
+                }
+            }
+        }
+        success {
+            echo "build success !"
+        }
+        failure {
+            echo "build failure !"
+        }
+        aborted {
+            echo "build aborted !"
+        }
+    }
+}

+ 43 - 0
Publish/DevOps3/release-bash/71.file-zip.sh

@@ -0,0 +1,43 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export appVersion=1.0
+
+export APPNAME=xxxxxx
+
+# "
+
+#----------------------------------------------
+echo "71.file-zip.sh"
+
+if [ ! -d "$basePath/Publish/release/release" ]; then
+    echo '71.file-zip.sh -> skip for no files exist'
+    exit 0
+fi
+
+docker run --rm -i \
+-v $basePath:/root/code \
+serset/filezip bash -c "
+set -e
+
+releasePath=/root/code/Publish/release
+rm -rf \$releasePath/release-zip
+
+for dirname in \`ls /root/code/Publish/release/release\`
+do
+  if [ -d \$releasePath/release/\$dirname ]
+  then
+    filezip zip -p -i \$releasePath/release/\$dirname -o \$releasePath/release-zip/${APPNAME}-\${dirname}-${appVersion}.zip 
+  fi
+done
+
+echo zip files:
+ls /root/code/Publish/release/release-zip
+
+"

+ 38 - 0
Publish/DevOps3/release-bash/72.nuget-push.sh

@@ -0,0 +1,38 @@
+set -e
+
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export NUGET_SERVER=https://api.nuget.org/v3/index.json
+export NUGET_KEY=xxxxxxxxxx
+
+# "
+
+#----------------------------------------------
+echo "72.nuget-push.sh"
+
+if [ ! -d "$basePath/Publish/release/release/nuget" ]; then
+    echo '71.file-zip.sh -> skip for no nuget files exist'
+    exit 0
+fi
+
+
+docker run -i --rm \
+--env LANG=C.UTF-8 \
+-v $basePath:/root/code \
+serset/dotnet:sdk-6.0 \
+bash -c "
+for file in /root/code/Publish/release/release/nuget/*.nupkg
+do
+    echo nuget push \$file
+    dotnet nuget push \$file -k ${NUGET_KEY} -s ${NUGET_SERVER} --skip-duplicate
+done
+" || true
+
+
+ 

+ 39 - 0
Publish/DevOps3/release-bash/73.docker-image-build-push.sh

@@ -0,0 +1,39 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export appVersion=1.0
+
+export DOCKER_ImagePrefix=serset/
+export DOCKER_USERNAME=serset
+export DOCKER_PASSWORD=xxx
+
+export DOCKER_Buildx=false #default: true
+export DOCKER_BuildxExtArgs=
+
+# "
+
+
+
+
+#---------------------------------------------------------------------
+echo "73.docker-image-build-push.sh"
+
+if [ ! -d "$basePath/Publish/release/release/docker-image" ]; then
+    echo '73.docker-image-build-push.sh -> skip for no docker image files exist'
+    exit 0
+fi
+
+if [ "$DOCKER_Buildx" != "false" ]; then
+    sh 75.docker-image-build-push_cross.bash
+else
+    sh 74.docker-image-build-push_amd64.bash
+fi
+
+
+

+ 41 - 0
Publish/DevOps3/release-bash/74.docker-image-build-push_amd64.bash

@@ -0,0 +1,41 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export appVersion=1.0
+
+export DOCKER_ImagePrefix=serset/
+export DOCKER_USERNAME=serset
+export DOCKER_PASSWORD=xxx
+export DOCKER_BuildxExtArgs=
+
+# "
+
+
+#---------------------------------------------------------------------
+echo "74.docker-image-build-push_amd64.bash"
+
+echo "#1 login if UserName is not empty"
+if [ -n "$DOCKER_USERNAME" ]; then docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD; fi
+
+dockerPath=$basePath/Publish/release/release/docker-image
+
+for dockerName in `ls $dockerPath`
+do
+  if [ -d $dockerPath/$dockerName ]
+  then
+    echo "#2.* docker build $dockerName"
+    echo "docker build $dockerPath/$dockerName -t ${DOCKER_ImagePrefix}$dockerName:$appVersion -t ${DOCKER_ImagePrefix}$dockerName"
+    docker build $dockerPath/$dockerName -t ${DOCKER_ImagePrefix}$dockerName:$appVersion -t ${DOCKER_ImagePrefix}$dockerName
+    docker push ${DOCKER_ImagePrefix}$dockerName:$appVersion
+    docker push ${DOCKER_ImagePrefix}$dockerName
+    docker rmi -f ${DOCKER_ImagePrefix}$dockerName:$appVersion ${DOCKER_ImagePrefix}$dockerName
+  fi
+done
+
+

+ 71 - 0
Publish/DevOps3/release-bash/75.docker-image-build-push_cross.bash

@@ -0,0 +1,71 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export appVersion=1.0
+
+export DOCKER_ImagePrefix=serset/
+export DOCKER_USERNAME=serset
+export DOCKER_PASSWORD=xxx
+export DOCKER_BuildxExtArgs=
+
+# "
+
+
+
+
+#---------------------------------------------------------------------
+echo "75.docker-image-build-push_cross.bash -> #1 docker - init buildx"
+
+
+export builderName="mybuilder__${appVersion}__"
+echo "builderName: $builderName"
+
+
+echo "#1.1 docker buildx version"
+docker buildx version
+
+echo "#1.2 install binfmt_misc"
+docker run --privileged --rm tonistiigi/binfmt --install all
+
+echo "#1.3 create builder"
+if [ ! "$(docker buildx ls | grep $builderName)" ]; then docker buildx create --use --name $builderName --buildkitd-flags '--allow-insecure-entitlement security.insecure'; fi
+
+echo "#1.4 start builder"
+docker buildx inspect $builderName --bootstrap
+
+echo "#1.5 show builders and supported CPU platform"
+docker buildx ls
+
+
+
+#---------------------------------------------------------------------
+echo "75.docker-image-build-push_cross.bash -> #2 docker - build and push"
+
+echo "#2.1 login if UserName is not empty"
+if [ -n "$DOCKER_USERNAME" ]; then docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD; fi
+
+dockerPath=$basePath/Publish/release/release/docker-image
+
+for dockerName in `ls $dockerPath`
+do
+  if [ -d $dockerPath/$dockerName ]
+  then
+    platform="linux/amd64,linux/arm64,linux/arm/v7"
+    if [ -f "$dockerPath/$dockerName/Dockerfile.platform" ]; then platform=`cat "$dockerPath/$dockerName/Dockerfile.platform"`; fi
+
+    echo "#2.* docker build $dockerName, platform: $platform"
+    echo "docker buildx build --allow security.insecure $dockerPath/$dockerName -t ${DOCKER_ImagePrefix}$dockerName:$appVersion -t ${DOCKER_ImagePrefix}$dockerName --platform=$platform --push $DOCKER_BuildxExtArgs --builder $builderName"
+    docker buildx build --allow security.insecure $dockerPath/$dockerName -t ${DOCKER_ImagePrefix}$dockerName:$appVersion -t ${DOCKER_ImagePrefix}$dockerName --platform=$platform --push $DOCKER_BuildxExtArgs --builder $builderName
+  fi
+done
+
+
+#---------------------------------------------------------------------
+echo "75.docker-image-build-push_cross.bash -> #3 remove builder"
+if [ "$(docker buildx ls | grep $builderName)" ]; then docker buildx rm $builderName; fi

+ 47 - 0
Publish/DevOps3/release-bash/78.push-releaseFiles-to-webdav.bash

@@ -0,0 +1,47 @@
+set -e
+
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export basePath=/root/temp/svn
+
+export APPNAME=Vit.Library
+export appVersion=2.2.14
+
+export WebDav_BaseUrl="https://nextcloud.xxx.com/remote.php/dav/files/release/releaseFiles/ki_jenkins"
+export WebDav_User="username:pwd"
+
+# "
+
+
+# will not throw errors if WebDav service is not available
+
+
+#---------------------------------------------------------------------
+echo "78.push-releaseFiles-to-webdav.bash -> #1 create dir"
+
+docker run -i --rm curlimages/curl sh -c "curl -X MKCOL -u \"$WebDav_User\" \"$WebDav_BaseUrl/$APPNAME\" " || true
+docker run -i --rm curlimages/curl sh -c "curl -X MKCOL -u \"$WebDav_User\" \"$WebDav_BaseUrl/$APPNAME/$appVersion\" " || true
+
+#---------------------------------------------------------------------
+echo "78.push-releaseFiles-to-webdav.bash -> #2 push release files"
+
+docker run -i --rm \
+-v "$basePath/Publish/release/release-zip":/releaseFiles \
+curlimages/curl \
+sh -c "
+cd /releaseFiles
+for file in /releaseFiles/*
+do
+    echo ''
+    echo '----------------------------'
+    fileName=\"\${file##*/}\"
+    echo push file: \$fileName
+    curl -X PUT -u \"$WebDav_User\" -T "/releaseFiles/\$fileName" \"$WebDav_BaseUrl/$APPNAME/$appVersion/\$fileName\"
+done
+" || true
+
+#---------------------------------------------------------------------
+echo "78.push-releaseFiles-to-webdav.bash -> #3 success"

+ 57 - 0
Publish/DevOps3/release-bash/startup.bash

@@ -0,0 +1,57 @@
+set -e
+
+# cd /root/temp/svn/Publish/DevOps/release-bash;bash startup.bash;
+
+#---------------------------------------------------------------------
+# args
+args_="
+
+export APPNAME=xxxxxx
+
+export DOCKER_ImagePrefix=serset/
+export DOCKER_USERNAME=serset
+export DOCKER_PASSWORD=xxx
+
+export NUGET_SERVER=https://api.nuget.org/v3/index.json
+export NUGET_KEY=xxxxxxxxxx
+# "
+
+
+
+#----------------------------------------------
+# cur path
+curPath="$PWD"
+
+cd "$curPath/../../.."
+export basePath="$PWD"
+cd "$curPath"
+
+# export basePath=/root/temp/svn
+
+
+
+#----------------------------------------------
+echo "#1 get appVersion"
+cd "$curPath/../build-bash"; source 19.get-app-version.bash;
+
+
+
+
+
+
+#----------------------------------------------
+echo "#2 bash"
+cd $curPath
+for file in *.sh
+do
+    echo "-----------------------------------------------------------------"
+    echo "[$(date "+%H:%M:%S")] sh $file"
+    sh "$file"
+done
+
+
+cd "$curPath"
+
+
+
+

+ 29 - 0
README.md

@@ -0,0 +1,29 @@
+
+# Vitorm
+Vitorm: an simple orm by Vit.Linq
+>source address: [https://github.com/VitormLib/Vitorm](https://github.com/VitormLib/Vitorm "https://github.com/VitormLib/Vitorm")    
+
+![](https://img.shields.io/github/license/VitormLib/Vitorm.svg)  
+![](https://img.shields.io/github/repo-size/VitormLib/Vitorm.svg)  ![](https://img.shields.io/github/last-commit/VitormLib/Vitorm.svg)  
+ 
+
+| Build | NuGet |
+| -------- | -------- |
+|![](https://github.com/VitormLib/Vitorm/workflows/ki_multibranch/badge.svg) | [![](https://img.shields.io/nuget/v/Vitorm.svg)](https://www.nuget.org/packages/Vitorm/) ![](https://img.shields.io/nuget/dt/Vitorm.svg) |
+
+
+ - do not support Transaction
+ - do not support Update
+
+------------
+# TODO
+
+
+
+# OnlySelect
+# Create table: multiple keys
+ 
+# CollapsingMergeTree
+https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/collapsingmergetree
+
+

+ 10 - 0
clean-temp.bat

@@ -0,0 +1,10 @@
+cd Publish\DevOps3\build-cmd
+VsTool.exe delete --path "..\..\.." --file "*.suo|*.user" --directory "obj|bin|.vs|packages|TestResults"
+
+
+rd /s/q ..\..\release
+
+
+echo %~n0.bat success£¡
+
+:: pause

+ 28 - 0
doc/demo.sql

@@ -0,0 +1,28 @@
+-- https://clickhouse.com/docs/zh/sql-reference/statements/drop
+-- #1 CREATE DATABASE
+CREATE DATABASE IF NOT EXISTS `dev-orm`;
+CREATE DATABASE `dev-orm-mysql` ENGINE = MySQL('10.123.27.170:3306', 'dev-orm', 'root', '123456');
+
+-- drop db
+DROP DATABASE IF EXISTS `dev-orm`;
+
+use `dev-orm`;
+
+-- #2 create table
+CREATE TABLE IF NOT EXISTS `User`
+(
+    `id` Int32,
+    `name` Nullable(String),
+    `birth` Nullable(DateTime),
+    `fatherId` Nullable(Int32),
+    `motherId` Nullable(Int32)
+)
+ENGINE = MergeTree
+ORDER BY `id`;
+
+-- #3 insert
+insert into `User`(`id`,`name`,`birth`,`fatherId`,`motherId`) values(1,'u1',null,4,6);
+
+
+-- #4 delete
+ALTER TABLE `User` DELETE WHERE  id in ( 7 ) ;

BIN
doc/vitorm_logo_v1.png


+ 62 - 0
src/Vitorm.ClickHouse/DbContext_Extensions.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+using Vit.Extensions.Linq_Extensions;
+
+using Vitorm.Sql;
+using Vitorm.Sql.SqlTranslate;
+
+namespace Vit.Extensions
+{
+    public static class DbContext_Extensions
+    {
+        public static SqlDbContext UseClickHouse(this SqlDbContext dbContext, string ConnectionString)
+        {
+            ISqlTranslateService sqlTranslateService = Vitorm.ClickHouse.SqlTranslateService.Instance;
+
+            //Func<IDbConnection> createDbConnection = () => new ClickHouse.Ado.ClickHouseConnection(ConnectionString);
+            Func<IDbConnection> createDbConnection = () => new ClickHouse.Client.ADO.ClickHouseConnection(ConnectionString);
+
+
+            dbContext.Init(sqlTranslateService: sqlTranslateService, createDbConnection: createDbConnection, sqlExecutor: SqlExecutorWithoutNull.Instance);
+
+            //dbContext.createTransactionScope = (dbContext) => new Vitorm.Sql.Transaction.SqlTransactionScope(dbContext);
+
+            return dbContext;
+        }
+
+
+
+        class SqlExecutorWithoutNull : SqlExecutor
+        {
+            public readonly static SqlExecutorWithoutNull Instance = new();
+
+            public override int Execute(IDbConnection conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
+            {
+                sql = ReplaceNullParameters(sql, param);
+                return base.Execute(conn, sql, param, transaction, commandTimeout);
+            }
+            public override object ExecuteScalar(IDbConnection conn, string sql, IDictionary<string, object> param = null, IDbTransaction transaction = null, int? commandTimeout = null)
+            {
+                sql = ReplaceNullParameters(sql, param);
+                return base.ExecuteScalar(conn, sql, param, transaction, commandTimeout);
+            }
+
+            public string ReplaceNullParameters(string sql, IDictionary<string, object> param = null)
+            {
+                //find null value and replace parameters to "null" in sql
+                param?.Where(kv => kv.Value == null).Select(kv => kv.Key).ToList().ForEach(key =>
+                {
+                    param.Remove(key);
+                    sql = sql.Replace("@" + key, "null");
+                });
+
+                return sql;
+            }
+
+        }
+
+    }
+}

+ 335 - 0
src/Vitorm.ClickHouse/SqlTranslateService.cs

@@ -0,0 +1,335 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vitorm.Entity;
+using Vitorm.Sql;
+using Vitorm.Sql.SqlTranslate;
+using Vitorm.ClickHouse.TranslateService;
+using System.ComponentModel.DataAnnotations.Schema;
+using Vitorm.StreamQuery;
+using System.Data.Common;
+using System.Text;
+
+namespace Vitorm.ClickHouse
+{
+    public class SqlTranslateService : Vitorm.Sql.SqlTranslate.SqlTranslateService
+    {
+        public static readonly SqlTranslateService Instance = new SqlTranslateService();
+
+        protected QueryTranslateService queryTranslateService;
+  
+        protected ExecuteDeleteTranslateService executeDeleteTranslateService;
+
+        public SqlTranslateService()
+        {
+            queryTranslateService = new QueryTranslateService(this);
+ 
+            executeDeleteTranslateService = new ExecuteDeleteTranslateService(this);
+        }
+        /// <summary>
+        ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to delimit.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public override string DelimitIdentifier(string identifier) => $"`{EscapeIdentifier(identifier)}`"; // Interpolation okay; strings
+
+        /// <summary>
+        ///     Generates the escaped SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to be escaped.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public override string EscapeIdentifier(string identifier) => identifier.Replace("`", "\\`");
+
+        public override string DelimitTableName(IEntityDescriptor entityDescriptor)
+        {
+            if (entityDescriptor.schema == null) return DelimitIdentifier(entityDescriptor.tableName);
+
+            return $"{DelimitIdentifier(entityDescriptor.schema)}.{DelimitIdentifier(entityDescriptor.tableName)}";
+        }
+
+
+
+        #region EvalExpression
+        /// <summary>
+        /// read where or value or on
+        /// </summary>
+        /// <param name="arg"></param>
+        /// <returns></returns>
+        /// <exception cref="NotSupportedException"></exception>
+        /// <param name="data"></param>
+        public override string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
+        {
+            switch (data.nodeType)
+            {
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall methodCall = data;
+                        switch (methodCall.methodName)
+                        {
+                            // ##1 ToString
+                            case nameof(object.ToString):
+                                {
+                                    return $"cast({EvalExpression(arg, methodCall.@object)} as char)";
+                                }
+
+                            #region ##2 String method:  StartsWith EndsWith Contains
+                            case nameof(string.StartsWith): // String.StartsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like concat({EvalExpression(arg, value)},'%')";
+                                }
+                            case nameof(string.EndsWith): // String.EndsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like concat('%',{EvalExpression(arg, value)})";
+                                }
+                            case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like concat('%',{EvalExpression(arg, value)},'%')";
+                                }
+                                #endregion
+                        }
+                        break;
+                    }
+
+                #region Read Value
+                case NodeType.Convert:
+                    {
+                        // cast( 4.1 as signed)
+
+                        ExpressionNode_Convert convert = data;
+
+                        Type targetType = convert.valueType?.ToType();
+
+                        if (targetType == typeof(object)) return EvalExpression(arg, convert.body);
+
+                        // Nullable
+                        if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
+
+                        string targetDbType = GetDbType(targetType);
+
+                        var sourceType = convert.body.Member_GetType();
+                        if (sourceType != null)
+                        {
+                            if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
+
+                            if (targetDbType == GetDbType(sourceType)) return EvalExpression(arg, convert.body);
+                        }
+
+                        if (targetType == typeof(string))
+                        {
+                            return $"cast({EvalExpression(arg, convert.body)} as String)";
+                        }
+
+                        return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
+                    }
+                case nameof(ExpressionType.Add):
+                    {
+                        ExpressionNode_Binary binary = data;
+
+                        // ##1 String Add
+                        if (data.valueType?.ToType() == typeof(string))
+                        {
+                            // cast(ifNull('null','') as String)
+                            //return $"CONCAT({EvalExpression(arg, binary.left)} ,{EvalExpression(arg, binary.right)})";
+                            return $"CONCAT(cast(ifNull({EvalExpression(arg, binary.left)},'') as String) ,cast(ifNull({EvalExpression(arg, binary.right)},'') as String) )";
+                        }
+
+                        // ##2 Numberic Add
+                        return $"{EvalExpression(arg, binary.left)} + {EvalExpression(arg, binary.right)}";
+                    }
+                case nameof(ExpressionType.Coalesce):
+                    {
+                        ExpressionNode_Binary binary = data;
+                        return $"COALESCE({EvalExpression(arg, binary.left)},{EvalExpression(arg, binary.right)})";
+                    }
+                    #endregion
+
+            }
+
+            return base.EvalExpression(arg, data);
+        }
+        #endregion
+
+
+
+        #region PrepareCreate
+        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
+        {
+            /* //sql
+CREATE TABLE IF NOT EXISTS `User`
+(
+    `id` Int32,
+    `name` Nullable(String),
+    `birth` Nullable(DateTime),
+    `fatherId` Nullable(Int32),
+    `motherId` Nullable(Int32)
+)
+ENGINE = MergeTree
+ORDER BY `id`;
+              */
+            List<string> sqlFields = new();
+
+            // #1 columns
+            entityDescriptor.allColumns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+
+            return $@"
+CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
+{string.Join(",\r\n  ", sqlFields)}
+)
+ENGINE = MergeTree
+ORDER BY  {DelimitIdentifier(entityDescriptor.key.name)};";
+
+            string GetColumnSql(IColumnDescriptor column)
+            {
+                var dbType = column.databaseType ?? GetDbType(column.type);
+                return $"  {DelimitIdentifier(column.name)} {(column.nullable ? $"Nullable({dbType})" : dbType)}";
+            }
+        }
+
+        // https://clickhouse.com/docs/en/sql-reference/data-types
+        protected readonly static Dictionary<Type, string> dbTypeMap = new()
+        {
+            [typeof(DateTime)] = "DateTime",
+            [typeof(string)] = "String",
+
+            [typeof(float)] = "Float32",
+            [typeof(double)] = "Float64",
+            [typeof(decimal)] = "Decimal32",
+
+            [typeof(Int64)] = "Int64",
+            [typeof(Int32)] = "Int32",
+            [typeof(Int16)] = "Int16",
+
+            [typeof(UInt64)] = "UInt64",
+            [typeof(UInt32)] = "UInt32",
+            [typeof(UInt16)] = "UInt16",
+            [typeof(byte)] = "UInt8",
+
+            [typeof(bool)] = "UInt8",
+        };
+        protected override string GetDbType(Type type)
+        {
+            type = TypeUtil.GetUnderlyingType(type);
+
+            if (dbTypeMap.TryGetValue(type, out var dbType)) return dbType;
+
+            //if (type.Name.ToLower().Contains("int")) return "INTEGER";
+
+            throw new NotSupportedException("unsupported column type:" + type.Name);
+        }
+
+        #endregion
+
+
+        public override (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg)
+        {
+            /* //sql
+             insert into user(name,birth,fatherId,motherId) values('','','');
+             select seq from sqlite_sequence where name='user';
+              */
+            var entityDescriptor = arg.entityDescriptor;
+
+            var columns = entityDescriptor.allColumns;
+
+            // #1 GetSqlParams 
+            Func<object, Dictionary<string, object>> GetSqlParams = (entity) =>
+            {
+                var sqlParam = new Dictionary<string, object>();
+                foreach (var column in columns)
+                {
+                    var columnName = column.name;
+                    var value = column.GetValue(entity);
+
+                    sqlParam[columnName] = value;
+                }
+                return sqlParam;
+            };
+
+            #region #2 columns 
+            List<string> columnNames = new List<string>();
+            List<string> valueParams = new List<string>();
+            string columnName;
+
+            foreach (var column in columns)
+            {
+                columnName = column.name;
+
+                columnNames.Add(DelimitIdentifier(columnName));
+                valueParams.Add(GenerateParameterName(columnName));
+            }
+            #endregion
+
+            // #3 build sql
+            string sql = $@"insert into {DelimitTableName(entityDescriptor)}({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
+            return (sql, GetSqlParams);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = queryTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam, arg.dataReader);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream) => throw new NotImplementedException();
+
+
+        public override string PrepareDelete(SqlTranslateArgument arg)
+        {
+            // ALTER TABLE `User` DELETE WHERE `id`=2;
+            var entityDescriptor = arg.entityDescriptor;
+
+            // #2 build sql
+            string sql = $@"ALTER TABLE {DelimitTableName(entityDescriptor)} DELETE where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)} ; ";
+
+            return sql;
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareDeleteByKeys<Key>(SqlTranslateArgument arg, IEnumerable<Key> keys)
+        {
+            //  ALTER TABLE `User` DELETE WHERE  id in ( 7 ) ;
+
+            var entityDescriptor = arg.entityDescriptor;
+
+            StringBuilder sql = new StringBuilder();
+            Dictionary<string, object> sqlParam = new();
+
+            sql.Append("ALTER TABLE ").Append(DelimitTableName(entityDescriptor)).Append(" DELETE where ").Append(DelimitIdentifier(entityDescriptor.keyName)).Append(" in (");
+
+            int keyIndex = 0;
+            foreach (var key in keys)
+            {
+                var paramName = "p" + (keyIndex++);
+                sql.Append(GenerateParameterName(paramName)).Append(",");
+                sqlParam[paramName] = key;
+            }
+            if (keyIndex == 0) sql.Append("null);");
+            else
+            {
+                sql.Length--;
+                sql.Append(");");
+            }
+            return (sql.ToString(), sqlParam);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeDeleteTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+
+
+    }
+}

+ 48 - 0
src/Vitorm.ClickHouse/TranslateService/ExecuteDeleteTranslateService.cs

@@ -0,0 +1,48 @@
+using Vitorm.Sql.SqlTranslate;
+using Vitorm.StreamQuery;
+
+namespace Vitorm.ClickHouse.TranslateService
+{
+    public class ExecuteDeleteTranslateService : BaseQueryTranslateService
+    {
+        /*
+ALTER TABLE `User` DELETE 
+where id in (
+    select u.id 
+    from `User` u
+    left join `User` father on u.fatherId = father.id 
+    where u.id > 10
+);
+         */
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            var sqlInner = base.BuildQuery(arg, stream);
+
+            var NewLine = "\r\n";
+            var keyName = entityDescriptor.keyName;
+
+            var sql = $"ALTER TABLE {sqlTranslator.DelimitTableName(entityDescriptor)} DELETE";
+            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ({sqlInner})";
+
+            return sql;
+        }
+
+
+        public ExecuteDeleteTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            // primary key
+            return $"{prefix} {sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}";
+        }
+
+
+
+    }
+}

+ 28 - 0
src/Vitorm.ClickHouse/Vitorm.ClickHouse.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <pack>nuget</pack>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <TargetFramework>netstandard2.1</TargetFramework>
+        <Version>1.0.0-temp</Version>
+        <LangVersion>9.0</LangVersion>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <Authors>Lith</Authors>
+        <Description>orm for ClickHouse</Description>
+        <PackageProjectUrl>https://github.com/VitormLib/Vitorm.ClickHouse</PackageProjectUrl>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <!--<PackageReference Include="ClickHouse.Ado" Version="2.0.5" />-->
+        <PackageReference Include="ClickHouse.Client" Version="7.5.0" /> 
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\..\..\Vitorm\src\Vitorm\Vitorm.csproj" />
+    </ItemGroup>
+
+</Project>

+ 46 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Orm_Extensions_ToExecuteString_Test.cs

@@ -0,0 +1,46 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Orm_Extensions_ToExecuteString_Test
+    {
+
+        [TestMethod]
+        public void Test_ToExecuteString()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            #region users.Where().OrderBy().Skip().Take().ToList
+            /*
+            users.Where(row => row.user.id > 2)
+            .OrderBy(user=>user.id)
+            .Select(row => new {row.user })
+            .Skip(1).Take(2);
+             */
+            {
+                var query = (from user in userQuery
+                             where user.id > 2 && !user.name.Contains("3")
+                             orderby user.id descending
+                             select new
+                             {
+                                 user
+                             })
+                            .Skip(1).Take(2);
+
+                var sql = query.ToExecuteString();
+                Assert.AreEqual(false, string.IsNullOrWhiteSpace(sql));
+
+                var list = query.ToList();
+                Assert.AreEqual(2, list.Count);
+                //Assert.AreEqual(5, list[0].user.id);
+            }
+            #endregion
+
+        }
+    }
+}

+ 182 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Group_Test.cs

@@ -0,0 +1,182 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class Query_Group_Test
+    {
+
+        [TestMethod]
+        public void Test_Group_Demo()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                        from user in userQuery
+                        group user by new { user.fatherId, user.motherId } into userGroup
+                        select new { userGroup.Key.fatherId, userGroup.Key.motherId };
+
+                var sql = query.ToExecuteString();
+                var rows = query.ToList();
+
+                Assert.AreEqual(3, rows.Count);
+                Assert.AreEqual(0, rows.Select(u => u.fatherId).Except(new int?[] { 4, 5, null }).Count());
+                Assert.AreEqual(0, rows.Select(u => u.motherId).Except(new int?[] { 6, null }).Count());
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                        userQuery
+                        .GroupBy(user => new { user.fatherId, user.motherId })
+                        .Select(userGroup => new
+                        {
+                            userGroup.Key.fatherId,
+                            userGroup.Key.motherId
+                        })
+                        ;
+
+                var sql = query.ToExecuteString();
+                var rows = query.ToList();
+
+                Assert.AreEqual(3, rows.Count);
+                Assert.AreEqual(0, rows.Select(u => u.fatherId).Except(new int?[] { 4, 5, null }).Count());
+                Assert.AreEqual(0, rows.Select(u => u.motherId).Except(new int?[] { 6, null }).Count());
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_Group_Complex()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                        from user in userQuery.Where(u => u.id > 1)
+                        group user by new { user.fatherId, user.motherId } into userGroup
+                        where userGroup.Key.motherId != null && userGroup.Count() >= 1
+                        orderby userGroup.Key.fatherId descending, userGroup.Count() descending
+                        select new { userGroup.Key.fatherId, userGroup.Key.motherId, rowCount = userGroup.Count(), maxId = userGroup.Max(m => m.id) };
+
+                query = query.Skip(1).Take(1);
+
+                var sql = query.ToExecuteString();
+                var rows = query.ToList();
+
+                Assert.AreEqual(1, rows.Count);
+                Assert.AreEqual(4, rows[0].fatherId);
+                Assert.AreEqual(6, rows[0].motherId);
+                Assert.AreEqual(1, rows[0].rowCount);
+                Assert.AreEqual(2, rows[0].maxId);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                        userQuery
+                        .Where(u => u.id > 1)
+                        .GroupBy(user => new { user.fatherId, user.motherId })
+                        .Where(userGroup => userGroup.Key.motherId != null)
+                        .OrderByDescending(userGroup => userGroup.Key.fatherId)
+                        .Select(userGroup => new
+                        {
+                            userGroup.Key.fatherId,
+                            userGroup.Key.motherId,
+                            rowCount = userGroup.Count(),
+                            maxId = userGroup.Max(m => m.id)
+                        })
+                        .Skip(1)
+                        .Take(1)
+                        ;
+
+                var sql = query.ToExecuteString();
+                var rows = query.ToList();
+
+                Assert.AreEqual(1, rows.Count);
+                Assert.AreEqual(4, rows[0].fatherId);
+                Assert.AreEqual(6, rows[0].motherId);
+                Assert.AreEqual(1, rows[0].rowCount);
+                Assert.AreEqual(2, rows[0].maxId);
+            }
+        }
+
+
+
+
+        [TestMethod]
+        public void Test_Others()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var query =
+                    userQuery
+                    .GroupBy(user => new { user.fatherId, user.motherId })
+                    .OrderByDescending(group => group.Count())
+                    .Select(userGroup => new
+                    {
+                        userGroup.Key.fatherId,
+                        rowCount = userGroup.Count(),
+                        maxId = userGroup.Max(m => m.id),
+                        minId = userGroup.Min(m => m.id),
+                        sumId = userGroup.Sum(m => m.id),
+                        avgId = userGroup.Average(m => (double)m.id)
+                    })
+                    ;
+
+                var sql = query.ToExecuteString();
+                var rows = query.ToList();
+
+                Assert.AreEqual(3, rows.Count);
+                var row = rows[1];
+                Assert.AreEqual(2, row.rowCount);
+                Assert.AreEqual(2, row.maxId);
+                Assert.AreEqual(1, row.minId);
+                Assert.AreEqual(3, row.sumId);
+                Assert.AreEqual(1.5, row.avgId);
+            }
+            {
+                var query =
+                    userQuery
+                    .GroupBy(user => new { user.fatherId, user.motherId })
+                    .Where(userGroup => userGroup.Key.motherId != null)
+                    .OrderByDescending(userGroup => userGroup.Key.fatherId)
+                    .Select(userGroup => new { userGroup.Key.fatherId, userGroup.Key.motherId })
+                    ;
+
+                var rows = query.ToList();
+                var sql = query.ToExecuteString();
+
+                Assert.AreEqual(2, rows.Count);
+                Assert.AreEqual(5, rows[0].fatherId);
+            }
+            {
+                var query =
+                    userQuery
+                    .GroupBy(user => user.fatherId)
+                    .Where(userGroup => userGroup.Key != null)
+                    .OrderByDescending(userGroup => userGroup.Key)
+                    .Select(userGroup => new { fatherId = userGroup.Key, rowCount = userGroup.Count() })
+                    ;
+
+                var rows = query.ToList();
+                var sql = query.ToExecuteString();
+
+                Assert.AreEqual(2, rows.Count);
+                Assert.AreEqual(5, rows[0].fatherId);
+            }
+        }
+
+
+    }
+}

+ 218 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_InnerJoin_ByJoin_Test.cs

@@ -0,0 +1,218 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class Query_InnerJoin_ByJoin_Test
+    {
+
+        [TestMethod]
+        public void Test_InnerJoin_Demo()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    from father in userQuery.Where(father => user.fatherId == father.id)
+                    where user.id > 2
+                    select new { user, father };
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().user.id);
+                Assert.AreEqual(5, userList.First().father.id);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                    userQuery.SelectMany(
+                        user => userQuery.Where(father => user.fatherId == father.id)
+                        , (user, father) => new { user, father }
+                    )
+                    .Where(row => row.user.id > 2)
+                    .Select(row => new { row.user, row.father });
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().user.id);
+                Assert.AreEqual(5, userList.First().father.id);
+            }
+
+        }
+
+
+        [TestMethod]
+        public void Test_InnerJoin_Complex()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    join father in userQuery on user.fatherId equals father.id
+                    join mother in userQuery on user.motherId equals mother.id
+                    where user.id > 1
+                    orderby father.id descending
+                    select new
+                    {
+                        user,
+                        father,
+                        mother,
+                        testId = user.id + 100,
+                        hasFather = father != null ? true : false
+                    };
+                query = query.Skip(1).Take(1);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+
+                var first = userList.First();
+                Assert.AreEqual(2, first.user.id);
+                Assert.AreEqual(4, first.father.id);
+                Assert.AreEqual(6, first.mother.id);
+                Assert.AreEqual(102, first.testId);
+                Assert.AreEqual(true, first.hasFather);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                    userQuery.Join(
+                        userQuery
+                        , user => user.fatherId
+                        , father => father.id
+                        , (user, father) => new { user, father }
+                    ).Join(
+                        userQuery
+                        , row => row.user.motherId
+                        , mother => mother.id
+                        , (row, mother) => new { row, mother }
+                    )
+                    .Where(row2 => row2.row.user.id > 1)
+                    .OrderByDescending(row2 => row2.row.father.id)
+                    .Select(row2 =>
+                        new
+                        {
+                            row2.row.user,
+                            row2.row.father,
+                            row2.mother,
+                            testId = row2.row.user.id + 100,
+                            hasFather = row2.row.father != null ? true : false
+                        }
+                    );
+
+                query = query.Skip(1).Take(1);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+
+                var first = userList.First();
+                Assert.AreEqual(2, first.user.id);
+                Assert.AreEqual(4, first.father.id);
+                Assert.AreEqual(6, first.mother.id);
+                Assert.AreEqual(102, first.testId);
+                Assert.AreEqual(true, first.hasFather);
+            }
+        }
+
+
+
+        [TestMethod]
+        public void Test_InnerJoin_Others()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // simple
+            {
+                var query =
+                    userQuery.Join(
+                        userQuery
+                        , user => user.fatherId
+                        , father => father.id
+                        , (user, father) => new { user, father }
+                    );
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(1, userList.First().user.id);
+            }
+
+            // where
+            {
+                var query =
+                    userQuery.Join(
+                        userQuery
+                        , user => user.fatherId
+                        , father => father.id
+                        , (user, father) => new { user, father }
+                    ).Where(row => row.user.id > 2);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().user.id);
+            }
+            // select
+            {
+                var query =
+                    userQuery.Join(
+                        userQuery
+                        , user => user.fatherId
+                        , father => father.id
+                        , (user, father) => new { user, father }
+                    ).Where(row => row.user.id > 2)
+                    .Select(row => new { userId = row.user.id, fatherId = row.father.id });
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().userId);
+                Assert.AreEqual(5, userList.First().fatherId);
+            }
+            // full feature
+            {
+                var query =
+                         from user in userQuery
+                         join father in userQuery on user.fatherId equals father.id
+                         where user.id > 1
+                         orderby user.id descending
+                         select new { user, father };
+
+                query = query.Skip(1).Take(1);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(2, userList.First().user.id);
+            }
+
+        }
+
+
+
+
+    }
+}

+ 135 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_InnerJoin_BySelectMany_Test.cs

@@ -0,0 +1,135 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class Query_InnerJoin_BySelectMany_Test
+    {
+
+        [TestMethod]
+        public void Test_InnerJoin_Demo()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    from father in userQuery.Where(father => user.fatherId == father.id)
+                    where user.id > 2
+                    select new { user, father };
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().user.id);
+                Assert.AreEqual(5, userList.First().father.id);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                    userQuery.SelectMany(
+                        user => userQuery.Where(father => user.fatherId == father.id)
+                        , (user, father) => new { user, father }
+                    )
+                    .Where(row => row.user.id > 2)
+                    .Select(row => new { row.user, row.father });
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().user.id);
+                Assert.AreEqual(5, userList.First().father.id);
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_InnerJoin_Complex()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    from father in userQuery.Where(father => user.fatherId == father.id)
+                    from mother in userQuery.Where(mother => user.motherId == mother.id)
+                    where user.id > 1
+                    orderby father.id descending
+                    select new
+                    {
+                        user,
+                        father,
+                        mother,
+                        testId = user.id + 100,
+                        hasFather = father != null ? true : false
+                    };
+                query = query.Skip(1).Take(1);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+
+                var first = userList.First();
+                Assert.AreEqual(2, first.user.id);
+                Assert.AreEqual(4, first.father.id);
+                Assert.AreEqual(6, first.mother.id);
+                Assert.AreEqual(102, first.testId);
+                Assert.AreEqual(true, first.hasFather);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                    userQuery.SelectMany(
+                        user => userQuery.Where(father => user.fatherId == father.id)
+                        , (user, father) => new { user, father }
+                    ).SelectMany(
+                        row => userQuery.Where(mother => row.user.motherId == mother.id)
+                        , (row, mother) => new { row, mother }
+                    )
+                    .Where(row2 => row2.row.user.id > 1)
+                    .OrderByDescending(row2 => row2.row.father.id)
+                    .Select(row2 =>
+                        new
+                        {
+                            row2.row.user,
+                            row2.row.father,
+                            row2.mother,
+                            testId = row2.row.user.id + 100,
+                            hasFather = row2.row.father != null ? true : false
+                        }
+                    );
+
+                query = query.Skip(1).Take(1);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+
+                var first = userList.First();
+                Assert.AreEqual(2, first.user.id);
+                Assert.AreEqual(4, first.father.id);
+                Assert.AreEqual(6, first.mother.id);
+                Assert.AreEqual(102, first.testId);
+                Assert.AreEqual(true, first.hasFather);
+            }
+        }
+
+
+
+
+
+    }
+}

+ 107 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LeftJoin_ByGroupJoin_Test.cs

@@ -0,0 +1,107 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class Query_LeftJoin_ByGroupJoin_Test
+    {
+        [TestMethod]
+        public void Test_LeftJoin_Demo()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    join father in userQuery on user.fatherId equals father.id into fathers
+                    from father in fathers.DefaultIfEmpty()
+                    where user.id > 2
+                    select new { user, father };
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(4, userList.Count);
+                //Assert.AreEqual(3, userList[0].user.id);
+                //Assert.AreEqual(5, userList[0].father?.id);
+                //Assert.AreEqual(4, userList[1].user.id);
+                //Assert.AreEqual(null, userList[1].father?.id);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                    userQuery.GroupJoin(
+                        userQuery
+                        , user => user.fatherId
+                        , father => father.id
+                        , (user, fathers) => new { user, fathers }
+                    )
+                    .SelectMany(
+                        row => row.fathers.DefaultIfEmpty()
+                        , (row, father) => new { row, father }
+                    )
+                    .Where(row2 => row2.row.user.id > 2)
+                    .Select(row2 => new { row2.row.user, row2.father });
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(4, userList.Count);
+                //Assert.AreEqual(3, userList[0].user.id);
+                //Assert.AreEqual(5, userList[0].father?.id);
+                //Assert.AreEqual(4, userList[1].user.id);
+                //Assert.AreEqual(null, userList[1].father?.id);
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_LeftJoin_Complex()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    join father in userQuery on user.fatherId equals father.id into fathers
+                    from father in fathers.DefaultIfEmpty()
+                    join mother in userQuery on user.motherId equals mother.id into mothers
+                    from mother in mothers.DefaultIfEmpty()
+                    where user.id > 2
+                    orderby father.id descending
+                    select new
+                    {
+                        user,
+                        father,
+                        mother,
+                        testId = user.id + 100,
+                        hasFather = father != null ? true : false
+                    };
+
+                query = query.Skip(1).Take(2);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(2, userList.Count);
+
+                //var first = userList.First();
+                //Assert.AreEqual(4, first.user.id);
+                //Assert.AreEqual(null, first.father?.id);
+                //Assert.AreEqual(null, first.mother?.id);
+                //Assert.AreEqual(104, first.testId);
+                //Assert.AreEqual(false, first.hasFather);
+            }
+        }
+
+
+    }
+}

+ 222 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LeftJoin_BySelectMany_Test.cs

@@ -0,0 +1,222 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+    [TestClass]
+    public class Query_LeftJoin_BySelectMany_Test
+    {
+
+
+
+
+        [TestMethod]
+        public void Test_LeftJoin_Demo()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                        from user in userQuery
+                        from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()                    
+                        where user.id > 2
+                        orderby user.id
+                        select new { user, father };
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(4, userList.Count);
+                //Assert.AreEqual(3, userList[0].user.id);
+                //Assert.AreEqual(5, userList[0].father?.id);
+                //Assert.AreEqual(4, userList[1].user.id);
+                //Assert.AreEqual(0, userList[1].father?.id??0);
+            }
+
+            // Lambda Expression
+            {
+                var query =
+                        userQuery.SelectMany(
+                            user => userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                            , (user, father) => new { user, father }
+                        )
+                        .Where(row => row.user.id > 2)
+                        .Select(row => new { row.user, row.father });
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(4, userList.Count);
+                //Assert.AreEqual(3, userList[0].user.id);
+                //Assert.AreEqual(5, userList[0].father?.id);
+                //Assert.AreEqual(4, userList[1].user.id);
+                //Assert.AreEqual(null, userList[1].father?.id);
+            }
+        }
+
+
+
+        [TestMethod]
+        public void Test_LeftJoin_Complex()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Linq Expresssion
+            {
+                var query =
+                    from user in userQuery
+                    from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                    from mother in userQuery.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
+                    where user.id > 2
+                    orderby user.fatherId descending
+                    select new
+                    {
+                        user,
+                        father,
+                        mother,
+                        testId = user.id + 100,
+                        hasFather = father != null ? true : false
+                    };
+
+                query = query.Skip(1).Take(2);
+
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(2, userList.Count);
+
+                //var first = userList.First();
+                //Assert.AreEqual(4, first.user.id);
+                //Assert.AreEqual(null, first.father?.id);
+                //Assert.AreEqual(null, first.mother?.id);
+                //Assert.AreEqual(104, first.testId);
+                //Assert.AreEqual(false, first.hasFather);
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_MultipleSelect()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+
+            {
+                var query = from user in userQuery
+                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             where user.id > 2 && father.name != null
+                             select new
+                             {
+                                 user,
+                                 father
+                             };
+
+                var userList = query.ToList();
+
+                Assert.AreEqual(1, userList.Count);
+                //Assert.AreEqual(3, userList.First().user.id);
+            }
+
+            {
+                var query = from user in userQuery
+                             from father in userQuery.Where(father => user.fatherId == father.id)
+                             from mother in userQuery.Where(mother => user.motherId == mother.id)
+                             select new
+                             {
+                                 uniqueId = user.id + "_" + father.id + "_" + mother.id,
+                                 uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
+                                 user,
+                                 user2 = user,
+                                 user3 = user,
+                                 father,
+                                 hasFather = user.fatherId != null ? true : false,
+                                 fatherName = father.name,
+                                 mother
+                             };
+
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                //Assert.AreEqual(1, userList.First().user.id);
+                //Assert.AreEqual(3, userList.Last().user.id);
+                //Assert.AreEqual(5, userList.Last().father?.id);
+            }
+
+            {
+                var query = from user in userQuery
+                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             from mother in userQuery.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
+                             select new
+                             {
+                                 user,
+                                 father,
+                                 userId = user.id + 100,
+                                 hasFather = user.fatherId != null ? true : false,
+                                 hasFather2 = father != null,
+                                 fatherName = father.name,
+                                 motherName = mother.name,
+                             };
+
+                var userList = query.ToList();
+
+                Assert.AreEqual(6, userList.Count);
+                //Assert.AreEqual(1, userList.First().user.id);
+                //Assert.AreEqual(101, userList.First().userId);
+                //Assert.AreEqual(6, userList.Last().user.id);
+                //Assert.AreEqual(5, userList[2].father.id);
+            }
+
+            {
+                var query = from user in userQuery
+                             from father in userQuery.Where(father => user.fatherId == father.id)
+                             from mother in userQuery.Where(mother => user.motherId == mother.id)
+                             where user.id > 1
+                             orderby father.id descending
+                             select new
+                             {
+                                 user,
+                                 father,
+                                 userId = user.id + 100,
+                                 hasFather = user.fatherId != null ? true : false,
+                                 hasFather2 = father != null,
+                                 fatherName = father.name,
+                                 motherName = mother.name,
+                             };
+
+                var userList = query.ToList();
+
+                Assert.AreEqual(2, userList.Count);
+                //Assert.AreEqual(5, userList.First().father?.id);
+                //Assert.AreEqual(4, userList.Last().father?.id);
+            }
+
+        }
+
+
+
+        [TestMethod]
+        public void Test_Count()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var count = (from user in userQuery
+                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             where user.id > 2 && father.name == null
+                             select new
+                             {
+                                 father
+                             }).Count();
+
+                Assert.AreEqual(3, count);
+            }
+        }
+
+
+    }
+}

+ 51 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LinqMethods_Distinct_Test.cs

@@ -0,0 +1,51 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_LinqMethods_Distinct_Test
+    {
+
+        [TestMethod]
+        public void Test_Distinct()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var query = userQuery.Select(u => new { u.fatherId }).Distinct();
+
+                //var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                var ids = userList.Select(u => u.fatherId).ToList();
+
+                Assert.AreEqual(3, ids.Count);
+                Assert.AreEqual(0, ids.Except(new int?[] { 4, 5, null }).Count());
+            }
+            {
+                var query = userQuery.Select(u => u.fatherId).Distinct();
+
+                //var sql = query.ToExecuteString();
+                var ids = query.ToList();
+
+                Assert.AreEqual(3, ids.Count);
+                Assert.AreEqual(0, ids.Except(new int?[] { 4, 5, null }).Count());
+            }
+            {
+                var query = userQuery.Distinct();
+
+                //var sql = query.ToExecuteString();
+                var userList = query.ToList();
+
+                Assert.AreEqual(6, userList.Count);
+            }
+
+        }
+
+
+
+    }
+}

+ 301 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_LinqMethods_Test.cs

@@ -0,0 +1,301 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_LinqMethods_Test
+    {
+
+
+
+        [TestMethod]
+        public void Test_PlainQuery()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.OrderBy(m => m.id).ToList();         
+                Assert.AreEqual(6, userList.Count);
+                Assert.AreEqual(1, userList.First().id);
+                Assert.AreEqual(6, userList.Last().id);
+            }
+
+
+            {
+                var userList = userQuery.OrderBy(m => m.id).Select(u => u.id).ToList();
+                Assert.AreEqual(6, userList.Count);
+                Assert.AreEqual(1, userList.First());
+                Assert.AreEqual(6, userList.Last());
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_AllFeatures()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            #region users.Where().OrderBy().Skip().Take().ToList
+            /*
+            users.Where(row => row.user.id > 2)
+            .OrderBy(user=>user.id)
+            .Select(row => new {row.user })
+            .Skip(1).Take(2);
+             */
+            {
+                var query = (from user in userQuery
+                             where user.id > 2
+                             orderby user.id descending
+                             select new
+                             {
+                                 user
+                             })
+                            .Skip(1).Take(2);
+
+                //var sql = query.ToExecuteString();
+                var list = query.ToList();
+
+                Assert.AreEqual(2, list.Count);
+                Assert.AreEqual(5, list[0].user.id);
+                Assert.AreEqual(4, list[1].user.id);
+            }
+            #endregion
+        }
+
+
+
+        [TestMethod]
+        public void Test_Get()
+        {
+            {
+                using var dbContext = DataSource.CreateDbContext();
+                var user = dbContext.Get<User>(3);
+                Assert.AreEqual(3, user?.id);
+            }
+            {
+                using var dbContext = DataSource.CreateDbContext();
+                var user = dbContext.DbSet<User>().Get(5);
+                Assert.AreEqual(5, user?.id);
+            }
+        }
+
+
+
+        [TestMethod]
+        public void Test_Select()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.Select(u => u).Where(user => user.id == 3).Select(u => u).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+
+            {
+                var userList = userQuery.Where(user => user.id == 3).Select(u => (float)u.id).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3.0, userList.First());
+            }
+
+
+            {
+                var query =
+                    from user in userQuery
+                    orderby user.id
+                    select new
+                    {
+                        uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
+                        uniqueId2 = $"{user.id}_{user.fatherId}_{user.motherId}"
+                    };
+
+                var userList = query.ToList();
+                Assert.AreEqual(6, userList.Count);
+                Assert.AreEqual("1_4_6", userList.First().uniqueId1);
+            }
+
+        }
+
+        [TestMethod]
+        public void Test_Count()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var count = (from user in userQuery
+                             where user.id > 2
+                             select new
+                             {
+                                 user
+                             }).Count();
+
+                Assert.AreEqual(4, count);
+            }
+        }
+
+
+
+
+
+        [TestMethod]
+        public void Test_FirstOrDefault()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var id = userQuery.OrderBy(m => m.id).Select(u => u.id).FirstOrDefault();
+                Assert.AreEqual(1, id);
+            }
+
+            {
+                var user = userQuery.OrderBy(m => m.id).FirstOrDefault();
+                Assert.AreEqual(1, user?.id);
+            }
+
+            {
+                var user = userQuery.FirstOrDefault(user => user.id == 3);
+                Assert.AreEqual(3, user?.id);
+            }
+
+            {
+                var user = userQuery.FirstOrDefault(user => user.id == 13);
+                Assert.AreEqual(null, user?.id);
+            }
+
+            {
+                var user = userQuery.OrderByDescending(m => m.id).FirstOrDefault();
+                Assert.AreEqual(6, user?.id);
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_First()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var user = userQuery.OrderBy(m => m.id).First();
+                Assert.AreEqual(1, user?.id);
+            }
+
+            {
+                var user = userQuery.First(user => user.id == 3);
+                Assert.AreEqual(3, user?.id);
+            }
+
+            {
+                try
+                {
+                    var user = userQuery.First(user => user.id == 13);
+                    Assert.Fail("IQueryalbe.First should throw Exception");
+                }
+                catch (Exception ex)
+                {
+                }
+
+            }
+
+        }
+
+
+
+        [TestMethod]
+        public void Test_LastOrDefault()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var id = userQuery.OrderBy(m => m.id).Select(u => u.id).FirstOrDefault();
+                Assert.AreEqual(1, id);
+            }
+            {
+                var user = userQuery.OrderBy(m => m.id).LastOrDefault();
+                Assert.AreEqual(6, user?.id);
+            }
+
+            {
+                var user = userQuery.LastOrDefault(user => user.id == 3);
+                Assert.AreEqual(3, user?.id);
+            }
+
+            {
+                var user = userQuery.LastOrDefault(user => user.id == 13);
+                Assert.AreEqual(null, user?.id);
+            }
+
+            {
+                var user = userQuery.OrderByDescending(m => m.id).LastOrDefault();
+                Assert.AreEqual(1, user?.id);
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_Last()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var user = userQuery.OrderBy(m => m.id).Last();
+                Assert.AreEqual(6, user?.id);
+            }
+
+            {
+                var user = userQuery.Last(user => user.id == 3);
+                Assert.AreEqual(3, user?.id);
+            }
+
+            {
+                try
+                {
+                    var user = userQuery.Last(user => user.id == 13);
+                    Assert.Fail("IQueryalbe.First should throw Exception");
+                }
+                catch (Exception ex)
+                {
+                }
+
+            }
+
+        }
+
+        // Enumerable.ToArray
+        [TestMethod]
+        public void Test_Enumerable_ToArray()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.OrderBy(m=>m.id).ToArray();
+                Assert.AreEqual(6, userList.Length);
+                Assert.AreEqual(1, userList.First().id);
+                Assert.AreEqual(6, userList.Last().id);
+            }
+
+
+            {
+                var userList = userQuery.OrderBy(m => m.id).Select(u => u.id).ToArray();
+                Assert.AreEqual(6, userList.Length);
+                Assert.AreEqual(1, userList.First());
+                Assert.AreEqual(6, userList.Last());
+            }
+        }
+
+
+
+
+    }
+}

+ 58 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_DateTime_Test.cs

@@ -0,0 +1,58 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Type_DateTime_Test
+    {
+
+        [TestMethod]
+        public void Test_Equal()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // ==
+            {
+                var userList = userQuery.Where(u => u.birth == new DateTime(2021, 01, 01, 03, 00, 00)).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+        }
+
+        [TestMethod]
+        public void Test_Compare()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.Where(u => u.birth >= new DateTime(2021, 01, 01, 05, 00, 00)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 5, 6 }).Count());
+            }
+
+        }
+
+
+        [TestMethod]
+        public void Test_Caculate()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.Where(u => u.birth == DateTime.Parse("2021-01-01 01:00:00").AddHours(2)).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+
+        }
+
+
+
+    }
+}

+ 37 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_Numric_Caculate_Test.cs

@@ -0,0 +1,37 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Type_Numric_Caculate_Test
+    {
+
+        [TestMethod]
+        public void Test_Caculate()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.Where(u => u.id + 1 == 4).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+            {
+                var userList = userQuery.Where(u => 4 == u.id + 1).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+            {
+                var userList = userQuery.Where(u => u.id == 4 - 1).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+        }
+
+
+    }
+}

+ 122 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_Numric_Test.cs

@@ -0,0 +1,122 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Data;
+using System.Linq;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Type_Numeric_Test
+    {
+        // Enumerable.Contains
+        // Queryable.Contains
+        [TestMethod]
+        public void Test_In()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Enumerable.Contains
+            {
+                var userList = userQuery.Where(u => new[] { 3, 5 }.Contains(u.id)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 3,5 }).Count());
+            }
+
+            // Enumerable.Contains
+            {
+                var ids = new[] { 3, 5 }.AsEnumerable();
+                var userList = userQuery.Where(u => ids.Contains(u.id)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 3, 5 }).Count());
+            }
+
+            // Queryable.Contains
+            {
+                var ids = new[] { 3, 5 }.AsQueryable();
+                var userList = userQuery.Where(u => ids.Contains(u.id)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 3, 5 }).Count());
+            }
+
+
+            // not Contains
+            {
+                var userList = userQuery.Where(u => !new[] { 3, 5 }.Contains(u.id)).ToList();
+                Assert.AreEqual(4, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 1, 2, 4, 6 }).Count());
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_Equal()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // #1 ==
+            {
+                var userList = userQuery.Where(u => u.id == 3 || 5 == u.id).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 3, 5 }).Count());
+            }
+
+            // #2 !=
+            {
+                var userList = userQuery.Where(u => u.id != 1).ToList();
+                Assert.AreEqual(5, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 2, 3, 4, 5, 6 }).Count());
+            }
+            {
+                var userList = userQuery.Where(u => 1 != u.id).ToList();
+                Assert.AreEqual(5, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 2, 3, 4, 5, 6 }).Count());
+            }
+        }
+
+
+
+        [TestMethod]
+        public void Test_Compare()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // #1 > and <
+            {
+                var userList = userQuery.Where(u => u.id > 2).Where(m => m.id < 4).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+            }
+
+            // #2  > or <
+            {
+                var userList = userQuery.Where(u => u.id > 5 || u.id < 2).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 1, 6 }).Count());
+            }
+
+            // #3  >= or <=
+            {
+                var userList = userQuery.Where(u => u.id >= 6 || u.id <= 1).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 1, 6 }).Count());
+            }
+
+            // #4  in right side
+            {
+                var userList = userQuery.Where(u => 4 >= u.id && 3 <= u.id).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(new[] { 3, 4 }).Count());
+            }
+
+        }
+
+
+
+
+
+    }
+}

+ 39 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_String_Caculate_Test.cs

@@ -0,0 +1,39 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Type_String_Caculate_Test
+    {
+
+
+        [TestMethod]
+        public void Test_Caculate()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            {
+                var userList = userQuery.Where(u => u.name + 1 == "u3561").ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList[0].id);
+            }
+            {
+                var userList = userQuery.Where(u => "u3561" == u.name + 1).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList[0].id);
+            }
+            {
+                var userList = userQuery.Where(u => u.name == "u35" + 6).ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList[0].id);
+            }
+
+        }
+
+
+    }
+}

+ 51 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_String_Like_Test.cs

@@ -0,0 +1,51 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Type_String_Like_Test
+    {
+
+
+        [TestMethod]
+        public void Test_Like()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // StartsWith
+            {
+                var query = userQuery.Where(u => u.name.StartsWith("u35"));
+                //var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+                Assert.AreEqual("u356", userList.First().name);
+            }
+            // EndsWith
+            {
+                var query = userQuery.Where(u => u.name.EndsWith("356"));
+                //var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+                Assert.AreEqual("u356", userList.First().name);
+            }
+            // Contains
+            {
+                var query = userQuery.Where(u => u.name.Contains("35"));
+                //var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(3, userList.First().id);
+                Assert.AreEqual("u356", userList.First().name);
+            }
+        }
+
+
+
+    }
+}

+ 86 - 0
test/Vitorm.ClickHouse.MsTest/CommonTest/Query_Type_String_Test.cs

@@ -0,0 +1,86 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Data;
+using System.Linq;
+
+namespace Vitorm.MsTest.CommonTest
+{
+
+    [TestClass]
+    public class Query_Type_String_Test
+    {
+
+        // Enumerable.Contains
+        // Queryable.Contains
+        [TestMethod]
+        public void Test_In()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // Enumerable.Contains
+            {
+                var userList = userQuery.Where(u => new[] { "u356", "u500" }.Contains(u.name)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u356", "u500" }).Count());
+            }
+
+            // Enumerable.Contains
+            {
+                var ids = new[] { "u356", "u500" }.AsEnumerable();
+                var userList = userQuery.Where(u => ids.Contains(u.name)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u356", "u500" }).Count());
+            }
+
+            // Queryable.Contains
+            {
+                var ids = new[] { "u356", "u500" }.AsQueryable();
+                var userList = userQuery.Where(u => ids.Contains(u.name)).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u356", "u500" }).Count());
+            }
+
+
+            // not Contains
+            {
+                var userList = userQuery.Where(u => !new[] { "u356", "u500" }.Contains(u.name)).ToList();
+                Assert.AreEqual(4, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u146", "u246", "u400", "u600" }).Count());
+            }
+        }
+
+
+        [TestMethod]
+        public void Test_Equal()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+            // #1 ==
+            {
+                var userList = userQuery.Where(u => u.name == "u356" || "u500" == u.name).ToList();
+                Assert.AreEqual(2, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u356", "u500" }).Count());
+            }
+
+            // #2 !=
+            {
+                var userList = userQuery.Where(u => u.name != "u146").ToList();
+                Assert.AreEqual(5, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u246", "u356", "u400", "u500", "u600" }).Count());
+            }
+            {
+                var userList = userQuery.Where(u => "u146" != u.name).ToList();
+                Assert.AreEqual(5, userList.Count);
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(new[] { "u246", "u356", "u400", "u500", "u600" }).Count());
+            }
+        }
+
+
+
+
+
+
+    }
+}

+ 105 - 0
test/Vitorm.ClickHouse.MsTest/CustomTest/CRUD_Test.cs

@@ -0,0 +1,105 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Data;
+
+namespace Vitorm.MsTest.CustomTest
+{
+
+    [TestClass]
+    public partial class CRUD_Test
+    {
+        static DbContext CreateDbContext() => DataSource.CreateDbContextForWriting();
+
+
+
+        #region #1 Create
+
+        [TestMethod]
+        public void Test_Create()
+        {
+            using var dbContext = CreateDbContext();
+
+            var newUserList = User.NewUsers(7, 4);
+
+
+            // #1 Add
+            dbContext.Add(newUserList[0]);
+
+            // #2 AddRange
+            dbContext.AddRange(newUserList.Skip(1));
+
+            Thread.Sleep(1000);
+
+            // assert
+            {
+                var userList = dbContext.Query<User>().Where(user => user.id >= 7).ToList();
+                Assert.AreEqual(newUserList.Count, userList.Count());
+                Assert.AreEqual(0, userList.Select(m => m.id).Except(newUserList.Select(m => m.id)).Count());
+                Assert.AreEqual(0, userList.Select(m => m.name).Except(newUserList.Select(m => m.name)).Count());
+            }
+
+            try
+            {
+                dbContext.Add(newUserList[0]);              
+            }
+            catch (Exception ex)
+            {
+                Assert.Fail("should be able to add same key twice");
+            }
+
+
+        }
+        #endregion
+
+
+      
+
+        #region #4 Delete
+
+
+        [TestMethod]
+        public void Test_Delete()
+        {
+            using var dbContext = CreateDbContext();
+
+            // #1 Delete
+            {
+                dbContext.Delete(User.NewUser(1));
+            }
+
+            // #2 DeleteRange
+            {
+                dbContext.DeleteRange(User.NewUsers(2, 2));
+            
+            }
+
+            // #3 DeleteByKey
+            {
+                var user = User.NewUser(4);
+                var key = dbContext.GetEntityDescriptor(typeof(User)).key;
+                var keyValue=key.GetValue(user);
+                dbContext.DeleteByKey<User>(keyValue);
+            
+            }
+
+            // #4 DeleteByKeys
+            {
+                var users = User.NewUsers(5, 2);
+                var key = dbContext.GetEntityDescriptor(typeof(User)).key;
+                var keyValues = users.Select(user => key.GetValue(user));
+                dbContext.DeleteByKeys<User, object>(keyValues);          
+            }
+
+            Thread.Sleep(1000);
+
+            // assert
+            {
+                var userList = dbContext.Query<User>().ToList();
+                Assert.AreEqual(0, userList.Count());
+            }
+        }
+        #endregion
+
+
+    }
+}

+ 43 - 0
test/Vitorm.ClickHouse.MsTest/CustomTest/DbFunction_Test.cs

@@ -0,0 +1,43 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CustomTest
+{
+
+    [TestClass]
+    public class DbFunction_Test
+    {
+        [TestMethod]
+        public void Test_DbFunction()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+
+            // select * from `User` as t0  where IF(`t0`.`fatherId` is not null,true, false)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<bool>("IF", u.fatherId != null, true, false));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(3, userList.Last().id);
+            }
+
+           
+
+            // coalesce(parameter1,parameter2, …)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(1, userList.First().id);
+            }
+
+
+        }
+
+
+    }
+}

+ 56 - 0
test/Vitorm.ClickHouse.MsTest/CustomTest/Orm_Extensions_ExecuteDelete_Test.cs

@@ -0,0 +1,56 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Vitorm_Extensions;
+using System.Data;
+
+namespace Vitorm.MsTest.CustomTest
+{
+
+    [TestClass]
+    public class Orm_Extensions_ExecuteDelete_Test
+    {
+        static DbContext CreateDbContext() => DataSource.CreateDbContextForWriting();
+
+
+        [TestMethod]
+        public void Test_ExecuteDelete()
+        {
+            if (1 == 1)
+            {
+                using var dbContext = CreateDbContext();
+                var userQuery = dbContext.Query<User>();
+
+                userQuery.Where(m => m.id == 2 || m.id == 4).ExecuteDelete();
+
+                Thread.Sleep(1000);
+
+                var newUsers = userQuery.ToList();
+                Assert.AreEqual(4, newUsers.Count());
+            }
+
+            if (1 == 1)
+            {
+                using var dbContext = CreateDbContext();
+                var userQuery = dbContext.Query<User>();
+
+                var query = from user in userQuery
+                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                            where user.id <= 5 && father.name != null
+                            select new
+                            {
+                                user,
+                                father
+                            };
+
+                query.ExecuteDelete();
+
+
+                Thread.Sleep(1000);
+
+                var newUsers = userQuery.ToList();
+                Assert.AreEqual(3, newUsers.Count()); 
+            }
+
+          
+        }
+    }
+}

+ 103 - 0
test/Vitorm.ClickHouse.MsTest/DataSource.cs

@@ -0,0 +1,103 @@
+using Vitorm.Sql;
+using Vit.Extensions;
+using Vit.Core.Util.ConfigurationManager;
+using System.ComponentModel.DataAnnotations.Schema;
+using ClickHouse.Client.ADO;
+
+namespace Vitorm.MsTest
+{
+    [System.ComponentModel.DataAnnotations.Schema.Table("User")]
+    public class User
+    {
+        [System.ComponentModel.DataAnnotations.Key]
+        public int id { get; set; }
+        public string name { get; set; }
+        public DateTime? birth { get; set; }
+
+        public int? fatherId { get; set; }
+        public int? motherId { get; set; }
+        public static User NewUser(int id) => new User { id = id, name = "testUser" + id };
+
+        public static List<User> NewUsers(int startId, int count = 1)
+        {
+            return Enumerable.Range(startId, count).Select(NewUser).ToList();
+        }
+    }
+
+
+    public class DataSource
+    {
+        readonly static string connectionString = Appsettings.json.GetStringByPath("App.Db.ConnectionString");
+
+        static int dbIndexCount = 0;
+        public static SqlDbContext CreateDbContextForWriting()
+        {
+            dbIndexCount++;
+            var dbName = "dev-orm" + dbIndexCount;
+            var connectionString = DataSource.connectionString;
+
+            // #1 create db
+            {
+                var dbContext = new SqlDbContext();
+                dbContext.UseClickHouse(connectionString);
+                dbContext.Execute(sql: $"create database if not exists `{dbName}`; ");
+            }
+
+            // #2
+            {
+                ClickHouseConnectionStringBuilder builder = new ClickHouseConnectionStringBuilder(connectionString);
+                builder.Database = dbName;
+                connectionString = builder.ToString();
+
+                var dbContext = new SqlDbContext();
+                dbContext.UseClickHouse(connectionString);
+
+                dbContext.Execute(sql: "DROP TABLE if exists `User`;");
+                dbContext.Create<User>();
+                InitDbContext(dbContext);
+
+                return dbContext;
+            }
+        }
+
+        static bool initedDefaultIndex = false;
+        public static SqlDbContext CreateDbContext()
+        {
+            var dbContext = new SqlDbContext();
+            dbContext.UseClickHouse(connectionString);
+
+            lock (typeof(DataSource))
+            {
+                if (!initedDefaultIndex)
+                {
+                    //dbContext.Execute(sql: "truncate TABLE `User`;");
+
+                    dbContext.Execute(sql: "DROP TABLE if exists `User`;");
+                    dbContext.Create<User>();
+                    InitDbContext(dbContext);
+
+                    initedDefaultIndex = true;
+                }
+            }
+
+            return dbContext;
+        }
+
+        static void InitDbContext(SqlDbContext dbContext)
+        {
+            var users = new List<User> {
+                    new User { id=1,  name="u146", fatherId=4, motherId=6 },
+                    new User { id=2,  name="u246", fatherId=4, motherId=6 },
+                    new User { id=3,  name="u356", fatherId=5, motherId=6 },
+                    new User { id=4,  name="u400" },
+                    new User { id=5,  name="u500" },
+                    new User { id=6,  name="u600" },
+                };
+
+            users.ForEach(user => { user.birth = DateTime.Parse("2021-01-01 00:00:00").AddHours(user.id); });
+
+            dbContext.AddRange(users);
+        }
+
+    }
+}

+ 42 - 0
test/Vitorm.ClickHouse.MsTest/Vitorm.ClickHouse.MsTest.csproj

@@ -0,0 +1,42 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+        <RootNamespace>Vitorm.MsTest</RootNamespace>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <Compile Remove="CustomTest\**" />
+      <EmbeddedResource Remove="CustomTest\**" />
+      <None Remove="CustomTest\**" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <Compile Include="CustomTest\CRUD_Test.cs" />
+      <Compile Include="CustomTest\DbFunction_Test.cs" />
+      <Compile Include="CustomTest\Orm_Extensions_ExecuteDelete_Test.cs" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="3.4.3" />
+        <PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
+
+        <PackageReference Include="Vit.Core" Version="2.1.21" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\..\src\Vitorm.ClickHouse\Vitorm.ClickHouse.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <None Update="appsettings.json">
+        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      </None>
+    </ItemGroup>
+
+</Project>

+ 7 - 0
test/Vitorm.ClickHouse.MsTest/appsettings.json

@@ -0,0 +1,7 @@
+{
+  "App": {
+    "Db": {
+      "ConnectionString": "Host=localhost;Port=8123;Database=dev-orm;User=default;Password=;Compress=True;CheckCompressedHash=False;Compressor=lz4;"
+    }
+  }
+}