├── .gitattributes ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── LICENSE ├── MicroApi.Authorization ├── MicroApi.Authorization.csproj └── MicroApiAuthorizationHandler.cs ├── MicroApi.Core ├── BaseSqlBuilder.cs ├── ConstEnums.cs ├── DefaultSqlBuilder.cs ├── ExpressionExtensions.cs ├── FluentSqlBuilder.cs ├── HandleResponse │ ├── BaseHandleResponse.cs │ ├── HandleResponseFactory.cs │ ├── HttpDeleteHandleResponse.cs │ ├── HttpGetHandleResponse.cs │ ├── HttpPostHandleResponse.cs │ ├── HttpPutHandleResponse.cs │ └── IHandleResponse.cs ├── IMappingBuilder.cs ├── IMicroApiBuilder.cs ├── IMiddleware.cs ├── ISqlBuilder.cs ├── MappingBuilder.cs ├── MicroApi.Core.csproj ├── MicroApiMiddleware.cs ├── MicroApiOperations.cs ├── MicroApiOption.cs ├── ObjectExtension.cs ├── StringExtensions.cs ├── TableMapping.cs └── TypeExtension.cs ├── MicroApi.Demo ├── Controllers │ ├── BaseController.cs │ └── LoginController.cs ├── JwtSettings.cs ├── MicroApi.Demo.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── MicroApi.Firebird ├── MicroApi.Firebird.csproj └── MicroApiMiddlewareExtensions.cs ├── MicroApi.MySql ├── MicroApi.MySql.csproj └── MicroApiMiddlewareExtensions.cs ├── MicroApi.Oracle ├── MicroApi.Oracle.csproj └── MicroApiMiddlewareExtensions.cs ├── MicroApi.PostgreSQL ├── MicroApi.PostgreSQL.csproj └── MicroApiMiddlewareExtensions.cs ├── MicroApi.SqlServer ├── AutoApiMiddlewareExtensions.cs └── MicroApi.SqlServer.csproj ├── MicroApi.Sqlite ├── MicroApi.Sqlite.csproj └── MicroApiMiddlewareExtensions.cs ├── MicroApi.sln ├── README-EN.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1.8.0 18 | with: 19 | dotnet-version: 6.0.100 20 | - name: Build MicroApi.Core 21 | run: dotnet build "MicroApi.Core/MicroApi.Core.csproj" --configuration Release 22 | - name: Build MicroApi.Demo 23 | run: dotnet build "MicroApi.Demo/MicroApi.Demo.csproj" --configuration Release 24 | - name: Build MicroApi.Firebird 25 | run: dotnet build "MicroApi.Firebird/MicroApi.Firebird.csproj" --configuration Release 26 | - name: Build MicroApi.MySql 27 | run: dotnet build "MicroApi.MySql/MicroApi.MySql.csproj" --configuration Release 28 | - name: Build MicroApi.Oracle 29 | run: dotnet build "MicroApi.Oracle/MicroApi.Oracle.csproj" --configuration Release 30 | - name: Build MicroApi.PostgreSQL 31 | run: dotnet build "MicroApi.PostgreSQL/MicroApi.PostgreSQL.csproj" --configuration Release 32 | - name: Build MicroApi.Sqlite 33 | run: dotnet build "MicroApi.Sqlite/MicroApi.Sqlite.csproj" --configuration Release 34 | - name: Build MicroApi.SqlServer 35 | run: dotnet build "MicroApi.SqlServer/MicroApi.SqlServer.csproj" --configuration Release 36 | - name: Pack MicroApi.Core 37 | run: dotnet pack "MicroApi.Core/MicroApi.Core.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 38 | - name: Pack MicroApi.Firebird 39 | run: dotnet pack "MicroApi.Firebird/MicroApi.Firebird.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 40 | - name: Pack MicroApi.MySql 41 | run: dotnet pack "MicroApi.MySql/MicroApi.MySql.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 42 | - name: Pack MicroApi.Oracle 43 | run: dotnet pack "MicroApi.Oracle/MicroApi.Oracle.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 44 | - name: Pack MicroApi.PostgreSQL 45 | run: dotnet pack "MicroApi.PostgreSQL/MicroApi.PostgreSQL.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 46 | - name: Pack MicroApi.Sqlite 47 | run: dotnet pack "MicroApi.Sqlite/MicroApi.Sqlite.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 48 | - name: Pack MicroApi.SqlServer 49 | run: dotnet pack "MicroApi.SqlServer/MicroApi.SqlServer.csproj" --configuration Release -p:PackageVersion=1.0.0 --output nupkgs 50 | - name: Publish MicroApi.Core 51 | run: dotnet nuget push "nupkgs/MicroApi.Core.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 52 | - name: Publish MicroApi.Firebird 53 | run: dotnet nuget push "nupkgs/MicroApi.Firebird.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 54 | - name: Publish MicroApi.MySql 55 | run: dotnet nuget push "nupkgs/MicroApi.MySql.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 56 | - name: Publish MicroApi.Oracle 57 | run: dotnet nuget push "nupkgs/MicroApi.Oracle.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 58 | - name: Publish MicroApi.PostgreSQL 59 | run: dotnet nuget push "nupkgs/MicroApi.PostgreSQL.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 60 | - name: Publish MicroApi.Sqlite 61 | run: dotnet nuget push "nupkgs/MicroApi.Sqlite.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 62 | - name: Publish MicroApi.SqlServer 63 | run: dotnet nuget push "nupkgs/MicroApi.SqlServer.1.0.0.nupkg" --skip-duplicate --api-key oy2pfof4by2kj2f2xuzqy6qaba4mtfjuzevxg2aq2bopgy --source "https://api.nuget.org/v3/index.json" 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 旺仔 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MicroApi.Authorization/MicroApi.Authorization.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.3 6 | 1.0.0.3 7 | 1.0.0.3 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.3 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MicroApi.Authorization/MicroApiAuthorizationHandler.cs: -------------------------------------------------------------------------------- 1 | using MicroApi.Core.HandleResponse; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Authorization.Infrastructure; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Security.Claims; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace MicroApi.Authorization 13 | { 14 | public class MicroApiAuthorizationHandler : AuthorizationHandler 15 | { 16 | protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement, IHandleResponse resource) 17 | { 18 | if (context.User.HasClaim(m=>m.Type == ClaimTypes.Name)) 19 | { 20 | context.Succeed(requirement); 21 | } 22 | else 23 | { 24 | context.Fail(); 25 | } 26 | return Task.CompletedTask; 27 | } 28 | } 29 | 30 | public static class MicroApiAuthorizationExtension 31 | { 32 | public static IServiceCollection AddMicroApiAuthorization(this IServiceCollection services) => 33 | services.AddSingleton(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /MicroApi.Core/BaseSqlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using MicroApi.Core.Request; 4 | using Microsoft.AspNetCore.Http; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using SqlKata; 8 | 9 | namespace MicroApi.Core; 10 | 11 | public abstract class BaseSqlBuilder : ISqlBuilder 12 | { 13 | private readonly IRequest _request; 14 | 15 | protected BaseSqlBuilder(IRequest request) 16 | { 17 | _request = request; 18 | } 19 | 20 | public virtual string GetTableName() 21 | { 22 | return _httpContext.GetRouteValue("controller").ToString(); 23 | } 24 | 25 | public virtual Dictionary GetRequestColumns() 26 | { 27 | if (_httpContext.Request.Method == HttpMethods.Get || _httpContext.Request.Method == HttpMethods.Delete) 28 | { 29 | return new Dictionary(); 30 | } 31 | //获取body 32 | var sr = new StreamReader(_httpContext.Request.Body); 33 | var body = sr.ReadToEndAsync().Result; 34 | _httpContext.Request.Body.Seek(0, SeekOrigin.Begin); 35 | 36 | var entity = (JObject)JsonConvert.DeserializeObject(body); 37 | 38 | var t = new Dictionary(); 39 | foreach (var p in entity.Properties()) 40 | { 41 | t.Add(p.Name, ((JValue)entity[p.Name])?.Value?.ToString()); 42 | } 43 | 44 | return t; 45 | } 46 | 47 | public Query BuildQuery() 48 | { 49 | return _request.BuildQuery(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MicroApi.Core/ConstEnums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MicroApi.Core 8 | { 9 | /// 10 | /// 来自 11 | /// 12 | public enum DataType 13 | { 14 | MySql = 0, 15 | SqlServer = 1, 16 | PostgreSQL = 2, 17 | Oracle = 3, 18 | Sqlite = 4, 19 | // 20 | // 摘要: 21 | // Firebird 是一个跨平台的关系数据库,能作为多用户环境下的数据库服务器运行,也提供嵌入式数据库的实现 22 | Firebird = 5, 23 | } 24 | 25 | public enum AuthorizeType 26 | { 27 | /// 28 | /// 不需要认证 29 | /// 30 | None = 0, 31 | /// 32 | /// JWT认证 33 | /// 34 | JWT 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /MicroApi.Core/DefaultSqlBuilder.cs: -------------------------------------------------------------------------------- 1 | using MicroApi.Core.Request; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace MicroApi.Core; 5 | 6 | internal class DefaultSqlBuilder : BaseSqlBuilder 7 | { 8 | public DefaultSqlBuilder(IRequest request) : base(request) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MicroApi.Core/ExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using MicroApi.Core; 7 | using Newtonsoft.Json; 8 | 9 | namespace MicroApi.Core 10 | { 11 | /// 12 | /// 表达式树常用扩展方法 13 | /// 源自:https://gitee.com/yubaolee/OpenAuth.Net/blob/bestflow/Infrastructure/DynamicLinq.cs 14 | /// 15 | public static class ExpressionExtensions 16 | { 17 | /// 18 | /// 取的 Expression> predicate 表达式对应的属性名称 19 | /// 例如:c=>c.Value.Year 侧返回:Value.Year 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | public static string GetPropertyName(this Expression> predicate) 26 | { 27 | var expression = predicate.Body as MemberExpression; 28 | //return expression.Member.Name; //该属性只返回最后一个属性,因此采用下面方法返回。 29 | return expression.ToString().Substring(2); 30 | } 31 | public static ParameterExpression CreateLambdaParam(string name) 32 | { 33 | return Expression.Parameter(typeof(T), name); 34 | } 35 | 36 | /// 37 | /// 创建linq表达示的body部分 38 | /// 39 | public static Expression GenerateBody(this ParameterExpression param, Filter filterObj) 40 | { 41 | PropertyInfo property = typeof(T).GetProperty(filterObj.Key); 42 | 43 | //组装左边 44 | Expression left = Expression.Property(param, property); 45 | //组装右边 46 | Expression right = null; 47 | 48 | if (property.PropertyType == typeof(int)) 49 | { 50 | right = Expression.Constant(int.Parse(filterObj.Value)); 51 | } 52 | else if (property.PropertyType == typeof(DateTime)) 53 | { 54 | right = Expression.Constant(DateTime.Parse(filterObj.Value)); 55 | } 56 | else if (property.PropertyType == typeof(string)) 57 | { 58 | right = Expression.Constant(filterObj.Value); 59 | } 60 | else if (property.PropertyType == typeof(decimal)) 61 | { 62 | right = Expression.Constant(decimal.Parse(filterObj.Value)); 63 | } 64 | else if (property.PropertyType == typeof(Guid)) 65 | { 66 | right = Expression.Constant(Guid.Parse(filterObj.Value)); 67 | } 68 | else if (property.PropertyType == typeof(bool)) 69 | { 70 | right = Expression.Constant(filterObj.Value.Equals("1")); 71 | } 72 | else if (property.PropertyType == typeof(Guid?)) 73 | { 74 | left = Expression.Property(left, "Value"); 75 | right = Expression.Constant(Guid.Parse(filterObj.Value)); 76 | } 77 | else 78 | { 79 | throw new Exception("暂不能解析该Key的类型"); 80 | } 81 | 82 | //c.XXX=="XXX" 83 | Expression filter = Expression.Equal(left, right); 84 | switch (filterObj.Contrast) 85 | { 86 | case "<=": 87 | filter = Expression.LessThanOrEqual(left, right); 88 | break; 89 | 90 | case "<": 91 | filter = Expression.LessThan(left, right); 92 | break; 93 | 94 | case ">": 95 | filter = Expression.GreaterThan(left, right); 96 | break; 97 | 98 | case ">=": 99 | filter = Expression.GreaterThanOrEqual(left, right); 100 | break; 101 | case "!=": 102 | filter = Expression.NotEqual(left, right); 103 | break; 104 | 105 | case "like": 106 | filter = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), 107 | Expression.Constant(filterObj.Value)); 108 | break; 109 | case "not in": 110 | var listExpression = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 111 | var method = typeof(List).GetMethod("Contains", new Type[] { typeof(string) }); //Contains语句 112 | filter = Expression.Not(Expression.Call(listExpression, method, left)); 113 | break; 114 | case "in": 115 | var lExp = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 116 | var methodInfo = 117 | typeof(List).GetMethod("Contains", new Type[] { typeof(string) }); //Contains语句 118 | filter = Expression.Call(lExp, methodInfo, left); 119 | break; 120 | } 121 | 122 | return filter; 123 | } 124 | 125 | public static Expression> GenerateTypeBody(this ParameterExpression param, Filter filterObj) 126 | { 127 | return (Expression>)param.GenerateBody(filterObj); 128 | } 129 | 130 | /// 131 | /// 创建完整的lambda 132 | /// 133 | public static LambdaExpression GenerateLambda(this ParameterExpression param, Expression body) 134 | { 135 | //c=>c.XXX=="XXX" 136 | return Expression.Lambda(body, param); 137 | } 138 | 139 | public static Expression> GenerateTypeLambda(this ParameterExpression param, Expression body) 140 | { 141 | return (Expression>)param.GenerateLambda(body); 142 | } 143 | 144 | public static Expression AndAlso(this Expression expression, Expression expressionRight) 145 | { 146 | return Expression.AndAlso(expression, expressionRight); 147 | } 148 | 149 | public static Expression Or(this Expression expression, Expression expressionRight) 150 | { 151 | return Expression.Or(expression, expressionRight); 152 | } 153 | 154 | public static Expression And(this Expression expression, Expression expressionRight) 155 | { 156 | return Expression.And(expression, expressionRight); 157 | } 158 | 159 | //系统已经有该函数的实现 160 | //public static IQueryable Where(this IQueryable query, Expression expression) 161 | //{ 162 | // Expression expr = Expression.Call(typeof(Queryable), "Where", new[] { typeof(T) }, 163 | // Expression.Constant(query), expression); 164 | // //生成动态查询 165 | // IQueryable result = query.Provider.CreateQuery(expr); 166 | // return result; 167 | //} 168 | 169 | public static IQueryable GenerateFilter(this IQueryable query, string filterjson) 170 | { 171 | if (!string.IsNullOrEmpty(filterjson)) 172 | { 173 | var filters = JsonConvert.DeserializeObject>(filterjson); 174 | var param = CreateLambdaParam("c"); 175 | 176 | Expression result = Expression.Constant(true); 177 | foreach (var filter in filters) 178 | { 179 | result = result.AndAlso(param.GenerateBody(filter)); 180 | } 181 | 182 | query = query.Where(param.GenerateTypeLambda(result)); 183 | } 184 | return query; 185 | } 186 | 187 | /// 188 | /// 以特定的条件运行组合两个Expression表达式 189 | /// 190 | /// 表达式的主实体类型 191 | /// 第一个Expression表达式 192 | /// 要组合的Expression表达式 193 | /// 组合条件运算方式 194 | /// 组合后的表达式 195 | public static Expression Compose(this Expression first, Expression second, Func merge) 196 | { 197 | // build parameter map (from parameters of second to parameters of first) 198 | Dictionary map = 199 | first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); 200 | 201 | // replace parameters in the second lambda expression with parameters from the first 202 | Expression secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); 203 | 204 | // apply composition of lambda expression bodies to parameters from the first expression 205 | return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); 206 | } 207 | 208 | /// 209 | /// 以 Expression.AndAlso 组合两个Expression表达式 210 | /// 211 | /// 表达式的主实体类型 212 | /// 第一个Expression表达式 213 | /// 要组合的Expression表达式 214 | /// 组合后的表达式 215 | public static Expression> And(this Expression> first, Expression> second) 216 | { 217 | return first.Compose(second, Expression.AndAlso); 218 | } 219 | 220 | /// 221 | /// 以 Expression.OrElse 组合两个Expression表达式 222 | /// 223 | /// 表达式的主实体类型 224 | /// 第一个Expression表达式 225 | /// 要组合的Expression表达式 226 | /// 组合后的表达式 227 | public static Expression> Or(this Expression> first, Expression> second) 228 | { 229 | return first.Compose(second, Expression.OrElse); 230 | } 231 | 232 | /// 233 | /// 参数重新绑定 234 | /// 235 | /// 236 | private class ParameterRebinder : ExpressionVisitor 237 | { 238 | private readonly Dictionary _map; 239 | 240 | /// 241 | /// Initializes a new instance of the class. 242 | /// 243 | /// The map. 244 | private ParameterRebinder(Dictionary map) 245 | { 246 | _map = map ?? new Dictionary(); 247 | } 248 | 249 | /// 250 | /// Replaces the parameters. 251 | /// 252 | /// The map. 253 | /// The exp. 254 | /// 255 | public static Expression ReplaceParameters(Dictionary map, Expression exp) 256 | { 257 | return new ParameterRebinder(map).Visit(exp); 258 | } 259 | 260 | /// 261 | /// 访问 。 262 | /// 263 | /// 要访问的表达式。 264 | /// 265 | /// 如果修改了该表达式或任何子表达式,则为修改后的表达式;否则返回原始表达式。 266 | /// 267 | protected override Expression VisitParameter(ParameterExpression node) 268 | { 269 | ParameterExpression replacement; 270 | if (_map.TryGetValue(node, out replacement)) 271 | { 272 | node = replacement; 273 | } 274 | return base.VisitParameter(node); 275 | } 276 | } 277 | } 278 | 279 | public class Filter 280 | { 281 | public string Key { get; set; } 282 | public string Value { get; set; } 283 | public string Contrast { get; set; } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /MicroApi.Core/FluentSqlBuilder.cs: -------------------------------------------------------------------------------- 1 | using MicroApi.Core; 2 | using MicroApi.Core.Request; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace MicroApi.Mapper; 6 | 7 | public class FluentSqlBuilder:BaseSqlBuilder 8 | { 9 | private readonly IMappingBuilder _mappingBuilder; 10 | public FluentSqlBuilder(IRequest request, IMappingBuilder mappingBuilder) : base(request) 11 | { 12 | _mappingBuilder = mappingBuilder; 13 | } 14 | 15 | public override string GetTableName() 16 | { 17 | var requestName = base.GetTableName(); 18 | return GetTableMapping()?.Source ?? requestName; 19 | } 20 | 21 | private TableMapping? GetTableMapping() 22 | { 23 | return _mappingBuilder.Build().FirstOrDefault(m => 24 | m.Target.Equals(base.GetTableName(), StringComparison.OrdinalIgnoreCase)); 25 | } 26 | 27 | public override Dictionary GetRequestColumns() 28 | { 29 | var columns = base.GetRequestColumns(); 30 | var tableMapping = GetTableMapping(); 31 | if (!columns.Any() || !(tableMapping?.ColumnConfig?.Any() ?? false)) 32 | { 33 | return columns; 34 | } 35 | 36 | var result = new Dictionary(); 37 | foreach (var item in columns) 38 | { 39 | var dbColumnName = tableMapping.ColumnConfig.FirstOrDefault(c => 40 | c.Key.Equals(item.Key, StringComparison.OrdinalIgnoreCase)) 41 | .Value; 42 | result.Add(item.Key, String.IsNullOrEmpty(dbColumnName)? item.Value: dbColumnName); 43 | } 44 | 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/BaseHandleResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Http; 3 | using SqlKata; 4 | using SqlKata.Execution; 5 | 6 | namespace MicroApi.Core.HandleResponse 7 | { 8 | public abstract class BaseHandleResponse : IHandleResponse 9 | { 10 | public HttpContext Context { get; set; } 11 | public abstract object Execute(); 12 | public string TableName { get; set; } 13 | 14 | public QueryFactory Query { get; set; } 15 | 16 | public BaseHandleResponse(IHttpContextAccessor contextAccessor, QueryFactory query) 17 | { 18 | Context = contextAccessor.HttpContext; 19 | Query = query; 20 | TableName = GetTableName(); 21 | } 22 | 23 | private string GetTableName() 24 | { 25 | var path = Context.Request.Path; 26 | 27 | var paths = path.Value.Split("/"); 28 | 29 | if (paths.Length < 2) 30 | { 31 | throw new Exception("非法请求,api请求路径格式为/api/{TableName},且TableName不能为空。");//api请求路径格式为/api/{TableName} 32 | } 33 | var table = paths[1]; 34 | if (table.IsNullOrWhiteSpace()) 35 | { 36 | throw new Exception("非法请求,api请求路径格式为/api/{TableName},且TableName不能为空。");//api请求路径格式为/api/{TableName} 37 | } 38 | return table; 39 | } 40 | protected Query GetQuery() => Query.Query().From(TableName); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/HandleResponseFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Humanizer; 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace MicroApi.Core.HandleResponse 7 | { 8 | public class HandleResponseFactory 9 | { 10 | public static IHandleResponse CreateHandleResponse(HttpContext context) 11 | { 12 | return (IHandleResponse)context.RequestServices.GetService(Type.GetType($"MicroApi.Core.HandleResponse.IHttp{context.Request.Method.ToLower().Titleize()}HandleResponse")); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/HttpDeleteHandleResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Http; 3 | using SqlKata.Execution; 4 | 5 | namespace MicroApi.Core.HandleResponse 6 | { 7 | public interface IHttpDeleteHandleResponse : IHandleResponse 8 | { 9 | 10 | } 11 | 12 | public class HttpDeleteHandleResponse : BaseHandleResponse, IHttpDeleteHandleResponse 13 | { 14 | public HttpDeleteHandleResponse(IHttpContextAccessor contextAccessor, QueryFactory query) : base(contextAccessor, query) 15 | { 16 | } 17 | 18 | public override object Execute() 19 | { 20 | var query = GetQuery(); 21 | 22 | foreach (var item in Context.Request.Query) 23 | { 24 | #region 特殊运算,比如大于等于、小于等于、大于、小于、IN查询 25 | 26 | //大于等于 27 | if (item.Key.EndsWith(".ge", StringComparison.OrdinalIgnoreCase)) 28 | { 29 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), ">=", 30 | item.Value.ToString()); 31 | continue; 32 | } 33 | //大于 34 | if (item.Key.EndsWith(".gt", StringComparison.OrdinalIgnoreCase)) 35 | { 36 | query.Where(item.Key.Replace(".gt", "", StringComparison.OrdinalIgnoreCase), ">", 37 | item.Value.ToString()); 38 | continue; 39 | } 40 | //小于等于 41 | if (item.Key.EndsWith(".le", StringComparison.OrdinalIgnoreCase)) 42 | { 43 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), "<=", 44 | item.Value.ToString()); 45 | continue; 46 | } 47 | //小于 48 | if (item.Key.EndsWith(".lt", StringComparison.OrdinalIgnoreCase)) 49 | { 50 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), "<", 51 | item.Value.ToString()); 52 | continue; 53 | } 54 | //在范围内 55 | if (item.Key.EndsWith(".in", StringComparison.OrdinalIgnoreCase)) 56 | { 57 | query.WhereIn(item.Key.Replace(".in", "", StringComparison.OrdinalIgnoreCase), 58 | item.Value.ToString().Split(",")); 59 | continue; 60 | } 61 | //模糊查询 62 | if (item.Key.EndsWith(".like", StringComparison.OrdinalIgnoreCase)) 63 | { 64 | query.WhereLike(item.Key.Replace(".like", "", StringComparison.OrdinalIgnoreCase), 65 | item.Value.ToString()); 66 | continue; 67 | } 68 | 69 | #endregion 70 | 71 | query = query.Where(item.Key, item.Value.ToString()); 72 | } 73 | 74 | return query.Delete(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/HttpGetHandleResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Http; 3 | using SqlKata.Execution; 4 | 5 | namespace MicroApi.Core.HandleResponse 6 | { 7 | public interface IHttpGetHandleResponse : IHandleResponse 8 | { 9 | 10 | } 11 | 12 | public class HttpGetHandleResponse : BaseHandleResponse, IHttpGetHandleResponse 13 | { 14 | public HttpGetHandleResponse(IHttpContextAccessor contextAccessor, QueryFactory query) : base(contextAccessor, query) 15 | { 16 | } 17 | 18 | public override object Execute() 19 | { 20 | 21 | var query = GetQuery(); 22 | 23 | int? page = null; 24 | int? size = null; 25 | int? offset = null; 26 | 27 | foreach (var item in Context.Request.Query) 28 | { 29 | #region 分页 30 | 31 | if ("page".Equals(item.Key, StringComparison.OrdinalIgnoreCase)) 32 | { 33 | page = item.Value.ToString().ToInt(1); 34 | continue; 35 | } 36 | if ("offset".Equals(item.Key, StringComparison.OrdinalIgnoreCase)) 37 | { 38 | offset = item.Value.ToString().ToInt(10); 39 | continue; 40 | } 41 | if ("size".Equals(item.Key, StringComparison.OrdinalIgnoreCase) || "limit".Equals(item.Key, StringComparison.OrdinalIgnoreCase)) 42 | { 43 | size = item.Value.ToString().ToInt(10); 44 | continue; 45 | } 46 | 47 | #endregion 48 | 49 | #region 排序 50 | 51 | //正序 52 | if ("orderAsc".Equals(item.Key, StringComparison.OrdinalIgnoreCase)) 53 | { 54 | query = query.OrderBy(item.Value.ToString().Split(",")); 55 | continue; 56 | } 57 | //倒序 58 | if ("orderDesc".Equals(item.Key, StringComparison.OrdinalIgnoreCase)) 59 | { 60 | query = query.OrderByDesc(item.Value.ToString().Split(",")); 61 | continue; 62 | } 63 | 64 | #endregion 65 | 66 | #region 特殊运算,比如大于等于、小于等于、大于、小于、IN查询 67 | 68 | //大于等于 69 | if (item.Key.EndsWith(".ge", StringComparison.OrdinalIgnoreCase)) 70 | { 71 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), ">=", 72 | item.Value.ToString()); 73 | continue; 74 | } 75 | //大于 76 | if (item.Key.EndsWith(".gt", StringComparison.OrdinalIgnoreCase)) 77 | { 78 | query.Where(item.Key.Replace(".gt", "", StringComparison.OrdinalIgnoreCase), ">", 79 | item.Value.ToString()); 80 | continue; 81 | } 82 | //小于等于 83 | if (item.Key.EndsWith(".le", StringComparison.OrdinalIgnoreCase)) 84 | { 85 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), "<=", 86 | item.Value.ToString()); 87 | continue; 88 | } 89 | //小于 90 | if (item.Key.EndsWith(".lt", StringComparison.OrdinalIgnoreCase)) 91 | { 92 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), "<", 93 | item.Value.ToString()); 94 | continue; 95 | } 96 | //在范围内 97 | if (item.Key.EndsWith(".in", StringComparison.OrdinalIgnoreCase)) 98 | { 99 | query.WhereIn(item.Key.Replace(".in", "", StringComparison.OrdinalIgnoreCase), 100 | item.Value.ToString().Split(",")); 101 | continue; 102 | } 103 | //模糊查询 104 | if (item.Key.EndsWith(".like", StringComparison.OrdinalIgnoreCase)) 105 | { 106 | query.WhereLike(item.Key.Replace(".like", "", StringComparison.OrdinalIgnoreCase), 107 | item.Value.ToString()); 108 | continue; 109 | } 110 | 111 | #endregion 112 | 113 | query = query.Where(item.Key, item.Value[0]); 114 | } 115 | 116 | if (page.HasValue || size.HasValue) 117 | { 118 | if (!offset.HasValue) 119 | offset = ((page ?? 1) - 1) * (size ?? 10); 120 | query = query.Skip(offset.Value).Take(size ?? 10); 121 | } 122 | 123 | var dt = query.Get(); 124 | return dt; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/HttpPostHandleResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.AspNetCore.Http; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using SqlKata.Execution; 8 | 9 | namespace MicroApi.Core.HandleResponse 10 | { 11 | public interface IHttpPostHandleResponse : IHandleResponse 12 | { 13 | 14 | } 15 | 16 | public class HttpPostHandleResponse : BaseHandleResponse, IHttpPostHandleResponse 17 | { 18 | public HttpPostHandleResponse(IHttpContextAccessor contextAccessor, QueryFactory query) : base(contextAccessor, query) 19 | { 20 | } 21 | 22 | public override object Execute() 23 | { 24 | var query = GetQuery(); 25 | 26 | //获取body 27 | var sr = new StreamReader(Context.Request.Body); 28 | var body = sr.ReadToEndAsync().Result; 29 | Context.Request.Body.Seek(0, SeekOrigin.Begin); 30 | 31 | var entity = (JObject)JsonConvert.DeserializeObject(body); 32 | 33 | var t = new List>(); 34 | foreach (var p in entity.Properties()) 35 | { 36 | t.Add(new KeyValuePair(p.Name, ((JValue)entity[p.Name]).Value)); 37 | } 38 | 39 | var ret = query.Insert(t); 40 | 41 | return ret; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/HttpPutHandleResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.AspNetCore.Http; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using SqlKata.Execution; 8 | 9 | namespace MicroApi.Core.HandleResponse 10 | { 11 | public interface IHttpPutHandleResponse : IHandleResponse 12 | { 13 | 14 | } 15 | 16 | public class HttpPutHandleResponse : BaseHandleResponse, IHttpPutHandleResponse 17 | { 18 | public HttpPutHandleResponse(IHttpContextAccessor contextAccessor, QueryFactory query) : base(contextAccessor, query) 19 | { 20 | } 21 | 22 | public override object Execute() 23 | { 24 | var query = GetQuery(); 25 | 26 | foreach (var item in Context.Request.Query) 27 | { 28 | #region 特殊运算,比如大于等于、小于等于、大于、小于、IN查询 29 | 30 | //大于等于 31 | if (item.Key.EndsWith(".ge", StringComparison.OrdinalIgnoreCase)) 32 | { 33 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), ">=", 34 | item.Value.ToString()); 35 | continue; 36 | } 37 | //大于 38 | if (item.Key.EndsWith(".gt", StringComparison.OrdinalIgnoreCase)) 39 | { 40 | query.Where(item.Key.Replace(".gt", "", StringComparison.OrdinalIgnoreCase), ">", 41 | item.Value.ToString()); 42 | continue; 43 | } 44 | //小于等于 45 | if (item.Key.EndsWith(".le", StringComparison.OrdinalIgnoreCase)) 46 | { 47 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), "<=", 48 | item.Value.ToString()); 49 | continue; 50 | } 51 | //小于 52 | if (item.Key.EndsWith(".lt", StringComparison.OrdinalIgnoreCase)) 53 | { 54 | query.Where(item.Key.Replace(".ge", "", StringComparison.OrdinalIgnoreCase), "<", 55 | item.Value.ToString()); 56 | continue; 57 | } 58 | //在范围内 59 | if (item.Key.EndsWith(".in", StringComparison.OrdinalIgnoreCase)) 60 | { 61 | query.WhereIn(item.Key.Replace(".in", "", StringComparison.OrdinalIgnoreCase), 62 | item.Value.ToString().Split(",")); 63 | continue; 64 | } 65 | //模糊查询 66 | if (item.Key.EndsWith(".like", StringComparison.OrdinalIgnoreCase)) 67 | { 68 | query.WhereLike(item.Key.Replace(".like", "", StringComparison.OrdinalIgnoreCase), 69 | item.Value.ToString()); 70 | continue; 71 | } 72 | 73 | #endregion 74 | 75 | query = query.Where(item.Key, item.Value.ToString()); 76 | } 77 | 78 | //获取body 79 | var sr = new StreamReader(Context.Request.Body); 80 | var body = sr.ReadToEndAsync().Result; 81 | Context.Request.Body.Seek(0, SeekOrigin.Begin); 82 | 83 | var entity = (JObject)JsonConvert.DeserializeObject(body); 84 | var t = new List>(); 85 | foreach (var p in entity.Properties()) 86 | { 87 | t.Add(new KeyValuePair(p.Name, ((JValue)entity[p.Name]).Value)); 88 | } 89 | 90 | 91 | var ret = query.Update(t); 92 | 93 | return ret; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /MicroApi.Core/HandleResponse/IHandleResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | 9 | namespace MicroApi.Core.HandleResponse 10 | { 11 | public interface IHandleResponse 12 | { 13 | HttpContext Context { get; set; } 14 | object Execute(); 15 | string TableName { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MicroApi.Core/IMappingBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace MicroApi.Mapper; 4 | 5 | public interface IMappingBuilder 6 | { 7 | IMappingBuilder CreateMap() where TFluentEntity : class, new(); 8 | 9 | List Build(); 10 | } 11 | 12 | 13 | public static class MappingBuilderExtensions 14 | { 15 | public static IServiceCollection AddFluentMapping(this IServiceCollection collection, Action config) 16 | { 17 | IMappingBuilder builder = new MappingBuilder(); 18 | config?.Invoke(builder); 19 | return collection.AddSingleton(builder); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /MicroApi.Core/IMicroApiBuilder.cs: -------------------------------------------------------------------------------- 1 | using MicroApi.Core.HandleResponse; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.VisualBasic.FileIO; 5 | using Newtonsoft.Json.Serialization; 6 | using Newtonsoft.Json; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using static Dapper.SqlMapper; 13 | 14 | namespace MicroApi.Core 15 | { 16 | public interface IMicroApiBuilder 17 | { 18 | IServiceCollection Services { get; set; } 19 | } 20 | 21 | internal class MicroApiBuilder : IMicroApiBuilder 22 | { 23 | public IServiceCollection Services { get; set; } 24 | 25 | internal MicroApiBuilder(IServiceCollection services) 26 | { 27 | Services = services; 28 | } 29 | } 30 | 31 | public static class MicroApiMiddlewareExtensions 32 | { 33 | public static IMicroApiBuilder PrepareApiBuilder(this IServiceCollection services) 34 | { 35 | return new MicroApiBuilder(services); 36 | } 37 | 38 | public static IMicroApiBuilder AddMicroApi(this IMicroApiBuilder builder, Action option = null) 39 | { 40 | if (builder == null) throw new ArgumentNullException(nameof(builder)); 41 | var defaultOption = new MicroApiOption() 42 | { 43 | AuthorizeType = AuthorizeType.None, 44 | JsonSerializerSettings = new JsonSerializerSettings() 45 | { 46 | DateFormatString = "yyyy-MM-dd HH:mm:ss", 47 | ContractResolver = new CamelCasePropertyNamesContractResolver() 48 | } 49 | }; 50 | if (option != null) 51 | option.Invoke(defaultOption); 52 | builder.Services.AddSingleton(defaultOption); 53 | builder.Services.AddSingleton(); 54 | 55 | builder.Services.AddScoped(); 56 | builder.Services.AddScoped(); 57 | builder.Services.AddScoped(); 58 | builder.Services.AddScoped(); 59 | 60 | return builder; 61 | } 62 | 63 | public static IServiceCollection Build(this IMicroApiBuilder builder) 64 | { 65 | if (builder == null) throw new ArgumentNullException(nameof(builder)); 66 | return builder.Services; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /MicroApi.Core/IMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MicroApi.Core 8 | { 9 | public interface IMiddleware 10 | { 11 | Task Invoke(HttpContext context); 12 | } 13 | 14 | public abstract class BaseMiddleware : IMiddleware 15 | { 16 | protected readonly RequestDelegate _next; 17 | 18 | public BaseMiddleware(RequestDelegate next) 19 | { 20 | _next = next; 21 | } 22 | 23 | public abstract Task Invoke(HttpContext context); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MicroApi.Core/ISqlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SqlKata; 3 | 4 | namespace MicroApi.Core; 5 | 6 | public interface ISqlBuilder 7 | { 8 | // string GetTableName(); 9 | // 10 | // Dictionary GetRequestColumns(); 11 | 12 | Query BuildQuery(); 13 | } 14 | -------------------------------------------------------------------------------- /MicroApi.Core/MappingBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.Reflection; 3 | 4 | namespace MicroApi.Mapper; 5 | 6 | internal class MappingBuilder:IMappingBuilder 7 | { 8 | private readonly List _mappings = new List(); 9 | 10 | public IMappingBuilder CreateMap() where TFluentEntity:class,new() 11 | { 12 | if (_mappings.Exists(m => m.Source == typeof(TFluentEntity).Name)) 13 | return this; 14 | var mapping = new TableMapping() 15 | { 16 | SourceType = typeof(TFluentEntity), 17 | Target = GetTableName(true), 18 | ColumnConfig = GetColumnMappings() 19 | }; 20 | _mappings.Add(mapping); 21 | return this; 22 | } 23 | 24 | public List Build() 25 | { 26 | return _mappings; 27 | } 28 | 29 | private string GetTableName(bool fluent = false) where T : class, new() 30 | { 31 | var tableName = typeof(T).Name; 32 | if (fluent) 33 | { 34 | var tableAttr = typeof(T).GetCustomAttribute(typeof(TableAttribute)); 35 | if (tableAttr is TableAttribute) 36 | { 37 | tableName = ((TableAttribute)tableAttr).Name; 38 | } 39 | } 40 | return tableName; 41 | } 42 | 43 | private Dictionary GetColumnMappings() where T : class, new() 44 | { 45 | var list = new Dictionary(); 46 | var properties = typeof(T).GetProperties(); 47 | foreach (var property in properties) 48 | { 49 | list.Add(property.Name, GetColumnName(property, true)); 50 | } 51 | return list; 52 | } 53 | 54 | private string GetColumnName(PropertyInfo propertyInfo, bool fluent = false) 55 | { 56 | var columnName = propertyInfo.Name; 57 | if (fluent) 58 | { 59 | var columnAttr = propertyInfo.GetCustomAttribute(typeof(ColumnAttribute)); 60 | if (columnAttr is ColumnAttribute) 61 | { 62 | columnName = ((ColumnAttribute)columnAttr).Name; 63 | } 64 | } 65 | 66 | return columnName; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /MicroApi.Core/MicroApi.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.3 6 | 1.0.0.3 7 | 1.0.0.3 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.3 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /MicroApi.Core/MicroApiMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Routing; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Net; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Newtonsoft.Json; 11 | using Newtonsoft.Json.Serialization; 12 | using MicroApi.Core.HandleResponse; 13 | using Microsoft.AspNetCore.Authorization; 14 | using Microsoft.Extensions.Options; 15 | 16 | namespace MicroApi.Core 17 | { 18 | public class MicroApiMiddleware : BaseMiddleware 19 | { 20 | private readonly IAuthorizationService _authService; 21 | private readonly MicroApiOption _apiOption; 22 | public MicroApiMiddleware(RequestDelegate next, 23 | IAuthorizationService authorizationService, 24 | MicroApiOption apiOption) : base(next) 25 | { 26 | _authService = authorizationService; 27 | _apiOption = apiOption; 28 | } 29 | 30 | public override async Task Invoke(HttpContext context) 31 | { 32 | context.Request.EnableBuffering(); 33 | await _next(context); 34 | 35 | var controller = context.GetRouteData().Values["controller"]?.ToString(); 36 | var action = context.GetRouteData().Values["action"]?.ToString(); 37 | if (!string.IsNullOrEmpty(controller) && !string.IsNullOrEmpty(action)) 38 | return; 39 | 40 | var pathBase = context.Request.PathBase; 41 | var path = context.Request.Path; 42 | if (!pathBase.HasValue || !pathBase.Value.Contains(_apiOption.ApiRoutePrefix, StringComparison.OrdinalIgnoreCase) || !path.HasValue) 43 | { 44 | return; 45 | } 46 | 47 | try 48 | { 49 | var handler = HandleResponseFactory.CreateHandleResponse(context); 50 | var gb2312 = Encoding.GetEncoding("GB2312"); 51 | if (_apiOption.AuthorizeType != AuthorizeType.None 52 | && !(await _authService.AuthorizeAsync(context.User, handler, MicroApiOperations.GetOperation(context.Request.Method))).Succeeded) 53 | { 54 | context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; 55 | await context.Response.WriteAsync(HandleResponseContent(new { success = false, error = "Unauthorized" }), gb2312); 56 | } 57 | else 58 | { 59 | var result = handler.Execute(); 60 | var response = new { success = true, data = result }; 61 | context.Response.StatusCode = (int)HttpStatusCode.OK; 62 | await context.Response.WriteAsync(HandleResponseContent(response), gb2312); 63 | } 64 | } 65 | catch (Exception e) 66 | { 67 | Console.WriteLine(e); 68 | var response = new 69 | { 70 | success = false, 71 | error = e 72 | }; 73 | context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; 74 | await context.Response.WriteAsync(HandleResponseContent(response), Encoding.GetEncoding("GB2312")); 75 | } 76 | 77 | } 78 | 79 | private string HandleResponseContent(object content) 80 | { 81 | return JsonConvert.SerializeObject(content, Formatting.Indented, _apiOption.JsonSerializerSettings); 82 | } 83 | } 84 | 85 | public static class AutoApiMiddlewareExtensions 86 | { 87 | public static IServiceCollection AddMicroApi(this IServiceCollection services, Action option = null) 88 | { 89 | if (services == null) throw new ArgumentNullException(nameof(services)); 90 | var defaultOption = new MicroApiOption(); 91 | if (option != null) 92 | option.Invoke(defaultOption); 93 | if (defaultOption.ApiRoutePrefix.IsNullOrWhiteSpace()) 94 | throw new Exception("The option MicroApiOption.ApiRoutePrefix can not be null or empty."); 95 | services.AddSingleton(defaultOption); 96 | services.AddSingleton(); 97 | 98 | services.AddScoped(); 99 | services.AddScoped(); 100 | services.AddScoped(); 101 | services.AddScoped(); 102 | 103 | return services; 104 | } 105 | 106 | public static IApplicationBuilder UseMicroApi(this IApplicationBuilder app) 107 | { 108 | var option = (IOptions)app.ApplicationServices.GetService(typeof(IOptions)); 109 | var pathMatch = $"/{option?.Value?.ApiRoutePrefix}/" + "{controller}"; 110 | app.Map(pathMatch, config => 111 | { 112 | config.UseMiddleware(); 113 | }); 114 | return app; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /MicroApi.Core/MicroApiOperations.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization.Infrastructure; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MicroApi.Core 9 | { 10 | public static class MicroApiOperations 11 | { 12 | public static OperationAuthorizationRequirement GetOperation(string method) 13 | { 14 | return new OperationAuthorizationRequirement { Name = method }; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MicroApi.Core/MicroApiOption.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace MicroApi.Core 4 | { 5 | public class MicroApiOption 6 | { 7 | public AuthorizeType AuthorizeType { get; set; } = AuthorizeType.None; 8 | 9 | public JsonSerializerSettings JsonSerializerSettings { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MicroApi.Core/ObjectExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Data; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Linq.Expressions; 9 | using System.Reflection; 10 | using System.Text; 11 | #if NETSTANDARD2_0_OR_GREATER 12 | using System.Runtime.Serialization.Formatters.Binary; 13 | #endif 14 | #if NET5_0_OR_GREATER 15 | using System.Text.Json.Serialization; 16 | using System.Text.Json; 17 | #endif 18 | using System.Text.RegularExpressions; 19 | using System.Threading.Tasks; 20 | using System.Web; 21 | using System.Xml; 22 | using System.Xml.Linq; 23 | 24 | namespace MicroApi.Core 25 | { 26 | public static class ObjectExtension 27 | { 28 | public static bool HasProperty(this T obj, string propertyName) 29 | { 30 | return obj != null && obj.GetType().GetProperties().Any(p => p.Name.Equals(propertyName)); 31 | } 32 | /// 33 | /// 取得对象指定属性的值 34 | /// 35 | /// 要取值的属性 36 | /// 37 | public static object GetPropertyValue(this T obj, Expression> predicate) 38 | { 39 | var propertyName = predicate.GetPropertyName(); //属性名称 40 | 41 | return obj.GetPropertyValue(propertyName); 42 | } 43 | 44 | /// 45 | /// 取对象属性值 46 | /// 47 | /// 48 | /// 支持“.”分隔的多级属性取值。 49 | /// 50 | public static object GetPropertyValue(this T obj, string propertyName) 51 | { 52 | var strs = propertyName.Split('.'); 53 | 54 | PropertyInfo property = null; 55 | object value = obj; 56 | 57 | for (var i = 0; i < strs.Length; i++) 58 | { 59 | property = value.GetType().GetProperty(strs[i]); 60 | value = property.GetValue(value, null); 61 | } 62 | return value; 63 | } 64 | 65 | /// 66 | /// 设置对象指定属性的值 67 | /// 68 | /// 要设置值的属性 69 | /// 设置值 70 | /// 是否设置成功 71 | public static bool SetPropertyValue(this T obj, Expression> predicate, 72 | object value) 73 | { 74 | var propertyName = predicate.GetPropertyName(); //属性名称 75 | 76 | return obj.SetPropertyValue(propertyName, value); 77 | } 78 | 79 | /// 80 | /// 设置对象属性值 81 | /// 82 | /// 83 | /// propertyName1.propertyName2.propertyName3 84 | /// 85 | /// 86 | public static bool SetPropertyValue(this T obj, string propertyName, object value) 87 | { 88 | var strs = propertyName.Split('.'); 89 | 90 | PropertyInfo property = null; 91 | object target = obj; 92 | 93 | for (var i = 0; i < strs.Length; i++) 94 | { 95 | property = target.GetType().GetProperty(strs[i]); 96 | if (i < strs.Length - 1) 97 | target = property.GetValue(target, null); 98 | } 99 | 100 | var flag = false; //设置成功标记 101 | if (property != null && property.CanWrite) 102 | { 103 | if (false == property.PropertyType.IsGenericType) //非泛型 104 | { 105 | if (property.PropertyType.IsEnum) 106 | { 107 | property.SetValue(target, Convert.ChangeType(value, typeof(int))); 108 | flag = true; 109 | } 110 | else if (value.ToString() != property.PropertyType.ToString()) 111 | { 112 | //property.SetValue(target, string.IsNullOrEmpty(value) ? null : Convert.ChangeType(value, property.PropertyType), null); 113 | property.SetValue(target, 114 | value == null ? null : Convert.ChangeType(value, property.PropertyType), 115 | null); 116 | flag = true; 117 | } 118 | } 119 | else //泛型Nullable<> 120 | { 121 | var genericTypeDefinition = property.PropertyType.GetGenericTypeDefinition(); 122 | if (genericTypeDefinition == typeof(Nullable<>)) 123 | { 124 | //property.SetValue(target, string.IsNullOrEmpty(value) ? null : Convert.ChangeType(value, Nullable.GetUnderlyingType(property.PropertyType)), null); 125 | property.SetValue(target, 126 | value == null 127 | ? null 128 | : Convert.ChangeType(value, Nullable.GetUnderlyingType(property.PropertyType)), 129 | null); 130 | flag = true; 131 | } 132 | } 133 | } 134 | 135 | return flag; 136 | } 137 | 138 | /// 139 | /// 将集合转换为数据集。 140 | /// 141 | /// 转换的元素类型。 142 | /// 集合。 143 | /// 是否生成泛型数据集。 144 | /// 数据集。 145 | public static DataSet ToDataSet(this IEnumerable list, bool generic = true) 146 | { 147 | return ListToDataSet(list, generic); 148 | } 149 | /// 150 | /// DataTable转List 151 | /// 152 | /// 153 | /// 154 | /// 155 | public static IList ToList(this DataTable dt) where T : class, new() 156 | { 157 | // 定义集合 158 | IList ts = new List(); 159 | 160 | if (dt == null || dt.Rows.Count == 0) return ts; 161 | 162 | string tempName = ""; 163 | 164 | foreach (DataRow dr in dt.Rows) 165 | { 166 | T t = new T(); 167 | // 获得此模型的公共属性 168 | PropertyInfo[] propertys = t.GetType().GetProperties(); 169 | foreach (PropertyInfo pi in propertys) 170 | { 171 | tempName = pi.Name; // 检查DataTable是否包含此列 172 | 173 | if (!dt.Columns.Contains(tempName)) 174 | { 175 | tempName = pi.GetCustomAttributes().FirstOrDefault()?.Name; 176 | } 177 | 178 | if (tempName != null && dt.Columns.Contains(tempName)) 179 | { 180 | // 判断此属性是否有Setter 181 | if (!pi.CanWrite) continue; 182 | 183 | object value = dr[tempName]; 184 | if (value != DBNull.Value) 185 | { 186 | pi.SetValue(t, ChangeType(value, pi.PropertyType), null); 187 | } 188 | } 189 | } 190 | ts.Add(t); 191 | } 192 | return ts; 193 | } 194 | 195 | /// 196 | /// 类型转换(包含Nullable<>和非Nullable<>转换) 197 | /// 198 | /// 199 | /// 200 | /// 201 | private static object ChangeType(object value, Type conversionType) 202 | { 203 | // Note: This if block was taken from Convert.ChangeType as is, and is needed here since we're 204 | // checking properties on conversionType below. 205 | if (conversionType == null) 206 | { 207 | throw new ArgumentNullException("conversionType"); 208 | } // end if 209 | 210 | // If it's not a nullable type, just pass through the parameters to Convert.ChangeType 211 | 212 | if (conversionType.IsGenericType && 213 | conversionType.IsNullable()) 214 | { 215 | if (value == null) 216 | { 217 | return null; 218 | } // end if 219 | 220 | // It's a nullable type, and not null, so that means it can be converted to its underlying type, 221 | // so overwrite the passed-in conversion type with this underlying type 222 | System.ComponentModel.NullableConverter nullableConverter = new System.ComponentModel.NullableConverter(conversionType); 223 | 224 | conversionType = nullableConverter.UnderlyingType; 225 | } // end if 226 | 227 | // Now that we've guaranteed conversionType is something Convert.ChangeType can handle (i.e. not a 228 | // nullable type), pass the call on to Convert.ChangeType 229 | return Convert.ChangeType(value, conversionType); 230 | } 231 | /// 232 | /// 将集合转换为数据集。 233 | /// 234 | /// 集合。 235 | /// 是否生成泛型数据集。 236 | /// 数据集。 237 | public static DataSet ToDataSet(this IEnumerable list, bool generic = true) 238 | { 239 | return ListToDataSet(list, generic); 240 | } 241 | 242 | /// 243 | /// 将集合转换为数据集。 244 | /// 245 | /// 转换的元素类型。 246 | /// 集合。 247 | /// 是否生成泛型数据集。 248 | /// 数据集。 249 | public static DataSet ToDataSet(this IEnumerable list, bool generic = true) 250 | { 251 | return ListToDataSet(list, typeof(T), generic); 252 | } 253 | 254 | /// 255 | /// 将实例转换为集合数据集。 256 | /// 257 | /// 实例类型。 258 | /// 实例。 259 | /// 是否生成泛型数据集。 260 | /// 数据集。 261 | public static DataSet ToListSet(this T o, bool generic = true) 262 | { 263 | if (o is IEnumerable) 264 | { 265 | return ListToDataSet(o as IEnumerable, generic); 266 | } 267 | else 268 | { 269 | return ListToDataSet(new T[] { o }, generic); 270 | } 271 | } 272 | 273 | /// 274 | /// 将可序列化实例转换为XmlDocument。 275 | /// 276 | /// 实例类型。 277 | /// 实例。 278 | /// XmlDocument。 279 | public static XmlDocument ToXmlDocument(this T o) 280 | { 281 | XmlDocument xmlDocument = new XmlDocument(); 282 | xmlDocument.InnerXml = o.ToListSet().GetXml(); 283 | return xmlDocument; 284 | } 285 | 286 | /// 287 | /// 将集合转换为数据集。 288 | /// 289 | /// 集合。 290 | /// 转换的元素类型。 291 | /// 是否生成泛型数据集。 292 | /// 转换后的数据集。 293 | private static DataSet ListToDataSet(IEnumerable list, Type t, bool generic) 294 | { 295 | DataSet ds = new DataSet("Data"); 296 | if (t == null) 297 | { 298 | if (list != null) 299 | { 300 | foreach (var i in list) 301 | { 302 | if (i == null) 303 | { 304 | continue; 305 | } 306 | t = i.GetType(); 307 | break; 308 | } 309 | } 310 | if (t == null) 311 | { 312 | return ds; 313 | } 314 | } 315 | ds.Tables.Add(t.Name); 316 | //如果集合中元素为DataSet扩展涉及到的基本类型时,进行特殊转换。 317 | if (t.IsValueType || t == typeof(string)) 318 | { 319 | ds.Tables[0].TableName = "Info"; 320 | ds.Tables[0].Columns.Add(t.Name); 321 | if (list != null) 322 | { 323 | foreach (var i in list) 324 | { 325 | DataRow addRow = ds.Tables[0].NewRow(); 326 | addRow[t.Name] = i; 327 | ds.Tables[0].Rows.Add(addRow); 328 | } 329 | } 330 | return ds; 331 | } 332 | //处理模型的字段和属性。 333 | var fields = t.GetFields(); 334 | var properties = t.GetProperties(); 335 | foreach (var j in fields) 336 | { 337 | if (!ds.Tables[0].Columns.Contains(j.Name)) 338 | { 339 | if (generic) 340 | { 341 | ds.Tables[0].Columns.Add(j.Name, j.FieldType); 342 | } 343 | else 344 | { 345 | ds.Tables[0].Columns.Add(j.Name); 346 | } 347 | } 348 | } 349 | foreach (var j in properties) 350 | { 351 | if (!ds.Tables[0].Columns.Contains(j.Name)) 352 | { 353 | if (generic) 354 | { 355 | ds.Tables[0].Columns.Add(j.Name, j.PropertyType); 356 | } 357 | else 358 | { 359 | ds.Tables[0].Columns.Add(j.Name); 360 | } 361 | } 362 | } 363 | if (list == null) 364 | { 365 | return ds; 366 | } 367 | //读取list中元素的值。 368 | foreach (var i in list) 369 | { 370 | if (i == null) 371 | { 372 | continue; 373 | } 374 | DataRow addRow = ds.Tables[0].NewRow(); 375 | foreach (var j in fields) 376 | { 377 | MemberExpression field = Expression.Field(Expression.Constant(i), j.Name); 378 | LambdaExpression lambda = Expression.Lambda(field, new ParameterExpression[] { }); 379 | Delegate func = lambda.Compile(); 380 | object value = func.DynamicInvoke(); 381 | addRow[j.Name] = value; 382 | } 383 | foreach (var j in properties) 384 | { 385 | MemberExpression property = Expression.Property(Expression.Constant(i), j); 386 | LambdaExpression lambda = Expression.Lambda(property, new ParameterExpression[] { }); 387 | Delegate func = lambda.Compile(); 388 | object value = func.DynamicInvoke(); 389 | addRow[j.Name] = value; 390 | } 391 | ds.Tables[0].Rows.Add(addRow); 392 | } 393 | return ds; 394 | } 395 | 396 | /// 397 | /// 将集合转换为数据集。 398 | /// 399 | /// 转换的元素类型。 400 | /// 集合。 401 | /// 是否生成泛型数据集。 402 | /// 数据集。 403 | private static DataSet ListToDataSet(IEnumerable list, bool generic) 404 | { 405 | return ListToDataSet(list, typeof(T), generic); 406 | } 407 | 408 | /// 409 | /// 将集合转换为数据集。 410 | /// 411 | /// 集合。 412 | /// 是否转换为字符串形式。 413 | /// 转换后的数据集。 414 | private static DataSet ListToDataSet(IEnumerable list, bool generic) 415 | { 416 | return ListToDataSet(list, null, generic); 417 | } 418 | 419 | /// 420 | /// 获取DataSet第一表,第一行,第一列的值。 421 | /// 422 | /// DataSet数据集。 423 | /// 值。 424 | public static object GetData(this DataSet ds) 425 | { 426 | if ( 427 | ds == null 428 | || ds.Tables.Count == 0 429 | ) 430 | { 431 | return string.Empty; 432 | } 433 | else 434 | { 435 | return ds.Tables[0].GetData(); 436 | } 437 | } 438 | 439 | /// 440 | /// 获取DataTable第一行,第一列的值。 441 | /// 442 | /// DataTable数据集表。 443 | /// 值。 444 | public static object GetData(this DataTable dt) 445 | { 446 | if ( 447 | dt.Columns.Count == 0 448 | || dt.Rows.Count == 0 449 | ) 450 | { 451 | return string.Empty; 452 | } 453 | else 454 | { 455 | return dt.Rows[0][0]; 456 | } 457 | } 458 | 459 | /// 460 | /// 获取DataSet第一个匹配columnName的值。 461 | /// 462 | /// 数据集。 463 | /// 列名。 464 | /// 值。 465 | public static object GetData(this DataSet ds, string columnName) 466 | { 467 | if ( 468 | ds == null 469 | || ds.Tables.Count == 0 470 | ) 471 | { 472 | return string.Empty; 473 | } 474 | foreach (DataTable dt in ds.Tables) 475 | { 476 | object o = dt.GetData(columnName); 477 | if (!string.IsNullOrEmpty(o.ToString())) 478 | { 479 | return o; 480 | } 481 | } 482 | return string.Empty; 483 | } 484 | 485 | /// 486 | /// 获取DataTable第一个匹配columnName的值。 487 | /// 488 | /// 数据表。 489 | /// 列名。 490 | /// 值。 491 | public static object GetData(this DataTable dt, string columnName) 492 | { 493 | if (string.IsNullOrEmpty(columnName)) 494 | { 495 | return dt.GetData(); 496 | } 497 | if ( 498 | dt.Columns.Count == 0 499 | || dt.Columns.IndexOf(columnName) == -1 500 | || dt.Rows.Count == 0 501 | ) 502 | { 503 | return string.Empty; 504 | } 505 | return dt.Rows[0][columnName]; 506 | } 507 | 508 | /// 509 | /// 将object转换为string类型信息。 510 | /// 511 | /// object。 512 | /// 默认值。 513 | /// string。 514 | public static string ToString(this object o, string t) 515 | { 516 | string info = string.Empty; 517 | if (o == null) 518 | { 519 | info = t; 520 | } 521 | else 522 | { 523 | info = o.ToString(); 524 | } 525 | return info; 526 | } 527 | 528 | /// 529 | /// 将DateTime?转换为string类型信息。 530 | /// 531 | /// DateTime?。 532 | /// 标准或自定义日期和时间格式的字符串。 533 | /// 默认值。 534 | /// string。 535 | public static string ToString(this DateTime? o, string format, string t) 536 | { 537 | string info = string.Empty; 538 | if (o == null) 539 | { 540 | info = t; 541 | } 542 | else 543 | { 544 | info = o.Value.ToString(format); 545 | } 546 | return info; 547 | } 548 | 549 | /// 550 | /// 将TimeSpan?转换为string类型信息。 551 | /// 552 | /// TimeSpan?。 553 | /// 标准或自定义时间格式的字符串。 554 | /// 默认值。 555 | /// string。 556 | public static string ToString(this TimeSpan? o, string format, string t) 557 | { 558 | string info = string.Empty; 559 | if (o == null) 560 | { 561 | info = t; 562 | } 563 | else 564 | { 565 | info = o.Value.ToString(format); 566 | } 567 | return info; 568 | } 569 | 570 | /// 571 | /// 将object转换为截取后的string类型信息。 572 | /// 573 | /// object。 574 | /// 此实例中子字符串的起始字符位置(从零开始)。 575 | /// 子字符串中的字符数。 576 | /// 后缀。如果没有截取则不添加。 577 | /// 截取后的string类型信息。 578 | public static string ToSubString(this object o, int startIndex, int length, string suffix = null) 579 | { 580 | string inputString = o.ToString(string.Empty); 581 | startIndex = Math.Max(startIndex, 0); 582 | startIndex = Math.Min(startIndex, inputString.Length - 1); 583 | length = Math.Max(length, 1); 584 | if (startIndex + length > inputString.Length) 585 | { 586 | length = inputString.Length - startIndex; 587 | } 588 | if (inputString.Length == startIndex + length) 589 | { 590 | return inputString; 591 | } 592 | else 593 | { 594 | return inputString.Substring(startIndex, length) + suffix; 595 | } 596 | } 597 | 598 | /// 599 | /// 将object转换为byte类型信息。 600 | /// 601 | /// object。 602 | /// 默认值。 603 | /// byte。 604 | public static byte ToByte(this object o, byte t = default) 605 | { 606 | byte info; 607 | if (!byte.TryParse(o.ToString(string.Empty), out info)) 608 | { 609 | info = t; 610 | } 611 | return info; 612 | } 613 | 614 | public static byte[] ToBytes(this object obj) 615 | { 616 | if (obj == null) 617 | return null; 618 | #if NETSTANDARD2_0_OR_GREATER 619 | var bf = new BinaryFormatter(); 620 | using (var ms = new MemoryStream()) 621 | { 622 | bf.Serialize(ms, obj); 623 | return ms.ToArray(); 624 | } 625 | #endif 626 | #if NET5_0_OR_GREATER 627 | return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(obj, GetJsonSerializerOptions())); 628 | #endif 629 | } 630 | 631 | public static object ToObject(this byte[] source) 632 | { 633 | if (source == null || !source.Any()) 634 | return default; 635 | #if NETSTANDARD2_0_OR_GREATER 636 | using (var memStream = new MemoryStream()) 637 | { 638 | var bf = new BinaryFormatter(); 639 | memStream.Write(source, 0, source.Length); 640 | memStream.Seek(0, SeekOrigin.Begin); 641 | var obj = bf.Deserialize(memStream); 642 | return obj; 643 | } 644 | #endif 645 | #if NET5_0_OR_GREATER 646 | return JsonSerializer.Deserialize(source, GetJsonSerializerOptions()); 647 | #endif 648 | } 649 | 650 | #if NET5_0_OR_GREATER 651 | private static JsonSerializerOptions GetJsonSerializerOptions() 652 | { 653 | return new JsonSerializerOptions() 654 | { 655 | PropertyNamingPolicy = null, 656 | WriteIndented = true, 657 | AllowTrailingCommas = true, 658 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, 659 | }; 660 | } 661 | #endif 662 | /// 663 | /// 将object转换为char类型信息。 664 | /// 665 | /// object。 666 | /// 默认值。 667 | /// char。 668 | public static char ToChar(this object o, char t = default) 669 | { 670 | char info; 671 | if (!char.TryParse(o.ToString(string.Empty), out info)) 672 | { 673 | info = t; 674 | } 675 | return info; 676 | } 677 | 678 | /// 679 | /// 将object转换为int类型信息。 680 | /// 681 | /// object。 682 | /// 默认值。 683 | /// int。 684 | public static int ToInt(this object o, int t = default) 685 | { 686 | int info; 687 | if (!int.TryParse(o.ToString(string.Empty), out info)) 688 | { 689 | info = t; 690 | } 691 | return info; 692 | } 693 | 694 | /// 695 | /// 将object转换为double类型信息。 696 | /// 697 | /// object。 698 | /// 默认值。 699 | /// double。 700 | public static double ToDouble(this object o, double t = default) 701 | { 702 | double info; 703 | if (!double.TryParse(o.ToString(string.Empty), out info)) 704 | { 705 | info = t; 706 | } 707 | return info; 708 | } 709 | 710 | /// 711 | /// 将object转换为decimal类型信息。 712 | /// 713 | /// object。 714 | /// 默认值。 715 | /// decimal。 716 | public static decimal ToDecimal(this object o, decimal t = default) 717 | { 718 | decimal info; 719 | if (!decimal.TryParse(o.ToString(string.Empty), out info)) 720 | { 721 | info = t; 722 | } 723 | return info; 724 | } 725 | 726 | /// 727 | /// 将object转换为float类型信息。 728 | /// 729 | /// object。 730 | /// 默认值。 731 | /// float。 732 | public static float ToFloat(this object o, float t = default) 733 | { 734 | float info; 735 | if (!float.TryParse(o.ToString(string.Empty), out info)) 736 | { 737 | info = t; 738 | } 739 | return info; 740 | } 741 | 742 | /// 743 | /// 将object转换为long类型信息。 744 | /// 745 | /// object。 746 | /// 默认值。 747 | /// long。 748 | public static long ToLong(this object o, long t = default) 749 | { 750 | long info; 751 | if (!long.TryParse(o.ToString(string.Empty), out info)) 752 | { 753 | info = t; 754 | } 755 | return info; 756 | } 757 | 758 | /// 759 | /// 将object转换为bool类型信息。 760 | /// 761 | /// object。 762 | /// 默认值。 763 | /// bool。 764 | public static bool ToBool(this object o, bool t = default) 765 | { 766 | bool info; 767 | if (!bool.TryParse(o.ToString(string.Empty), out info)) 768 | { 769 | info = t; 770 | } 771 | return info; 772 | } 773 | 774 | /// 775 | /// 将object转换为sbyte类型信息。 776 | /// 777 | /// object。 778 | /// 默认值。 779 | /// sbyte。 780 | public static sbyte ToSbyte(this object o, sbyte t = default) 781 | { 782 | sbyte info; 783 | if (!sbyte.TryParse(o.ToString(string.Empty), out info)) 784 | { 785 | info = t; 786 | } 787 | return info; 788 | } 789 | 790 | /// 791 | /// 将object转换为short类型信息。 792 | /// 793 | /// object。 794 | /// 默认值。 795 | /// short。 796 | public static short ToShort(this object o, short t = default) 797 | { 798 | short info; 799 | if (!short.TryParse(o.ToString(string.Empty), out info)) 800 | { 801 | info = t; 802 | } 803 | return info; 804 | } 805 | 806 | /// 807 | /// 将object转换为ushort类型信息。 808 | /// 809 | /// object。 810 | /// 默认值。 811 | /// ushort。 812 | public static ushort ToUShort(this object o, ushort t = default) 813 | { 814 | ushort info; 815 | if (!ushort.TryParse(o.ToString(string.Empty), out info)) 816 | { 817 | info = t; 818 | } 819 | return info; 820 | } 821 | 822 | /// 823 | /// 将object转换为ulong类型信息。 824 | /// 825 | /// object。 826 | /// 默认值。 827 | /// ulong。 828 | public static ulong ToULong(this object o, ulong t = default) 829 | { 830 | ulong info; 831 | if (!ulong.TryParse(o.ToString(string.Empty), out info)) 832 | { 833 | info = t; 834 | } 835 | return info; 836 | } 837 | 838 | /// 839 | /// 将object转换为Enum[T]类型信息。 840 | /// 841 | /// object。 842 | /// 默认值。 843 | /// Enum[T]。 844 | public static T ToEnum(this object o, T t = default) 845 | where T : struct 846 | { 847 | T info; 848 | if (!Enum.TryParse(o.ToString(string.Empty), out info)) 849 | { 850 | info = t; 851 | } 852 | return info; 853 | } 854 | 855 | /// 856 | /// 将object转换为DateTime类型信息。 857 | /// 858 | /// object。 859 | /// 默认值。 860 | /// DateTime。 861 | public static DateTime ToDateTime(this object o, DateTime t = default) 862 | { 863 | if (t == default) 864 | { 865 | t = new DateTime(1753, 1, 1); 866 | } 867 | DateTime info; 868 | if (!DateTime.TryParse(o.ToString(string.Empty), out info)) 869 | { 870 | info = t; 871 | } 872 | return info; 873 | } 874 | 875 | /// 876 | /// 将object转换为TimeSpan类型信息。 877 | /// 878 | /// object。 879 | /// 默认值。 880 | /// TimeSpan。 881 | public static TimeSpan ToTimeSpan(this object o, TimeSpan t = default) 882 | { 883 | if (t == default) 884 | { 885 | t = new TimeSpan(0, 0, 0); 886 | } 887 | TimeSpan info; 888 | if (!TimeSpan.TryParse(o.ToString(string.Empty), out info)) 889 | { 890 | info = t; 891 | } 892 | return info; 893 | } 894 | 895 | /// 896 | /// 将object转换为Guid类型信息。 897 | /// 898 | /// object。 899 | /// 默认值。 900 | /// Guid。 901 | public static Guid ToGuid(this object o, Guid t = default) 902 | { 903 | Guid info; 904 | if (!Guid.TryParse(o.ToString(string.Empty), out info)) 905 | { 906 | info = t; 907 | } 908 | return info; 909 | } 910 | 911 | private static Regex BoolRegex = new Regex("(?(true|false))", RegexOptions.IgnoreCase | RegexOptions.Singleline); 912 | 913 | /// 914 | /// 从object中获取bool类型信息。 915 | /// 916 | /// object。 917 | /// bool。 918 | public static bool? GetBool(this object o) 919 | { 920 | bool info; 921 | if (!bool.TryParse(BoolRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 922 | { 923 | return null; 924 | } 925 | return info; 926 | } 927 | 928 | private static Regex IntRegex = new Regex("(?-?\\d+)", RegexOptions.IgnoreCase | RegexOptions.Singleline); 929 | 930 | /// 931 | /// 从object中获取int类型信息。 932 | /// 933 | /// object。 934 | /// int。 935 | public static int? GetInt(this object o) 936 | { 937 | int info; 938 | if (!int.TryParse(IntRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 939 | { 940 | return null; 941 | } 942 | return info; 943 | } 944 | 945 | private static Regex DecimalRegex = new Regex("(?-?\\d+(\\.\\d+)?)", RegexOptions.IgnoreCase | RegexOptions.Singleline); 946 | 947 | /// 948 | /// 从object中获取decimal类型信息。 949 | /// 950 | /// object。 951 | /// decimal。 952 | public static decimal? GetDecimal(this object o) 953 | { 954 | decimal info; 955 | if (!decimal.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 956 | { 957 | return null; 958 | } 959 | return info; 960 | } 961 | 962 | /// 963 | /// 从object中获取double类型信息。 964 | /// 965 | /// object。 966 | /// double。 967 | public static double? GetDouble(this object o) 968 | { 969 | double info; 970 | if (!double.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 971 | { 972 | return null; 973 | } 974 | return info; 975 | } 976 | 977 | /// 978 | /// 从object中获取正数信息。 979 | /// 980 | /// object。 981 | /// decimal。 982 | public static decimal? GetPositiveNumber(this object o) 983 | { 984 | decimal info; 985 | if (!decimal.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 986 | { 987 | return null; 988 | } 989 | return Math.Abs(info); 990 | } 991 | 992 | private static Regex DateTimeRegex = new Regex("(?(((\\d+)[/年-](0?[13578]|1[02])[/月-](3[01]|[12]\\d|0?\\d)[日]?)|((\\d+)[/年-](0?[469]|11)[/月-](30|[12]\\d|0?\\d)[日]?)|((\\d+)[/年-]0?2[/月-](2[0-8]|1\\d|0?\\d)[日]?))(\\s((2[0-3]|[0-1]\\d)):[0-5]\\d:[0-5]\\d)?)", RegexOptions.IgnoreCase | RegexOptions.Singleline); 993 | 994 | /// 995 | /// 从object中获取DateTime?类型信息。 996 | /// 997 | /// object。 998 | /// DateTime?。 999 | public static DateTime? GetDateTime(this object o) 1000 | { 1001 | DateTime info; 1002 | if (!DateTime.TryParse(DateTimeRegex.Match(o.ToString(string.Empty)).Groups["info"].Value.Replace("年", "-").Replace("月", "-").Replace("/", "-").Replace("日", ""), out info)) 1003 | { 1004 | return null; 1005 | } 1006 | return info; 1007 | } 1008 | 1009 | private static Regex TimeSpanRegex = new Regex("(?-?(\\d+\\.(([0-1]\\d)|(2[0-3])):[0-5]\\d:[0-5]\\d)|((([0-1]\\d)|(2[0-3])):[0-5]\\d:[0-5]\\d)|(\\d+))", RegexOptions.IgnoreCase | RegexOptions.Singleline); 1010 | 1011 | /// 1012 | /// 从object中获取TimeSpan?类型信息。 1013 | /// 1014 | /// object。 1015 | /// TimeSpan?。 1016 | public static TimeSpan? GetTimeSpan(this object o) 1017 | { 1018 | TimeSpan info; 1019 | if (!TimeSpan.TryParse(TimeSpanRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 1020 | { 1021 | return null; 1022 | } 1023 | return info; 1024 | } 1025 | 1026 | private static Regex GuidRegex = new Regex("(?\\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\\}{0,1})", RegexOptions.IgnoreCase | RegexOptions.Singleline); 1027 | 1028 | /// 1029 | /// 从object中获取Guid?类型信息。 1030 | /// 1031 | /// object。 1032 | /// Guid?。 1033 | public static Guid? GetGuid(this object o) 1034 | { 1035 | Guid info; 1036 | if (!Guid.TryParse(GuidRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) 1037 | { 1038 | return null; 1039 | } 1040 | return info; 1041 | } 1042 | 1043 | /// 1044 | /// 将object转换为SqlServer中的DateTime?类型信息。 1045 | /// 1046 | /// object。 1047 | /// 默认值。 1048 | /// DateTime?。 1049 | public static DateTime? GetSqlDateTime(this object o, DateTime t = default) 1050 | { 1051 | DateTime info; 1052 | if (!DateTime.TryParse(o.ToString(string.Empty), out info)) 1053 | { 1054 | info = t; 1055 | } 1056 | if (info < new DateTime(1753, 1, 1) || info > new DateTime(9999, 12, 31)) 1057 | { 1058 | return null; 1059 | } 1060 | return info; 1061 | } 1062 | 1063 | /// 1064 | /// 读取XElement节点的文本内容。 1065 | /// 1066 | /// XElement节点。 1067 | /// 默认值。 1068 | /// 文本内容。 1069 | public static string Value(this XElement xElement, string t = default) 1070 | { 1071 | if (xElement == null) 1072 | { 1073 | return t; 1074 | } 1075 | else 1076 | { 1077 | return xElement.Value; 1078 | } 1079 | } 1080 | 1081 | /// 1082 | /// 获取与指定键相关的值。 1083 | /// 1084 | /// 键类型。 1085 | /// 值类型。 1086 | /// 表示键/值对象的泛型集合。 1087 | /// 键。 1088 | /// 默认值。 1089 | /// 值。 1090 | public static TValue GetValue(this IDictionary dictionary, TKey key, TValue t = default) 1091 | { 1092 | TValue value = default; 1093 | if (dictionary == null || key == null) 1094 | { 1095 | return t; 1096 | } 1097 | if (!dictionary.TryGetValue(key, out value)) 1098 | { 1099 | value = t; 1100 | } 1101 | return value; 1102 | } 1103 | 1104 | /// 1105 | /// 获取与指定键相关或者第一个的值。 1106 | /// 1107 | /// 键类型。 1108 | /// 值类型。 1109 | /// 表示键/值对象的泛型集合。 1110 | /// 键。 1111 | /// 默认值。 1112 | /// 值。 1113 | public static TValue GetFirstOrDefaultValue(this IDictionary dictionary, TKey key, TValue t = default) 1114 | { 1115 | TValue value = default; 1116 | if (dictionary == null || key == null) 1117 | { 1118 | return t; 1119 | } 1120 | if (!dictionary.TryGetValue(key, out value)) 1121 | { 1122 | if (dictionary.Count() == 0) 1123 | { 1124 | value = t; 1125 | } 1126 | else 1127 | { 1128 | value = dictionary.FirstOrDefault().Value; 1129 | } 1130 | } 1131 | return value; 1132 | } 1133 | 1134 | /// 1135 | /// 获取具有指定 System.Xml.Linq.XName 的第一个(按文档顺序)子元素。 1136 | /// 1137 | /// XContainer。 1138 | /// 要匹配的 System.Xml.Linq.XName。 1139 | /// 是否返回同名默认值。 1140 | /// 与指定 System.Xml.Linq.XName 匹配的 System.Xml.Linq.XElement,或者为 null。 1141 | public static XElement Element(this XContainer xContainer, XName xName, bool t) 1142 | { 1143 | XElement info; 1144 | if (xContainer == null) 1145 | { 1146 | info = null; 1147 | } 1148 | else 1149 | { 1150 | info = xContainer.Element(xName); 1151 | } 1152 | if (t && info == null) 1153 | { 1154 | info = new XElement(xName); 1155 | } 1156 | return info; 1157 | } 1158 | 1159 | /// 1160 | /// 按文档顺序返回此元素或文档的子元素集合。 1161 | /// 1162 | /// XContainer。 1163 | /// 是否返回非空默认值。 1164 | /// System.Xml.Linq.XElement 的按文档顺序包含此System.Xml.Linq.XContainer 的子元素,或者非空默认值。 1165 | public static IEnumerable Elements(this XContainer xContainer, bool t) 1166 | { 1167 | IEnumerable info; 1168 | if (xContainer == null) 1169 | { 1170 | info = null; 1171 | } 1172 | else 1173 | { 1174 | info = xContainer.Elements(); 1175 | } 1176 | if (t && info == null) 1177 | { 1178 | info = new List(); 1179 | } 1180 | return info; 1181 | } 1182 | 1183 | /// 1184 | /// 按文档顺序返回此元素或文档的经过筛选的子元素集合。集合中只包括具有匹配 System.Xml.Linq.XName 的元素。 1185 | /// 1186 | /// XContainer。 1187 | /// 要匹配的 System.Xml.Linq.XName。 1188 | /// 是否返回非空默认值。 1189 | /// System.Xml.Linq.XElement 的按文档顺序包含具有匹配System.Xml.Linq.XName 的 System.Xml.Linq.XContainer 的子级,或者非空默认值。 1190 | public static IEnumerable Elements(this XContainer xContainer, XName xName, bool t) 1191 | { 1192 | IEnumerable info; 1193 | if (xContainer == null) 1194 | { 1195 | info = null; 1196 | } 1197 | else 1198 | { 1199 | info = xContainer.Elements(xName); 1200 | } 1201 | if (t && info == null) 1202 | { 1203 | info = new List(); 1204 | } 1205 | return info; 1206 | } 1207 | 1208 | /// 1209 | /// 删除html标签。 1210 | /// 1211 | /// 输入的字符串。 1212 | /// 没有html标签的字符串。 1213 | public static string RemoveHTMLTags(this string html) 1214 | { 1215 | return Regex.Replace(Regex.Replace(Regex.Replace((html ?? string.Empty).Replace(" ", " ").Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " "), "<\\/?[^>]+>", "\r\n"), "(\r\n)+", "\r\n"), "(\\s)+", " ").Trim(); 1216 | } 1217 | 1218 | /// 1219 | /// 字符串转换为文件名。 1220 | /// 1221 | /// 字符串。 1222 | /// 文件名。 1223 | public static string ToFileName(this string s) 1224 | { 1225 | return Regex.Replace(s.ToString(string.Empty), @"[\\/:*?<>|]", "_").Replace("\t", " ").Replace("\r\n", " ").Replace("\"", " "); 1226 | } 1227 | 1228 | /// 1229 | /// 获取星期一的日期。 1230 | /// 1231 | /// 日期。 1232 | /// 星期一的日期。 1233 | public static DateTime? GetMonday(this DateTime dateTime) 1234 | { 1235 | return dateTime.AddDays(-1 * (int)dateTime.AddDays(-1).DayOfWeek).ToString("yyyy-MM-dd").GetDateTime(); 1236 | } 1237 | 1238 | /// 1239 | /// 获取默认非空字符串。 1240 | /// 1241 | /// 首选默认非空字符串。 1242 | /// 依次非空字符串可选项。 1243 | /// 默认非空字符串。若无可选项则返回string.Empty。 1244 | public static string DefaultStringIfEmpty(this string s, params string[] args) 1245 | { 1246 | if (string.IsNullOrEmpty(s)) 1247 | { 1248 | foreach (string i in args) 1249 | { 1250 | if (!string.IsNullOrEmpty(i) && !string.IsNullOrEmpty(i.Trim())) 1251 | { 1252 | return i; 1253 | } 1254 | } 1255 | } 1256 | return s ?? string.Empty; 1257 | } 1258 | 1259 | /// 1260 | /// 对 URL 字符串进行编码。 1261 | /// 1262 | /// 要编码的文本。 1263 | /// 匹配要编码的文本。 1264 | /// 指定编码方案的 System.Text.Encoding 对象。 1265 | /// 一个已编码的字符串。 1266 | public static string ToUrlEncodeString(this string s, Regex regex = default, Encoding encoding = null) 1267 | { 1268 | if (encoding == null) 1269 | { 1270 | encoding = Encoding.UTF8; 1271 | } 1272 | if (regex == null) 1273 | { 1274 | return HttpUtility.UrlEncode(s, encoding); 1275 | } 1276 | List l = new List(); 1277 | foreach (char i in s) 1278 | { 1279 | string t = i.ToString(); 1280 | l.Add(regex.IsMatch(t) ? HttpUtility.UrlEncode(t, encoding) : t); 1281 | } 1282 | return string.Join(string.Empty, l); 1283 | } 1284 | 1285 | /// 1286 | /// 对 URL 字符串进行编码。 1287 | /// 1288 | /// 要编码的文本。 1289 | /// 匹配要编码的文本。 1290 | /// 指定编码方案的 System.Text.Encoding 对象。 1291 | /// 一个已编码的字符串。 1292 | public static string ToUrlEncodeString(this string s, string regex, Encoding encoding = null) 1293 | { 1294 | return s.ToUrlEncodeString(new Regex(regex), encoding); 1295 | } 1296 | 1297 | /// 1298 | /// 将日期转换为UNIX时间戳字符串 1299 | /// 1300 | /// 1301 | /// 1302 | public static string ToUnixTimeStamp(this DateTime date) 1303 | { 1304 | DateTime startTime = TimeZoneInfo.ConvertTimeToUtc(new DateTime(1970, 1, 1)); 1305 | string timeStamp = date.Subtract(startTime).Ticks.ToString(); 1306 | return timeStamp.Substring(0, timeStamp.Length - 7); 1307 | } 1308 | 1309 | private static Regex MobileRegex = new Regex("^1[3|4|5|7|8][0-9]\\d{4,8}$"); 1310 | private static Regex EmailRegex = new Regex("^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\\.[a-zA-Z0-9_-]{2,3}){1,2})$"); 1311 | 1312 | /// 1313 | /// 判断当前字符串是否是移动电话号码 1314 | /// 1315 | /// 1316 | /// 1317 | public static bool IsMobile(this string mobile) 1318 | { 1319 | return MobileRegex.IsMatch(mobile); 1320 | } 1321 | 1322 | /// 1323 | /// 判断当前字符串是否为邮箱 1324 | /// 1325 | /// 1326 | /// 1327 | public static bool IsEmail(this string email) 1328 | { 1329 | return EmailRegex.IsMatch(email); 1330 | } 1331 | 1332 | public static bool IsTask(this Type source) 1333 | { 1334 | return source.BaseType == typeof(Task); 1335 | } 1336 | 1337 | public static bool In(this T source, ICollection target) 1338 | { 1339 | if (source == null || target == null) return false; 1340 | return target.Contains(source); 1341 | } 1342 | 1343 | public static bool IsImplement(this Type entityType, Type interfaceType) 1344 | { 1345 | return /*entityType.IsClass && !entityType.IsAbstract &&*/ entityType.GetTypeInfo().GetInterfaces().Any(t => 1346 | t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == interfaceType); 1347 | } 1348 | 1349 | public static bool IsSubClassOf(this Type entityType, Type superType) 1350 | { 1351 | return entityType.GetTypeInfo().IsSubclassOf(superType); 1352 | } 1353 | 1354 | #region 人民币大写转换 1355 | public static string RMBD(this double num) 1356 | { 1357 | return ((decimal)num).RMBD(); 1358 | } 1359 | 1360 | /// 1361 | /// 转换人民币大小金额 1362 | /// 1363 | /// 金额 1364 | /// 返回大写形式 1365 | public static string RMBD(this decimal num) 1366 | { 1367 | string str1 = "零壹贰叁肆伍陆柒捌玖"; //0-9所对应的汉字 1368 | string str2 = "万仟佰拾亿仟佰拾万仟佰拾元角分"; //数字位所对应的汉字 1369 | string str3 = ""; //从原num值中取出的值 1370 | string str4 = ""; //数字的字符串形式 1371 | string str5 = ""; //人民币大写金额形式 1372 | int i; //循环变量 1373 | int j; //num的值乘以100的字符串长度 1374 | string ch1 = ""; //数字的汉语读法 1375 | string ch2 = ""; //数字位的汉字读法 1376 | int nzero = 0; //用来计算连续的零值是几个 1377 | int temp; //从原num值中取出的值 1378 | 1379 | num = Math.Round(Math.Abs(num), 2); //将num取绝对值并四舍五入取2位小数 1380 | str4 = ((long)(num * 100)).ToString(); //将num乘100并转换成字符串形式 1381 | j = str4.Length; //找出最高位 1382 | if (j > 15) { return "溢出"; } 1383 | str2 = str2.Substring(15 - j); //取出对应位数的str2的值。如:200.55,j为5所以str2=佰拾元角分 1384 | 1385 | //循环取出每一位需要转换的值 1386 | for (i = 0; i < j; i++) 1387 | { 1388 | str3 = str4.Substring(i, 1); //取出需转换的某一位的值 1389 | temp = Convert.ToInt32(str3); //转换为数字 1390 | if (i != j - 3 && i != j - 7 && i != j - 11 && i != j - 15) 1391 | { 1392 | //当所取位数不为元、万、亿、万亿上的数字时 1393 | if (str3 == "0") 1394 | { 1395 | ch1 = ""; 1396 | ch2 = ""; 1397 | nzero = nzero + 1; 1398 | } 1399 | else 1400 | { 1401 | if (str3 != "0" && nzero != 0) 1402 | { 1403 | ch1 = "零" + str1.Substring(temp * 1, 1); 1404 | ch2 = str2.Substring(i, 1); 1405 | nzero = 0; 1406 | } 1407 | else 1408 | { 1409 | ch1 = str1.Substring(temp * 1, 1); 1410 | ch2 = str2.Substring(i, 1); 1411 | nzero = 0; 1412 | } 1413 | } 1414 | } 1415 | else 1416 | { 1417 | //该位是万亿,亿,万,元位等关键位 1418 | if (str3 != "0" && nzero != 0) 1419 | { 1420 | ch1 = "零" + str1.Substring(temp * 1, 1); 1421 | ch2 = str2.Substring(i, 1); 1422 | nzero = 0; 1423 | } 1424 | else 1425 | { 1426 | if (str3 != "0" && nzero == 0) 1427 | { 1428 | ch1 = str1.Substring(temp * 1, 1); 1429 | ch2 = str2.Substring(i, 1); 1430 | nzero = 0; 1431 | } 1432 | else 1433 | { 1434 | if (str3 == "0" && nzero >= 3) 1435 | { 1436 | ch1 = ""; 1437 | ch2 = ""; 1438 | nzero = nzero + 1; 1439 | } 1440 | else 1441 | { 1442 | if (j >= 11) 1443 | { 1444 | ch1 = ""; 1445 | nzero = nzero + 1; 1446 | } 1447 | else 1448 | { 1449 | ch1 = ""; 1450 | ch2 = str2.Substring(i, 1); 1451 | nzero = nzero + 1; 1452 | } 1453 | } 1454 | } 1455 | } 1456 | } 1457 | if (i == j - 11 || i == j - 3) 1458 | { 1459 | //如果该位是亿位或元位,则必须写上 1460 | ch2 = str2.Substring(i, 1); 1461 | } 1462 | str5 = str5 + ch1 + ch2; 1463 | 1464 | if (i == j - 1 && str3 == "0") 1465 | { 1466 | //最后一位(分)为0时,加上“整” 1467 | str5 = str5 + '整'; 1468 | } 1469 | } 1470 | if (num == 0) 1471 | { 1472 | str5 = "零元整"; 1473 | } 1474 | return str5; 1475 | } 1476 | #endregion 1477 | 1478 | public static T ToObject(this byte[] source) 1479 | { 1480 | return (T)source.ToObject(); 1481 | } 1482 | 1483 | public static string Join(this IEnumerable source, string separator) 1484 | { 1485 | return string.Join(separator, source); 1486 | } 1487 | } 1488 | } -------------------------------------------------------------------------------- /MicroApi.Core/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace MicroApi.Core 6 | { 7 | public static class StringExtensions 8 | { 9 | public static object ParseTo(this string str, string type) 10 | { 11 | switch (type) 12 | { 13 | case "System.Boolean": 14 | return str.ToBoolean(); 15 | case "System.SByte": 16 | return str.ToSByte(); 17 | case "System.Byte": 18 | return str.ToByte(); 19 | case "System.UInt16": 20 | return str.ToUInt16(); 21 | case "System.Int16": 22 | return str.ToInt16(); 23 | case "System.uInt32": 24 | return str.ToUInt32(); 25 | case "System.Int32": 26 | return str.ToInt32(); 27 | case "System.UInt64": 28 | return str.ToUInt64(); 29 | case "System.Int64": 30 | return str.ToInt64(); 31 | case "System.Single": 32 | return str.ToSingle(); 33 | case "System.Double": 34 | return str.ToDouble(); 35 | case "System.Decimal": 36 | return str.ToDecimal(); 37 | case "System.DateTime": 38 | return str.ToDateTime(); 39 | case "System.Guid": 40 | return str.ToGuid(); 41 | } 42 | throw new NotSupportedException(string.Format("The string of \"{0}\" can not be parsed to {1}", str, type)); 43 | } 44 | 45 | public static sbyte? ToSByte(this string value) 46 | { 47 | sbyte value2; 48 | if (sbyte.TryParse(value, out value2)) 49 | { 50 | return value2; 51 | } 52 | return null; 53 | } 54 | 55 | public static byte? ToByte(this string value) 56 | { 57 | byte value2; 58 | if (byte.TryParse(value, out value2)) 59 | { 60 | return value2; 61 | } 62 | return null; 63 | } 64 | 65 | public static ushort? ToUInt16(this string value) 66 | { 67 | ushort value2; 68 | if (ushort.TryParse(value, out value2)) 69 | { 70 | return value2; 71 | } 72 | return null; 73 | } 74 | 75 | public static short? ToInt16(this string value) 76 | { 77 | short value2; 78 | if (short.TryParse(value, out value2)) 79 | { 80 | return value2; 81 | } 82 | return null; 83 | } 84 | 85 | public static uint? ToUInt32(this string value) 86 | { 87 | uint value2; 88 | if (uint.TryParse(value, out value2)) 89 | { 90 | return value2; 91 | } 92 | return null; 93 | } 94 | 95 | public static ulong? ToUInt64(this string value) 96 | { 97 | ulong value2; 98 | if (ulong.TryParse(value, out value2)) 99 | { 100 | return value2; 101 | } 102 | return null; 103 | } 104 | 105 | public static long? ToInt64(this string value) 106 | { 107 | long value2; 108 | if (long.TryParse(value, out value2)) 109 | { 110 | return value2; 111 | } 112 | return null; 113 | } 114 | 115 | public static float? ToSingle(this string value) 116 | { 117 | float value2; 118 | if (float.TryParse(value, out value2)) 119 | { 120 | return value2; 121 | } 122 | return null; 123 | } 124 | 125 | public static double? ToDouble(this string value) 126 | { 127 | double value2; 128 | if (double.TryParse(value, out value2)) 129 | { 130 | return value2; 131 | } 132 | return null; 133 | } 134 | 135 | public static decimal? ToDecimal(this string value) 136 | { 137 | decimal value2; 138 | if (decimal.TryParse(value, out value2)) 139 | { 140 | return value2; 141 | } 142 | return null; 143 | } 144 | 145 | public static bool? ToBoolean(this string value) 146 | { 147 | bool value2; 148 | if (bool.TryParse(value, out value2)) 149 | { 150 | return value2; 151 | } 152 | return null; 153 | } 154 | 155 | public static T? ToEnum(this string str) where T : struct 156 | { 157 | T t; 158 | if (Enum.TryParse(str, true, out t) && Enum.IsDefined(typeof(T), t)) 159 | { 160 | return t; 161 | } 162 | return null; 163 | } 164 | 165 | public static Guid? ToGuid(this string str) 166 | { 167 | Guid value; 168 | if (Guid.TryParse(str, out value)) 169 | { 170 | return value; 171 | } 172 | return null; 173 | } 174 | 175 | public static DateTime? ToDateTime(this string value) 176 | { 177 | DateTime value2; 178 | if (DateTime.TryParse(value, out value2)) 179 | { 180 | return value2; 181 | } 182 | return null; 183 | } 184 | 185 | public static int? ToInt32(this string input) 186 | { 187 | if (string.IsNullOrEmpty(input)) 188 | { 189 | return null; 190 | } 191 | int value; 192 | if (int.TryParse(input, out value)) 193 | { 194 | return value; 195 | } 196 | return null; 197 | } 198 | 199 | /// 200 | /// 替换空格字符 201 | /// 202 | /// 203 | /// 替换为该字符 204 | /// 替换后的字符串 205 | public static string ReplaceWhitespace(this string input, string replacement = "") 206 | { 207 | return string.IsNullOrEmpty(input) ? null : Regex.Replace(input, "\\s", replacement, RegexOptions.Compiled); 208 | } 209 | 210 | /// 211 | /// 返回一个值,该值指示指定的 String 对象是否出现在此字符串中。 212 | /// 213 | /// 214 | /// 要搜寻的字符串。 215 | /// 指定搜索规则的枚举值之一。 216 | /// 如果 value 参数出现在此字符串中则为 true;否则为 false。 217 | public static bool Contains(this string source, string value, 218 | StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) 219 | { 220 | return source.IndexOf(value, comparisonType) >= 0; 221 | } 222 | 223 | /// 224 | /// 清除 Html 代码,并返回指定长度的文本。(连续空行或空格会被替换为一个) 225 | /// 226 | /// 227 | /// 返回的文本长度(为0返回所有文本) 228 | /// 229 | public static string StripHtml(this string text, int maxLength = 0) 230 | { 231 | if (string.IsNullOrEmpty(text)) return string.Empty; 232 | text = text.Trim(); 233 | 234 | text = Regex.Replace(text, "[\\r\\n]{2,}", "<&rn>"); //替换回车和换行为<&rn>,防止下一行代码替换空格的时候被替换掉 235 | text = Regex.Replace(text, "[\\s]{2,}", " "); //替换 2 个以上的空格为 1 个 236 | text = Regex.Replace(text, "(<&rn>)+", "\n"); //还原 <&rn> 为 \n 237 | text = Regex.Replace(text, "(\\s*&[n|N][b|B][s|S][p|P];\\s*)+", " "); //  238 | 239 | text = Regex.Replace(text, "(<[b|B][r|R]/*>)+|(<[p|P](.|\\n)*?>)", "\n"); //
240 | text = Regex.Replace(text, "<(.|\n)+?>", " ", RegexOptions.IgnoreCase); //any other tags 241 | 242 | if (maxLength > 0 && text.Length > maxLength) 243 | text = text.Substring(0, maxLength); 244 | 245 | return text; 246 | } 247 | 248 | public static string ToPascalCase(this string original) 249 | { 250 | Regex invalidCharsRgx = new Regex("[^_a-zA-Z0-9]"); 251 | Regex whiteSpace = new Regex(@"(?<=\s)"); 252 | Regex startsWithLowerCaseChar = new Regex("^[a-z]"); 253 | Regex firstCharFollowedByUpperCasesOnly = new Regex("(?<=[A-Z])[A-Z0-9]+$"); 254 | Regex lowerCaseNextToNumber = new Regex("(?<=[0-9])[a-z]"); 255 | Regex upperCaseInside = new Regex("(?<=[A-Z])[A-Z]+?((?=[A-Z][a-z])|(?=[0-9]))"); 256 | 257 | // replace white spaces with undescore, then replace all invalid chars with empty string 258 | var pascalCase = invalidCharsRgx.Replace(whiteSpace.Replace(original, "_"), string.Empty) 259 | // split by underscores 260 | .Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries) 261 | // set first letter to uppercase 262 | .Select(w => startsWithLowerCaseChar.Replace(w, m => m.Value.ToUpper())) 263 | // replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc) 264 | .Select(w => firstCharFollowedByUpperCasesOnly.Replace(w, m => m.Value.ToLower())) 265 | // set upper case the first lower case following a number (Ab9cd -> Ab9Cd) 266 | .Select(w => lowerCaseNextToNumber.Replace(w, m => m.Value.ToUpper())) 267 | // lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef) 268 | .Select(w => upperCaseInside.Replace(w, m => m.Value.ToLower())); 269 | 270 | return string.Concat(pascalCase); 271 | } 272 | 273 | public static bool IsNullOrWhiteSpace(this string source) 274 | { 275 | return string.IsNullOrEmpty(source) || string.IsNullOrWhiteSpace(source); 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /MicroApi.Core/TableMapping.cs: -------------------------------------------------------------------------------- 1 | namespace MicroApi.Mapper; 2 | 3 | public sealed class TableMapping 4 | { 5 | public string Source => SourceType.Name; 6 | 7 | public string Target { get; set; } 8 | 9 | public Type SourceType { get; set; } 10 | 11 | public Dictionary ColumnConfig { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /MicroApi.Core/TypeExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MicroApi.Core 4 | { 5 | /// 6 | /// Type类的扩展方法 7 | /// 8 | public static class TypeExtension 9 | { 10 | /// 11 | /// 判断是否为基础类型 12 | /// 13 | /// 14 | /// 15 | public static bool IsBulitinType(this Type type) 16 | { 17 | return type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object; 18 | } 19 | 20 | public static bool IsNullable(this Type type) 21 | { 22 | if (type == null) throw new ArgumentNullException(nameof(type)); 23 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MicroApi.Demo/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Cors; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace MicroApi.Demo.Controllers 6 | { 7 | [ApiController] 8 | [EnableCors("allow_all")] 9 | [Authorize] 10 | [Route("api/[controller]")] 11 | public abstract class BaseController : ControllerBase 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicroApi.Demo/Controllers/LoginController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Cors; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using Microsoft.IdentityModel.Tokens; 6 | using System; 7 | using System.IdentityModel.Tokens.Jwt; 8 | using System.Security.Claims; 9 | using System.Text; 10 | 11 | namespace MicroApi.Demo.Controllers 12 | { 13 | [ApiController] 14 | [Route("api/[controller]")] 15 | [EnableCors("allow_all")] 16 | public class LoginController : ControllerBase 17 | { 18 | private JwtSettings _jwtSeetings; 19 | private ILogger _logger; 20 | public LoginController(IOptions option, ILogger logger) 21 | { 22 | _jwtSeetings = option.Value; 23 | _logger = logger; 24 | } 25 | 26 | [HttpPost] 27 | public IActionResult Login(string username, string password) 28 | { 29 | try 30 | { 31 | var claims = new Claim[] 32 | { 33 | new Claim(ClaimTypes.Name,username), 34 | new Claim(ClaimTypes.Role,"admin") 35 | }; 36 | var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSeetings.SecretKey)); 37 | var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); 38 | var token = new JwtSecurityToken( 39 | _jwtSeetings.Issuer, 40 | _jwtSeetings.Audience, 41 | claims, 42 | DateTime.Now, 43 | DateTime.Now.AddMinutes(30), 44 | creds 45 | ); 46 | return Ok(new { success = true, data = new JwtSecurityTokenHandler().WriteToken(token) }); 47 | } 48 | catch (Exception ex) 49 | { 50 | _logger.LogError(ex, message: ex.Message); 51 | throw; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /MicroApi.Demo/JwtSettings.cs: -------------------------------------------------------------------------------- 1 | namespace MicroApi.Demo 2 | { 3 | public class JwtSettings 4 | { 5 | /// 6 | /// 谁颁发的jwt 7 | /// 8 | public string Issuer { get; set; } 9 | 10 | /// 11 | /// 谁使用这个jwt 12 | /// 13 | public string Audience { get; set; } 14 | 15 | /// 16 | /// secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证, 17 | /// 所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了 18 | /// 通过jwt header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分 19 | /// 20 | public string SecretKey { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MicroApi.Demo/MicroApi.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /MicroApi.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace MicroApi.Demo 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder 18 | .UseKestrel(config => 19 | { 20 | //config.ListenAnyIP(8188); 21 | }) 22 | .ConfigureAppConfiguration((context, config) => 23 | { 24 | // Configure the app here. 25 | }) 26 | .UseStartup(); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MicroApi.Demo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:50113", 8 | "sslPort": 44393 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "MicroApi.Demo": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "applicationUrl": "https://localhost:5001/", 27 | "launchUrl": "swagger/index.html" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /MicroApi.Demo/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.OpenApi.Models; 7 | using Microsoft.AspNetCore.Http; 8 | using MicroApi.Core; 9 | using MicroApi.SqlServer; 10 | using Microsoft.AspNetCore.Authentication.JwtBearer; 11 | using Microsoft.IdentityModel.Tokens; 12 | using System.Text; 13 | using System.Collections.Generic; 14 | using System.Linq; 15 | using Swashbuckle.AspNetCore.Filters; 16 | using System; 17 | using MicroApi.Authorization; 18 | 19 | namespace MicroApi.Demo 20 | { 21 | public class Startup 22 | { 23 | public Startup(IConfiguration configuration) 24 | { 25 | Configuration = configuration; 26 | } 27 | 28 | public IConfiguration Configuration { get; } 29 | 30 | // This method gets called by the runtime. Use this method to add services to the container. 31 | public void ConfigureServices(IServiceCollection services) 32 | { 33 | //��Ҫ�Ӽ��������ļ�appsettings.json 34 | services.AddOptions(); 35 | services.AddSingleton(Configuration); 36 | 37 | InitWorks(services); 38 | services.AddControllers(); 39 | services.AddSwaggerGen(options => 40 | { 41 | options.SwaggerDoc("v1", new OpenApiInfo { Title = "MicroApi.Demo", Version = "v1" }); 42 | 43 | 44 | options.OperationFilter(); 45 | options.OperationFilter(); // 在header中添加token,传递到后台 46 | // 47 | options.OperationFilter(); 48 | options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme 49 | { 50 | Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)", 51 | Name = "Authorization", // jwt默认的参数名称 52 | In = ParameterLocation.Header, // jwt默认存放Authorization信息的位置(请求头中) 53 | Type = SecuritySchemeType.ApiKey 54 | }); 55 | }); 56 | } 57 | 58 | private void InitWorks(IServiceCollection services) 59 | { 60 | services.Configure(Configuration.GetSection("JwtSettings")); 61 | var jwtSeetings = new JwtSettings(); 62 | //绑定jwtSeetings 63 | Configuration.Bind("JwtSettings", jwtSeetings); 64 | services.AddAuthentication(options => 65 | { 66 | options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 67 | options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 68 | 69 | }) 70 | .AddJwtBearer(options => 71 | { 72 | options.TokenValidationParameters = new TokenValidationParameters 73 | { 74 | ValidIssuer = jwtSeetings.Issuer, 75 | ValidAudience = jwtSeetings.Audience, 76 | IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSeetings.SecretKey)) 77 | }; 78 | }); 79 | //以上是注册jwt 80 | services.Configure(options => 81 | { 82 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 83 | options.CheckConsentNeeded = context => true; 84 | options.MinimumSameSitePolicy = SameSiteMode.None; 85 | }); 86 | //指定域名 允许任何来源的主机访问 builder.AllowAnyOrigin() 87 | services.AddCors(options => 88 | { 89 | options.AddPolicy("allow_all", builder => 90 | { 91 | builder.WithOrigins(jwtSeetings.Audience) 92 | .AllowAnyMethod() 93 | .AllowAnyHeader() 94 | .AllowCredentials();//指定处理cookie 95 | }); 96 | }); 97 | 98 | var dbConnectionString = Configuration.GetConnectionString("MsSqlServer"); 99 | 100 | services.AddMicroApi(option => 101 | { 102 | option.AuthorizeType = AuthorizeType.JWT; 103 | }) 104 | .UseSqlServer(dbConnectionString) 105 | .AddMicroApiAuthorization(); 106 | } 107 | 108 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 109 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 110 | { 111 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 112 | if (env.IsDevelopment()) 113 | { 114 | app.UseDeveloperExceptionPage(); 115 | app.UseSwagger(); 116 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MicroApi.Demo v1")); 117 | } 118 | app.UsePathBase(""); 119 | app.UseHttpsRedirection(); 120 | app.UseStaticFiles(); 121 | app.UseCookiePolicy(); 122 | app.UseRouting(); 123 | app.UseCors(); 124 | app.UseAuthorization(); 125 | app.UseAuthentication(); 126 | 127 | app.UseEndpoints(endpoints => 128 | { 129 | endpoints.MapControllers(); 130 | }); 131 | app.UseMicroApi(); 132 | 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /MicroApi.Demo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /MicroApi.Demo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "ConnectionStrings": { 11 | "MsSqlServer": "initial catalog=MicroApiDemo;data source=.;password=111111;User id=sa;MultipleActiveResultSets=True" 12 | }, 13 | "JwtSettings": { 14 | "Issuer": "https://localhost:5001", 15 | "Audience": "https://localhost:7007", 16 | "SecretKey": "zhoudafu201807041123" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MicroApi.Firebird/MicroApi.Firebird.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.2 6 | 1.0.0.2 7 | 1.0.0.2 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.2 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MicroApi.Firebird/MicroApiMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using FirebirdSql.Data.FirebirdClient; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using SqlKata; 4 | using SqlKata.Compilers; 5 | using SqlKata.Execution; 6 | using System; 7 | using System.Data.Common; 8 | 9 | namespace MicroApi.Firebird 10 | { 11 | public static class MicroApiMiddlewareExtensions 12 | { 13 | public static IServiceCollection UseSqlFirebird(this IServiceCollection services, string connectionString, Action logger = null) 14 | { 15 | 16 | DbConnection connection = new FbConnection(connectionString); 17 | Compiler compiler = new FirebirdCompiler(); 18 | 19 | var db = new QueryFactory(connection, compiler) 20 | { 21 | Logger = logger ?? (q => 22 | { 23 | var oldColor = Console.ForegroundColor; 24 | Console.ForegroundColor = ConsoleColor.DarkGreen; 25 | Console.WriteLine(q.Sql); 26 | Console.ForegroundColor = oldColor; 27 | }) 28 | }; 29 | 30 | services.AddSingleton(db); 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MicroApi.MySql/MicroApi.MySql.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.2 6 | 1.0.0.2 7 | 1.0.0.2 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.2 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /MicroApi.MySql/MicroApiMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using MySqlConnector; 3 | using SqlKata; 4 | using SqlKata.Compilers; 5 | using SqlKata.Execution; 6 | using System; 7 | using System.Data.Common; 8 | 9 | namespace MicroApi.MySql 10 | { 11 | public static class MicroApiMiddlewareExtensions 12 | { 13 | public static IServiceCollection UseMySql(this IServiceCollection services, string connectionString, Action logger = null) 14 | { 15 | 16 | DbConnection connection = new MySqlConnection(connectionString); 17 | Compiler compiler = new MySqlCompiler(); 18 | 19 | var db = new QueryFactory(connection, compiler) 20 | { 21 | Logger = logger ?? (q => 22 | { 23 | var oldColor = Console.ForegroundColor; 24 | Console.ForegroundColor = ConsoleColor.DarkGreen; 25 | Console.WriteLine(q.Sql); 26 | Console.ForegroundColor = oldColor; 27 | }) 28 | }; 29 | 30 | services.AddSingleton(db); 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MicroApi.Oracle/MicroApi.Oracle.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.2 6 | 1.0.0.2 7 | 1.0.0.2 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.2 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /MicroApi.Oracle/MicroApiMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Oracle.ManagedDataAccess.Client; 3 | using SqlKata; 4 | using SqlKata.Compilers; 5 | using SqlKata.Execution; 6 | using System; 7 | using System.Data.Common; 8 | 9 | namespace MicroApi.Oracle 10 | { 11 | public static class MicroApiMiddlewareExtensions 12 | { 13 | public static IServiceCollection UseOracle(this IServiceCollection services, string connectionString, Action logger = null) 14 | { 15 | 16 | DbConnection connection = new OracleConnection(connectionString); 17 | Compiler compiler = new OracleCompiler(); 18 | 19 | var db = new QueryFactory(connection, compiler) 20 | { 21 | Logger = logger ?? (q => 22 | { 23 | var oldColor = Console.ForegroundColor; 24 | Console.ForegroundColor = ConsoleColor.DarkGreen; 25 | Console.WriteLine(q.Sql); 26 | Console.ForegroundColor = oldColor; 27 | }) 28 | }; 29 | 30 | services.AddSingleton(db); 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MicroApi.PostgreSQL/MicroApi.PostgreSQL.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.2 6 | 1.0.0.2 7 | 1.0.0.2 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.2 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /MicroApi.PostgreSQL/MicroApiMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Npgsql; 3 | using SqlKata; 4 | using SqlKata.Compilers; 5 | using SqlKata.Execution; 6 | using System; 7 | using System.Data.Common; 8 | 9 | namespace MicroApi.PostgreSQL 10 | { 11 | public static class MicroApiMiddlewareExtensions 12 | { 13 | public static IServiceCollection UsePostgreSQL(this IServiceCollection services, string connectionString, Action logger = null) 14 | { 15 | 16 | DbConnection connection = new NpgsqlConnection(connectionString); 17 | Compiler compiler = new PostgresCompiler(); 18 | 19 | var db = new QueryFactory(connection, compiler) 20 | { 21 | Logger = logger ?? (q => 22 | { 23 | var oldColor = Console.ForegroundColor; 24 | Console.ForegroundColor = ConsoleColor.DarkGreen; 25 | Console.WriteLine(q.Sql); 26 | Console.ForegroundColor = oldColor; 27 | }) 28 | }; 29 | 30 | services.AddSingleton(db); 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MicroApi.SqlServer/AutoApiMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using SqlKata; 3 | using SqlKata.Compilers; 4 | using SqlKata.Execution; 5 | using System; 6 | using System.Data.Common; 7 | using System.Data.SqlClient; 8 | 9 | namespace MicroApi.SqlServer 10 | { 11 | public static class MicroApiMiddlewareExtensions 12 | { 13 | public static IServiceCollection UseSqlServer(this IServiceCollection services, string connectionString, Action logger = null) 14 | { 15 | 16 | DbConnection connection = new SqlConnection(connectionString); 17 | Compiler compiler = new SqlServerCompiler(); 18 | 19 | var db = new QueryFactory(connection, compiler) 20 | { 21 | Logger = logger ?? (q => 22 | { 23 | var oldColor = Console.ForegroundColor; 24 | Console.ForegroundColor = ConsoleColor.DarkGreen; 25 | Console.WriteLine(q.Sql); 26 | Console.ForegroundColor = oldColor; 27 | }) 28 | }; 29 | 30 | services.AddSingleton(db); 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MicroApi.SqlServer/MicroApi.SqlServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.2 6 | 1.0.0.2 7 | 1.0.0.2 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.2 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /MicroApi.Sqlite/MicroApi.Sqlite.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0; 5 | 1.0.0.2 6 | 1.0.0.2 7 | 1.0.0.2 8 | zengxw 9 | 基于SqlKata Query Builder的可根据数据表自动生成Restful API的dotnet中间件 10 | 1.0.0.2 11 | add authorization filter 12 | True 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MicroApi.Sqlite/MicroApiMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Data.Sqlite; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using SqlKata; 4 | using SqlKata.Compilers; 5 | using SqlKata.Execution; 6 | using System; 7 | using System.Data.Common; 8 | 9 | namespace MicroApi.Sqlite 10 | { 11 | public static class MicroApiMiddlewareExtensions 12 | { 13 | public static IServiceCollection UseSqlite(this IServiceCollection services, string connectionString, Action logger = null) 14 | { 15 | 16 | DbConnection connection = new SqliteConnection(connectionString); 17 | Compiler compiler = new SqliteCompiler(); 18 | 19 | var db = new QueryFactory(connection, compiler) 20 | { 21 | Logger = logger ?? (q => 22 | { 23 | var oldColor = Console.ForegroundColor; 24 | Console.ForegroundColor = ConsoleColor.DarkGreen; 25 | Console.WriteLine(q.Sql); 26 | Console.ForegroundColor = oldColor; 27 | }) 28 | }; 29 | 30 | services.AddSingleton(db); 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MicroApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.Core", "MicroApi.Core\MicroApi.Core.csproj", "{216F47DB-2428-425B-93A6-5116353F1D68}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.Demo", "MicroApi.Demo\MicroApi.Demo.csproj", "{E5E93B62-29EE-43BC-84AF-95CAC8D1B9FE}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.SqlServer", "MicroApi.SqlServer\MicroApi.SqlServer.csproj", "{DB2E8580-34C7-4AA0-B4A1-408EAA76D95A}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.Firebird", "MicroApi.Firebird\MicroApi.Firebird.csproj", "{76202146-50A8-4774-8314-0AECBE571243}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.Sqlite", "MicroApi.Sqlite\MicroApi.Sqlite.csproj", "{984B72FA-EE67-46E8-81EA-D851F375B2DA}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.MySql", "MicroApi.MySql\MicroApi.MySql.csproj", "{57CB4D40-C225-41F4-9D98-A36F9199208B}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.Oracle", "MicroApi.Oracle\MicroApi.Oracle.csproj", "{3030A02F-9817-40DD-BD3F-BDB8E0113D55}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroApi.PostgreSQL", "MicroApi.PostgreSQL\MicroApi.PostgreSQL.csproj", "{4FE4F85D-5EB7-4A24-9946-8F4A0A1F6B50}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroApi.Authorization", "MicroApi.Authorization\MicroApi.Authorization.csproj", "{0C9C9B6A-1B47-46E9-9D85-2F5C9B4D1358}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {216F47DB-2428-425B-93A6-5116353F1D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {216F47DB-2428-425B-93A6-5116353F1D68}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {216F47DB-2428-425B-93A6-5116353F1D68}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {216F47DB-2428-425B-93A6-5116353F1D68}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {E5E93B62-29EE-43BC-84AF-95CAC8D1B9FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {E5E93B62-29EE-43BC-84AF-95CAC8D1B9FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {E5E93B62-29EE-43BC-84AF-95CAC8D1B9FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {E5E93B62-29EE-43BC-84AF-95CAC8D1B9FE}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {DB2E8580-34C7-4AA0-B4A1-408EAA76D95A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {DB2E8580-34C7-4AA0-B4A1-408EAA76D95A}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {DB2E8580-34C7-4AA0-B4A1-408EAA76D95A}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {DB2E8580-34C7-4AA0-B4A1-408EAA76D95A}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {76202146-50A8-4774-8314-0AECBE571243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {76202146-50A8-4774-8314-0AECBE571243}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {76202146-50A8-4774-8314-0AECBE571243}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {76202146-50A8-4774-8314-0AECBE571243}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {984B72FA-EE67-46E8-81EA-D851F375B2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {984B72FA-EE67-46E8-81EA-D851F375B2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {984B72FA-EE67-46E8-81EA-D851F375B2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {984B72FA-EE67-46E8-81EA-D851F375B2DA}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {57CB4D40-C225-41F4-9D98-A36F9199208B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {57CB4D40-C225-41F4-9D98-A36F9199208B}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {57CB4D40-C225-41F4-9D98-A36F9199208B}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {57CB4D40-C225-41F4-9D98-A36F9199208B}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {3030A02F-9817-40DD-BD3F-BDB8E0113D55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {3030A02F-9817-40DD-BD3F-BDB8E0113D55}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {3030A02F-9817-40DD-BD3F-BDB8E0113D55}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {3030A02F-9817-40DD-BD3F-BDB8E0113D55}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {4FE4F85D-5EB7-4A24-9946-8F4A0A1F6B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {4FE4F85D-5EB7-4A24-9946-8F4A0A1F6B50}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {4FE4F85D-5EB7-4A24-9946-8F4A0A1F6B50}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {4FE4F85D-5EB7-4A24-9946-8F4A0A1F6B50}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {0C9C9B6A-1B47-46E9-9D85-2F5C9B4D1358}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {0C9C9B6A-1B47-46E9-9D85-2F5C9B4D1358}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {0C9C9B6A-1B47-46E9-9D85-2F5C9B4D1358}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {0C9C9B6A-1B47-46E9-9D85-2F5C9B4D1358}.Release|Any CPU.Build.0 = Release|Any CPU 66 | EndGlobalSection 67 | GlobalSection(SolutionProperties) = preSolution 68 | HideSolutionNode = FALSE 69 | EndGlobalSection 70 | GlobalSection(ExtensibilityGlobals) = postSolution 71 | SolutionGuid = {0A60F63A-DF74-4E04-A476-D12C8FFDD17A} 72 | EndGlobalSection 73 | EndGlobal 74 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | [中文](https://github.com/x-trip/MicroApi/blob/master/README.md) 2 | # MicroApi 3 | A dotnet middleware based on [SqlKata Query Builder](https://github.com/sqlkata/querybuilder) that can automatically generate restful API according to the database 4 | ## Supported databases: 5 | 1. MySql *[MicroApi.MySql](https://www.nuget.org/packages/MicroApi.MySql/)* 6 | 2. SqlServer *[MicroApi.SqlServer](https://www.nuget.org/packages/MicroApi.SqlServer/)* 7 | 3. PostgreSQL *[MicroApi.PostgreSQL](https://www.nuget.org/packages/MicroApi.PostgreSQL/)* 8 | 4. Oracle *[MicroApi.Oracle](https://www.nuget.org/packages/MicroApi.Oracle/)* 9 | 5. Sqlite *[MicroApi.Sqlite](https://www.nuget.org/packages/MicroApi.Sqlite/)* 10 | 6. Firebird *[MicroApi.Firebird](https://www.nuget.org/packages/MicroApi.Firebird/)* 11 | ## Sample 12 | [MicroApi.Demo](https://github.com/x-trip/MicroApi/tree/master/MicroApi.Demo) 13 | ## How to use 14 | 1. Add nuget package *[MicroApi.Core](https://www.nuget.org/packages/MicroApi.Core/)* 15 | 16 | ``` Install-Package MicroApi.Core -Version 1.0.0 ``` 17 | 18 | or 19 | 20 | ``` dotnet add package MicroApi.Core --version 1.0.0 ``` 21 | 2. Add database nuget package, such as *[MicroApi.SqlServer](https://www.nuget.org/packages/MicroApi.SqlServer/)* 22 | 23 | ``` Install-Package MicroApi.SqlServer -Version 1.0.0 ``` 24 | 25 | or 26 | 27 | ``` dotnet add package MicroApi.SqlServer --version 1.0.0 ``` 28 | 29 | 3. Add the following code to the **configureservices** method in the file *startup.cs*: 30 | 31 | ``` 32 | services.AddAutoRestfulApi() 33 | .UseSqlServer(connectionString);//Your sql server database connection string 34 | ``` 35 | 36 | 4. Add the following code to the **Configure** method in the file *startup.cs*: 37 | ``` 38 | app.UseAutoRestfulApi(); 39 | ``` 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English](https://github.com/x-trip/MicroApi/blob/master/README-EN.md) 2 | 3 | # MicroApi 4 | 5 | 基于[SqlKata Query Builder](https://github.com/sqlkata/querybuilder)的可根据数据表自动生成Restful API的dotnet中间件 6 | 7 | ## 支持的数据库 8 | 9 | 1. MySql *[MicroApi.MySql](https://www.nuget.org/packages/MicroApi.MySql/)* 10 | 2. SqlServer *[MicroApi.SqlServer](https://www.nuget.org/packages/MicroApi.SqlServer/)* 11 | 3. PostgreSQL *[MicroApi.PostgreSQL](https://www.nuget.org/packages/MicroApi.PostgreSQL/)* 12 | 4. Oracle *[MicroApi.Oracle](https://www.nuget.org/packages/MicroApi.Oracle/)* 13 | 5. Sqlite *[MicroApi.Sqlite](https://www.nuget.org/packages/MicroApi.Sqlite/)* 14 | 6. Firebird *[MicroApi.Firebird](https://www.nuget.org/packages/MicroApi.Firebird/)* 15 | 16 | ## 接口认证中间件 17 | 18 | **目前仅支持JWT方式** 19 | 20 | *[MicroApi.Authorization](https://www.nuget.org/packages/MicroApi.Authorization)* 21 | 22 | ## 示例 23 | 24 | [MicroApi.Demo](https://github.com/x-trip/MicroApi/tree/master/MicroApi.Demo) 25 | 26 | ## 用法 27 | 28 | 1. 安装nuget包 *[MicroApi.Core](https://www.nuget.org/packages/MicroApi.Core/)* 29 | 30 | ``` Install-Package MicroApi.Core -Version 1.0.0 ``` 31 | 32 | or 33 | 34 | ``` dotnet add package MicroApi.Core --version 1.0.0 ``` 35 | 2. 安装数据库对应类型的nuget包,比SQL Server安装 *[MicroApi.SqlServer](https://www.nuget.org/packages/MicroApi.SqlServer/)* 36 | 37 | ``` Install-Package MicroApi.SqlServer -Version 1.0.0 ``` 38 | 39 | or 40 | 41 | ``` dotnet add package MicroApi.SqlServer --version 1.0.0 ``` 42 | 3. 在文件Startup.cs中的ConfigureServices方法中添加如下代码: 43 | 44 | ``` 45 | services.AddMicroApi() 46 | .UseSqlServer(connectionString)//Your sql server database connection string 47 | .AddMicroApiAuthorization();//添加接口认证中间件 48 | ``` 49 | 50 | 4. 在文件Startup.cs中的Configure方法中添加如下代码: 51 | 52 | ``` 53 | app.UseMicroApi(); 54 | ``` 55 | ## API内置格式 56 | 生成api的访问路径统一为:/api/{TableName} 57 | ### GET 58 | **暂不支持/api/{TableName}/{PrimaryKey}这种路由**,后续版本考虑增加表名以及字段名称自定义映射功能后会支持。当前版本主要支持以下功能: 59 | 60 | **分页查询** 61 | 62 | 接口路径为:/api/{TableName}?page=&size= 或者 /api/{TableName}?offset=&limit= 两种方式 63 | 64 | **排序** 65 | 66 | 1. 升序接口路径为:/api/{TableName}?orderAsc={列名1,列名2,列名3,.....} 67 | 2. 降序接口路径为:/api/{TableName}?orderDesc={列名1,列名2,列名3,.....} 68 | 69 | **特殊条件查询,比如大于等于、小于等于、大于、小于、IN、LIKE查询** 70 | 71 | 1. 大于等于:/api/{TableName}?{列名}.ge={值} 72 | 2. 大于:/api/{TableName}?{列名}.gt={值} 73 | 3. 小于等于:/api/{TableName}?{列名}.le={值} 74 | 4. 小于:/api/{TableName}?{列名}.lt={值} 75 | 5. IN: /api/{TableName}?{列名}.in={值} 76 | 6. LIKE: /api/{TableName}?{列名}.like={值} 77 | 78 | ### POST 79 | 用于新增数据,访问路径后面跟的参数无效 80 | ### PUT 81 | 用于更新数据,可按查询条件进行批量更新。查询条件支持GET中的特殊条件查询,比如大于等于、小于等于、大于、小于、IN、LIKE查询 82 | ### DELETE 83 | 用于删除数据,可按查询条件进行批量删除。查询条件支持GET中的特殊条件查询,比如大于等于、小于等于、大于、小于、IN、LIKE查询 84 | --------------------------------------------------------------------------------