├── .gitignore
├── LICENSE
├── README.md
├── Sixnet.Database.MySqlConnector.nuspec
├── Sixnet.Database.MySqlConnector.sln
├── Sixnet.Database.MySqlConnector
├── MySqlBulkInsertionOptions.cs
├── MySqlDataCommandResolver.cs
├── MySqlDefaultFieldFormatter.cs
├── MySqlManager.cs
├── MySqlProvider.cs
├── Sixnet.Database.MySqlConnector.csproj
└── sixnet.snk
└── nugeticon.png
/.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 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 |
56 | # StyleCop
57 | StyleCopReport.xml
58 |
59 | # Files built by Visual Studio
60 | *_i.c
61 | *_p.c
62 | *_i.h
63 | *.ilk
64 | *.meta
65 | *.obj
66 | *.iobj
67 | *.pch
68 | *.pdb
69 | *.ipdb
70 | *.pgc
71 | *.pgd
72 | *.rsp
73 | *.sbr
74 | *.tlb
75 | *.tli
76 | *.tlh
77 | *.tmp
78 | *.tmp_proj
79 | *.log
80 | *.vspscc
81 | *.vssscc
82 | .builds
83 | *.pidb
84 | *.svclog
85 | *.scc
86 |
87 | # Chutzpah Test files
88 | _Chutzpah*
89 |
90 | # Visual C++ cache files
91 | ipch/
92 | *.aps
93 | *.ncb
94 | *.opendb
95 | *.opensdf
96 | *.sdf
97 | *.cachefile
98 | *.VC.db
99 | *.VC.VC.opendb
100 |
101 | # Visual Studio profiler
102 | *.psess
103 | *.vsp
104 | *.vspx
105 | *.sap
106 |
107 | # Visual Studio Trace Files
108 | *.e2e
109 |
110 | # TFS 2012 Local Workspace
111 | $tf/
112 |
113 | # Guidance Automation Toolkit
114 | *.gpState
115 |
116 | # ReSharper is a .NET coding add-in
117 | _ReSharper*/
118 | *.[Rr]e[Ss]harper
119 | *.DotSettings.user
120 |
121 | # JustCode is a .NET coding add-in
122 | .JustCode
123 |
124 | # TeamCity is a build add-in
125 | _TeamCity*
126 |
127 | # DotCover is a Code Coverage Tool
128 | *.dotCover
129 |
130 | # AxoCover is a Code Coverage Tool
131 | .axoCover/*
132 | !.axoCover/settings.json
133 |
134 | # Visual Studio code coverage results
135 | *.coverage
136 | *.coveragexml
137 |
138 | # NCrunch
139 | _NCrunch_*
140 | .*crunch*.local.xml
141 | nCrunchTemp_*
142 |
143 | # MightyMoose
144 | *.mm.*
145 | AutoTest.Net/
146 |
147 | # Web workbench (sass)
148 | .sass-cache/
149 |
150 | # Installshield output folder
151 | [Ee]xpress/
152 |
153 | # DocProject is a documentation generator add-in
154 | DocProject/buildhelp/
155 | DocProject/Help/*.HxT
156 | DocProject/Help/*.HxC
157 | DocProject/Help/*.hhc
158 | DocProject/Help/*.hhk
159 | DocProject/Help/*.hhp
160 | DocProject/Help/Html2
161 | DocProject/Help/html
162 |
163 | # Click-Once directory
164 | publish/
165 |
166 | # Publish Web Output
167 | *.[Pp]ublish.xml
168 | *.azurePubxml
169 | # Note: Comment the next line if you want to checkin your web deploy settings,
170 | # but database connection strings (with potential passwords) will be unencrypted
171 | *.pubxml
172 | *.publishproj
173 |
174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
175 | # checkin your Azure Web App publish settings, but sensitive information contained
176 | # in these scripts will be unencrypted
177 | PublishScripts/
178 |
179 | # NuGet Packages
180 | *.nupkg
181 | # The packages folder can be ignored because of Package Restore
182 | **/[Pp]ackages/*
183 | # except build/, which is used as an MSBuild target.
184 | !**/[Pp]ackages/build/
185 | # Uncomment if necessary however generally it will be regenerated when needed
186 | #!**/[Pp]ackages/repositories.config
187 | # NuGet v3's project.json files produces more ignorable files
188 | *.nuget.props
189 | *.nuget.targets
190 |
191 | # Microsoft Azure Build Output
192 | csx/
193 | *.build.csdef
194 |
195 | # Microsoft Azure Emulator
196 | ecf/
197 | rcf/
198 |
199 | # Windows Store app package directories and files
200 | AppPackages/
201 | BundleArtifacts/
202 | Package.StoreAssociation.xml
203 | _pkginfo.txt
204 | *.appx
205 |
206 | # Visual Studio cache files
207 | # files ending in .cache can be ignored
208 | *.[Cc]ache
209 | # but keep track of directories ending in .cache
210 | !*.[Cc]ache/
211 |
212 | # Others
213 | ClientBin/
214 | ~$*
215 | *~
216 | *.dbmdl
217 | *.dbproj.schemaview
218 | *.jfm
219 | *.pfx
220 | *.publishsettings
221 | orleans.codegen.cs
222 |
223 | # Including strong name files can present a security risk
224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
225 | #*.snk
226 |
227 | # Since there are multiple workflows, uncomment next line to ignore bower_components
228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
229 | #bower_components/
230 |
231 | # RIA/Silverlight projects
232 | Generated_Code/
233 |
234 | # Backup & report files from converting an old project file
235 | # to a newer Visual Studio version. Backup files are not needed,
236 | # because we have git ;-)
237 | _UpgradeReport_Files/
238 | Backup*/
239 | UpgradeLog*.XML
240 | UpgradeLog*.htm
241 | ServiceFabricBackup/
242 | *.rptproj.bak
243 |
244 | # SQL Server files
245 | *.mdf
246 | *.ldf
247 | *.ndf
248 |
249 | # Business Intelligence projects
250 | *.rdl.data
251 | *.bim.layout
252 | *.bim_*.settings
253 | *.rptproj.rsuser
254 |
255 | # Microsoft Fakes
256 | FakesAssemblies/
257 |
258 | # GhostDoc plugin setting file
259 | *.GhostDoc.xml
260 |
261 | # Node.js Tools for Visual Studio
262 | .ntvs_analysis.dat
263 | node_modules/
264 |
265 | # Visual Studio 6 build log
266 | *.plg
267 |
268 | # Visual Studio 6 workspace options file
269 | *.opt
270 |
271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
272 | *.vbw
273 |
274 | # Visual Studio LightSwitch build output
275 | **/*.HTMLClient/GeneratedArtifacts
276 | **/*.DesktopClient/GeneratedArtifacts
277 | **/*.DesktopClient/ModelManifest.xml
278 | **/*.Server/GeneratedArtifacts
279 | **/*.Server/ModelManifest.xml
280 | _Pvt_Extensions
281 |
282 | # Paket dependency manager
283 | .paket/paket.exe
284 | paket-files/
285 |
286 | # FAKE - F# Make
287 | .fake/
288 |
289 | # JetBrains Rider
290 | .idea/
291 | *.sln.iml
292 |
293 | # CodeRush
294 | .cr/
295 |
296 | # Python Tools for Visual Studio (PTVS)
297 | __pycache__/
298 | *.pyc
299 |
300 | # Cake - Uncomment if you are using it
301 | # tools/**
302 | # !tools/packages.config
303 |
304 | # Tabs Studio
305 | *.tss
306 |
307 | # Telerik's JustMock configuration file
308 | *.jmconfig
309 |
310 | # BizTalk build output
311 | *.btp.cs
312 | *.btm.cs
313 | *.odx.cs
314 | *.xsd.cs
315 |
316 | # OpenCover UI analysis results
317 | OpenCover/
318 |
319 | # Azure Stream Analytics local run output
320 | ASALocalRun/
321 |
322 | # MSBuild Binary and Structured Log
323 | *.binlog
324 |
325 | # NVidia Nsight GPU debugger configuration file
326 | *.nvuser
327 |
328 | # MFractors (Xamarin productivity tool) working folder
329 | .mfractor/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 EZNEW.NET
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sixnet.Database.MySqlConnector
2 |
3 | Provides access to MySQL databases based on the Sixnet development framework
4 |
5 | # Reporting issues and bugs
6 |
7 | If you have any questions or Suggestions, you can report to us via email,to the lidingbin@live.com, and we will reply to you as soon as possible, or you can contact [DingBin Li](https://github.com/lidingbin) via GitHub
8 |
9 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sixnet.Database.MySqlConnector
5 | $version$
6 |
7 | DingBin.Li
8 | DingBin.Li
9 | false
10 | MIT
11 | https://github.com/sixnet-net
12 | images\nugeticon.png
13 | Provides access to MySQL databases based on the Sixnet development framework
14 | Copyright © DingBin.Li
15 | .NET,Sixnet,MySQL
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33110.190
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sixnet.Database.MySqlConnector", "Sixnet.Database.MySqlConnector\Sixnet.Database.MySqlConnector.csproj", "{72A9B114-740D-4B6F-B6BC-9CE6D0AC1274}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {72A9B114-740D-4B6F-B6BC-9CE6D0AC1274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {72A9B114-740D-4B6F-B6BC-9CE6D0AC1274}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {72A9B114-740D-4B6F-B6BC-9CE6D0AC1274}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {72A9B114-740D-4B6F-B6BC-9CE6D0AC1274}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {53A9E125-91E6-48C3-B350-3C0E53D5A420}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/MySqlBulkInsertionOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using MySqlConnector;
5 | using Sixnet.Development.Data.Database;
6 |
7 | namespace Sixnet.Database.MySqlConnector
8 | {
9 | ///
10 | /// Defines bulk insertion options for mysql
11 | ///
12 | public class MySqlBulkInsertionOptions : ISixnetBulkInsertionOptions
13 | {
14 | ///
15 | /// Gets or sets the loader priority.
16 | ///
17 | public MySqlBulkLoaderPriority Priority { get; set; } = MySqlBulkLoaderPriority.None;
18 |
19 | ///
20 | /// Gets or sets the conflict option.
21 | ///
22 | public MySqlBulkLoaderConflictOption ConflictOption { get; set; } = MySqlBulkLoaderConflictOption.None;
23 |
24 | ///
25 | /// Gets or sets the escape character.
26 | ///
27 | public char EscapeCharacter { get; set; }
28 |
29 | ///
30 | /// Indicates whether [field quotation optional].
31 | ///
32 | public bool FieldQuotationOptional { get; set; }
33 |
34 | ///
35 | /// Gets or sets the field quotation character.
36 | ///
37 | public char FieldQuotationCharacter { get; set; }
38 |
39 | ///
40 | /// Gets or sets the line prefix.
41 | ///
42 | public string LinePrefix { get; set; }
43 |
44 | ///
45 | /// Gets or sets the number of lines to skip.
46 | ///
47 | public int NumberOfLinesToSkip { get; set; } = 0;
48 |
49 | ///
50 | /// Gets the columns.
51 | ///
52 | public List Columns { get; }
53 |
54 | ///
55 | /// Gets or sets the timeout.
56 | ///
57 | public int Timeout { get; set; }
58 |
59 | ///
60 | /// Gets or sets the character set.
61 | ///
62 | public string CharacterSet { get; set; }
63 |
64 | ///
65 | /// Gets or sets the line terminator.
66 | /// Default values is Environment.NewLine.
67 | ///
68 | public string LineTerminator { get; set; } = Environment.NewLine;
69 |
70 | ///
71 | /// Gets or sets the field terminator.
72 | /// Default values is CultureInfo.CurrentCulture.TextInfo.ListSeparator;
73 | ///
74 | public string FieldTerminator { get; set; } = CultureInfo.CurrentCulture.TextInfo.ListSeparator;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/MySqlDataCommandResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Linq;
5 | using System.Text;
6 | using Sixnet.Development.Data;
7 | using Sixnet.Development.Data.Command;
8 | using Sixnet.Development.Data.Database;
9 | using Sixnet.Development.Data.Field;
10 | using Sixnet.Development.Entity;
11 | using Sixnet.Development.Queryable;
12 | using Sixnet.Exceptions;
13 |
14 | namespace Sixnet.Database.MySqlConnector
15 | {
16 | ///
17 | /// Defines data command resolver for mysql
18 | ///
19 | internal class MySqlDataCommandResolver : BaseDataCommandResolver
20 | {
21 | #region Constructor
22 |
23 | public MySqlDataCommandResolver()
24 | {
25 | DatabaseType = DatabaseType.MySQL;
26 | DefaultFieldFormatter = new MySqlDefaultFieldFormatter();
27 | ParameterPrefix = "@";
28 | WrapKeywordFunc = MySqlManager.WrapKeyword;
29 | RecursiveKeyword = "WITH RECURSIVE";
30 | DbTypeDefaultValues = new Dictionary()
31 | {
32 | { DbType.Byte, "0" },
33 | { DbType.SByte, "0" },
34 | { DbType.Int16, "0" },
35 | { DbType.UInt16, "0" },
36 | { DbType.Int32, "0" },
37 | { DbType.UInt32, "0" },
38 | { DbType.Int64, "0" },
39 | { DbType.UInt64, "0" },
40 | { DbType.Single, "0" },
41 | { DbType.Double, "0" },
42 | { DbType.Decimal, "0" },
43 | { DbType.Boolean, "0" },
44 | { DbType.String, "''" },
45 | { DbType.StringFixedLength, "''" },
46 | { DbType.Guid, "UUID()" },
47 | { DbType.DateTime, "NOW()" },
48 | { DbType.DateTime2, "NOW()" },
49 | { DbType.DateTimeOffset, "NOW()" },
50 | { DbType.Time, "CURTIME()" }
51 | };
52 | }
53 |
54 | #endregion
55 |
56 | #region Get query statement
57 |
58 | ///
59 | /// Get query statement
60 | ///
61 | /// Command resolve context
62 | /// Queryable translation result
63 | /// Queryable location
64 | ///
65 | protected override QueryDatabaseStatement GenerateQueryStatementCore(DataCommandResolveContext context, QueryableTranslationResult translationResult, QueryableLocation location)
66 | {
67 | var queryable = translationResult.GetOriginalQueryable();
68 | string sqlStatement;
69 | IEnumerable outputFields = null;
70 | switch (queryable.ExecutionMode)
71 | {
72 | case QueryableExecutionMode.Script:
73 | sqlStatement = translationResult.GetCondition();
74 | break;
75 | case QueryableExecutionMode.Regular:
76 | default:
77 | // table pet name
78 | var tablePetName = context.GetTablePetName(queryable, queryable.GetModelType());
79 | //sort
80 | var sort = translationResult.GetSort();
81 | var hasSort = !string.IsNullOrWhiteSpace(sort);
82 | //limit
83 | var limit = GetLimitString(queryable.SkipCount, queryable.TakeCount, hasSort);
84 | //combine
85 | var combine = translationResult.GetCombine();
86 | var hasCombine = !string.IsNullOrWhiteSpace(combine);
87 | //group
88 | var group = translationResult.GetGroup();
89 | //having
90 | var having = translationResult.GetHavingCondition();
91 | //pre script output
92 | var targetScript = translationResult.GetPreOutputStatement();
93 |
94 | if (string.IsNullOrWhiteSpace(targetScript))
95 | {
96 | //target
97 | var targetStatement = GetFromTargetStatement(context, queryable, location, tablePetName);
98 | outputFields = targetStatement.OutputFields;
99 | //condition
100 | var condition = translationResult.GetCondition(ConditionStartKeyword);
101 | //join
102 | var join = translationResult.GetJoin();
103 | //target statement
104 | targetScript = $"{targetStatement.Script}{join}{condition}{group}{having}";
105 | }
106 | else
107 | {
108 | targetScript = $"{targetScript}{group}{having}";
109 | outputFields = translationResult.GetPreOutputFields();
110 | }
111 |
112 | // output fields
113 | if (outputFields.IsNullOrEmpty() || !queryable.SelectedFields.IsNullOrEmpty())
114 | {
115 | outputFields = SixnetDataManager.GetQueryableFields(DatabaseType, queryable.GetModelType(), queryable, context.IsRootQueryable(queryable));
116 | }
117 | var outputFieldString = FormatFieldsString(context, queryable, location, FieldLocation.Output, outputFields);
118 | //pre script
119 | var preScript = GetPreScript(context, location);
120 | //statement
121 | sqlStatement = $"SELECT{GetDistinctString(queryable)} {outputFieldString} FROM {targetScript}{sort}{limit}";
122 | switch (queryable.OutputType)
123 | {
124 | case QueryableOutputType.Count:
125 | sqlStatement = hasCombine
126 | ? $"{preScript}SELECT COUNT(1) FROM (({sqlStatement}){combine}){TablePetNameKeyword}{tablePetName}"
127 | : $"{preScript}SELECT COUNT(1) FROM ({sqlStatement}){TablePetNameKeyword}{tablePetName}";
128 | break;
129 | case QueryableOutputType.Predicate:
130 | sqlStatement = hasCombine
131 | ? $"{preScript}SELECT EXISTS(({sqlStatement}){combine})"
132 | : $"{preScript}SELECT EXISTS({sqlStatement})";
133 | break;
134 | default:
135 | sqlStatement = hasCombine
136 | ? $"{preScript}({sqlStatement}){combine}"
137 | : $"{preScript}{sqlStatement}";
138 | break;
139 | }
140 | break;
141 | }
142 |
143 | //parameters
144 | var parameters = context.GetParameters();
145 |
146 | //log script
147 | if (location == QueryableLocation.Top)
148 | {
149 | LogScript(sqlStatement, parameters);
150 | }
151 |
152 | return QueryDatabaseStatement.Create(sqlStatement, parameters, outputFields);
153 | }
154 |
155 | #endregion
156 |
157 | #region Get insert statement
158 |
159 | ///
160 | /// Get insert statement
161 | ///
162 | /// Command resolve context
163 | ///
164 | protected override List GenerateInsertStatements(DataCommandResolveContext context)
165 | {
166 | var command = context.DataCommandExecutionContext.Command;
167 | var dataCommandExecutionContext = context.DataCommandExecutionContext;
168 | var entityType = dataCommandExecutionContext.Command.GetEntityType();
169 | var fields = SixnetDataManager.GetInsertableFields(DatabaseType, entityType);
170 | var fieldCount = fields.GetCount();
171 | var insertFields = new List(fieldCount);
172 | var insertValues = new List(fieldCount);
173 | DataField autoIncrementField = null;
174 | DataField splitField = null;
175 | dynamic splitValue = default;
176 |
177 | foreach (var field in fields)
178 | {
179 | if (field.InRole(FieldRole.Increment))
180 | {
181 | autoIncrementField ??= field;
182 | if (!autoIncrementField.InRole(FieldRole.PrimaryKey) && field.InRole(FieldRole.PrimaryKey)) // get first primary key field
183 | {
184 | autoIncrementField = field;
185 | }
186 | continue;
187 | }
188 | // fields
189 | insertFields.Add(WrapKeywordFunc(field.GetFieldName(DatabaseType)));
190 | // values
191 | var insertValue = command.FieldsAssignment.GetNewValue(field.PropertyName);
192 | insertValues.Add(FormatInsertValueField(context, command.Queryable, insertValue));
193 |
194 | // split value
195 | if (field.InRole(FieldRole.SplitValue))
196 | {
197 | splitValue = insertValue;
198 | splitField = field;
199 | }
200 | }
201 |
202 | SixnetDirectThrower.ThrowNotSupportIf(autoIncrementField != null && splitField != null, $"Not support auto increment field for split table:{entityType.Name}");
203 |
204 | if (splitField != null)
205 | {
206 | dataCommandExecutionContext.SetSplitValues(new List(1) { splitValue });
207 | }
208 | var tableNames = dataCommandExecutionContext.GetTableNames();
209 | SixnetDirectThrower.ThrowInvalidOperationIf(tableNames.IsNullOrEmpty(), $"Get table name failed for {entityType.Name}");
210 | var statementBuilder = new StringBuilder();
211 | var incrScripts = new List();
212 | foreach (var tableName in tableNames)
213 | {
214 | statementBuilder.AppendLine($"INSERT INTO {WrapKeywordFunc(tableName)} ({string.Join(",", insertFields)}) VALUES ({string.Join(",", insertValues)});");
215 | }
216 | if (autoIncrementField != null)
217 | {
218 | var incrField = $"{command.Id}";
219 | var incrParameter = FormatParameterName(incrField);
220 | statementBuilder.AppendLine($"SET {incrParameter} = LAST_INSERT_ID();");
221 | incrScripts.Add($"{incrParameter} {ColumnPetNameKeyword} {incrField}");
222 | }
223 | return new List()
224 | {
225 | new ExecutionDatabaseStatement()
226 | {
227 | Script = statementBuilder.ToString(),
228 | ScriptType = GetCommandType(command),
229 | MustAffectData = command.Options?.MustAffectData ?? false,
230 | Parameters = context.GetParameters(),
231 | IncrScript = string.Join(",", incrScripts)
232 | }
233 | };
234 | }
235 |
236 | #endregion
237 |
238 | #region Get update statement
239 |
240 | ///
241 | /// Get update statement
242 | ///
243 | /// Command resolve context
244 | ///
245 | protected override List GenerateUpdateStatements(DataCommandResolveContext context)
246 | {
247 | var command = context.DataCommandExecutionContext.Command;
248 | SixnetException.ThrowIf(command?.FieldsAssignment?.NewValues.IsNullOrEmpty() ?? true, "No set update field");
249 |
250 | #region translate
251 |
252 | var translationResult = Translate(context);
253 | var preScripts = context.GetPreScripts();
254 |
255 | #endregion
256 |
257 | #region script
258 |
259 | var dataCommandExecutionContext = context.DataCommandExecutionContext;
260 | var entityType = dataCommandExecutionContext.Command.GetEntityType();
261 |
262 | var tableNames = dataCommandExecutionContext.GetTableNames(command);
263 | SixnetDirectThrower.ThrowInvalidOperationIf(tableNames.IsNullOrEmpty(), $"Get table name failed for {entityType.Name}");
264 |
265 | var tablePetName = command.Queryable == null ? context.GetNewTablePetName() : context.GetDefaultTablePetName(command.Queryable);
266 | var newValues = command.FieldsAssignment.NewValues;
267 | var updateSetArray = new List();
268 | foreach (var newValueItem in newValues)
269 | {
270 | var newValue = newValueItem.Value;
271 | var propertyName = newValueItem.Key;
272 | var updateField = SixnetDataManager.GetField(dataCommandExecutionContext.Server.DatabaseType, command.GetEntityType(), DataField.Create(propertyName)) as DataField;
273 |
274 | SixnetDirectThrower.ThrowSixnetExceptionIf(updateField == null, $"Not found field:{propertyName}");
275 |
276 | var fieldFormattedName = WrapKeywordFunc(updateField.GetFieldName(DatabaseType));
277 | var newValueExpression = FormatUpdateValueField(context, command, newValue);
278 | updateSetArray.Add($"{tablePetName}.{fieldFormattedName}={newValueExpression}");
279 | }
280 |
281 | // parameters
282 | var parameters = ConvertParameter(command.ScriptParameters) ?? new DataCommandParameters();
283 | parameters.Union(context.GetParameters());
284 |
285 | // statement
286 | var scriptType = GetCommandType(command);
287 | string scriptTemplate;
288 | if (preScripts.IsNullOrEmpty())
289 | {
290 | var condition = translationResult?.GetCondition(ConditionStartKeyword);
291 | var join = translationResult?.GetJoin();
292 | scriptTemplate = $"UPDATE {{0}}{TablePetNameKeyword}{tablePetName}{join} SET {string.Join(",", updateSetArray)}{condition};";
293 | var statementBuilder = new StringBuilder();
294 | foreach (var tableName in tableNames)
295 | {
296 | statementBuilder.AppendLine(string.Format(scriptTemplate, WrapKeywordFunc(tableName)));
297 | }
298 | return new List(1)
299 | {
300 | new ExecutionDatabaseStatement()
301 | {
302 | Script = statementBuilder.ToString(),
303 | ScriptType = scriptType,
304 | MustAffectData = command.Options?.MustAffectData ?? false,
305 | Parameters = parameters,
306 | HasPreScript = false
307 | }
308 | };
309 | }
310 | else
311 | {
312 | var queryStatement = GenerateQueryStatementCore(context, translationResult, QueryableLocation.JoinTarget);
313 | var updateTablePetName = "UTB";
314 | var joinItems = FormatWrapJoinPrimaryKeys(context, command.Queryable, command.GetEntityType(), tablePetName, tablePetName, updateTablePetName);
315 | scriptTemplate = $"{FormatPreScript(context)}UPDATE {{0}}{TablePetNameKeyword}{tablePetName} INNER JOIN ({queryStatement.Script}){TablePetNameKeyword}{updateTablePetName} ON {string.Join(" AND ", joinItems)} SET {string.Join(",", updateSetArray)};";
316 | var statements = new List(tableNames.Count);
317 | foreach (var tableName in tableNames)
318 | {
319 | statements.Add(new ExecutionDatabaseStatement()
320 | {
321 | Script = string.Format(scriptTemplate, WrapKeywordFunc(tableName)),
322 | ScriptType = scriptType,
323 | MustAffectData = command.Options?.MustAffectData ?? false,
324 | Parameters = parameters,
325 | HasPreScript = true
326 | });
327 | }
328 | return statements;
329 | }
330 |
331 | #endregion
332 | }
333 |
334 | #endregion
335 |
336 | #region Get delete statement
337 |
338 | ///
339 | /// Get delete statement
340 | ///
341 | /// Command resolve context
342 | ///
343 | protected override List GenerateDeleteStatements(DataCommandResolveContext context)
344 | {
345 | var dataCommandExecutionContext = context.DataCommandExecutionContext;
346 | var command = dataCommandExecutionContext.Command;
347 |
348 | #region translate
349 |
350 | var translationResult = Translate(context);
351 | var preScripts = context.GetPreScripts();
352 |
353 | #endregion
354 |
355 | #region script
356 |
357 | var tablePetName = command.Queryable == null ? context.GetNewTablePetName() : context.GetDefaultTablePetName(command.Queryable);
358 | var entityType = dataCommandExecutionContext.Command.GetEntityType();
359 | var tableNames = dataCommandExecutionContext.GetTableNames(command);
360 | SixnetDirectThrower.ThrowInvalidOperationIf(tableNames.IsNullOrEmpty(), $"Get table name failed for {entityType.Name}");
361 |
362 | // parameters
363 | var parameters = ConvertParameter(command.ScriptParameters) ?? new DataCommandParameters();
364 | parameters.Union(context.GetParameters());
365 |
366 | // statement
367 | var scriptType = GetCommandType(command);
368 | string scriptTemplate;
369 | if (preScripts.IsNullOrEmpty())
370 | {
371 | var condition = translationResult?.GetCondition(ConditionStartKeyword);
372 | var join = translationResult?.GetJoin();
373 | scriptTemplate = $"DELETE {tablePetName} FROM {{0}}{TablePetNameKeyword}{tablePetName}{join}{condition};";
374 | var statementBuilder = new StringBuilder();
375 | foreach (var tableName in tableNames)
376 | {
377 | statementBuilder.AppendLine(string.Format(scriptTemplate, WrapKeywordFunc(tableName)));
378 | }
379 | return new List(1)
380 | {
381 | new ExecutionDatabaseStatement()
382 | {
383 | Script = statementBuilder.ToString(),
384 | ScriptType = scriptType,
385 | MustAffectData = command.Options?.MustAffectData ?? false,
386 | Parameters = parameters,
387 | HasPreScript = false
388 | }
389 | };
390 | }
391 | else
392 | {
393 | var queryStatement = GenerateQueryStatementCore(context, translationResult, QueryableLocation.JoinTarget);
394 | var updateTablePetName = "DTB";
395 | var joinItems = FormatWrapJoinPrimaryKeys(context, command.Queryable, command.GetEntityType(), tablePetName, tablePetName, updateTablePetName);
396 | scriptTemplate = $"{FormatPreScript(context)}DELETE {tablePetName} FROM {{0}}{TablePetNameKeyword}{tablePetName} INNER JOIN ({queryStatement.Script}){TablePetNameKeyword}{updateTablePetName} ON {string.Join(" AND ", joinItems)};";
397 | var statements = new List(tableNames.Count);
398 | foreach (var tableName in tableNames)
399 | {
400 | statements.Add(new ExecutionDatabaseStatement()
401 | {
402 | Script = string.Format(scriptTemplate, WrapKeywordFunc(tableName)),
403 | ScriptType = scriptType,
404 | MustAffectData = command.Options?.MustAffectData ?? false,
405 | Parameters = parameters,
406 | HasPreScript = true
407 | });
408 | }
409 | return statements;
410 | }
411 |
412 | #endregion
413 | }
414 |
415 | #endregion
416 |
417 | #region Get create table statements
418 |
419 | ///
420 | /// Get create table statements
421 | ///
422 | /// Migration command
423 | ///
424 | protected override List GetCreateTableStatements(MigrationDatabaseCommand migrationCommand)
425 | {
426 | var migrationInfo = migrationCommand.MigrationInfo;
427 | if (migrationInfo?.NewTables.IsNullOrEmpty() ?? true)
428 | {
429 | return new List(0);
430 | }
431 | var newTables = migrationInfo.NewTables;
432 | var statements = new List();
433 | var options = migrationCommand.MigrationInfo;
434 | foreach (var newTableInfo in newTables)
435 | {
436 | if (newTableInfo?.EntityType == null || (newTableInfo?.TableNames.IsNullOrEmpty() ?? true))
437 | {
438 | continue;
439 | }
440 | var entityType = newTableInfo.EntityType;
441 | var entityConfig = SixnetEntityManager.GetEntityConfig(entityType);
442 | SixnetDirectThrower.ThrowSixnetExceptionIf(entityConfig == null, $"Get entity config failed for {entityType.Name}");
443 |
444 | var newFieldScripts = new List();
445 | var primaryKeyNames = new List();
446 | foreach (var field in entityConfig.AllFields)
447 | {
448 | var dataField = SixnetDataManager.GetField(MySqlManager.CurrentDatabaseServerType, entityType, field.Value);
449 | if (dataField is DataField dataEntityField)
450 | {
451 | var dataFieldName = MySqlManager.WrapKeyword(dataEntityField.GetFieldName(DatabaseType));
452 | newFieldScripts.Add($"{dataFieldName}{GetSqlDataType(dataEntityField, options)}{GetFieldNullable(dataEntityField, options)}{GetSqlDefaultValue(dataEntityField, options)}");
453 | if (dataEntityField.InRole(FieldRole.PrimaryKey))
454 | {
455 | primaryKeyNames.Add($"{dataFieldName}");
456 | }
457 | }
458 | }
459 | foreach (var tableName in newTableInfo.TableNames)
460 | {
461 | var createTableStatement = new ExecutionDatabaseStatement()
462 | {
463 | Script = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(",", newFieldScripts)}{(primaryKeyNames.IsNullOrEmpty() ? "" : $", PRIMARY KEY ({string.Join(",", primaryKeyNames)}) USING BTREE")});"
464 | };
465 | statements.Add(createTableStatement);
466 |
467 | // Log script
468 | LogExecutionStatement(createTableStatement);
469 | }
470 | }
471 | return statements;
472 | }
473 |
474 | #endregion
475 |
476 | #region Get limit string
477 |
478 | ///
479 | /// Get limit string
480 | ///
481 | /// Offset num
482 | /// Take num
483 | ///
484 | protected override string GetLimitString(int offsetNum, int takeNum, bool hasSort)
485 | {
486 | if (takeNum < 1)
487 | {
488 | return string.Empty;
489 | }
490 | if (offsetNum < 0)
491 | {
492 | offsetNum = 0;
493 | }
494 | return $" LIMIT {offsetNum},{takeNum}";
495 |
496 | }
497 |
498 | #endregion
499 |
500 | #region Get field sql data type
501 |
502 | ///
503 | /// Get sql data type
504 | ///
505 | /// Field
506 | ///
507 | protected override string GetSqlDataType(DataField field, MigrationInfo options)
508 | {
509 | SixnetDirectThrower.ThrowArgNullIf(field == null, nameof(field));
510 | var dbTypeName = "";
511 | if (!string.IsNullOrWhiteSpace(field.DbType))
512 | {
513 | dbTypeName = field.DbType;
514 | }
515 | else
516 | {
517 | var dbType = field.GetDataType().GetDbType();
518 | var length = field.Length;
519 | var precision = field.Precision;
520 | var notFixedLength = options.NotFixedLength || field.HasDbFeature(FieldDbFeature.NotFixedLength);
521 | static int getCharLength(int flength, int defLength) => flength < 1 ? defLength : flength;
522 | switch (dbType)
523 | {
524 | case DbType.Binary:
525 | dbTypeName = $"LONGBLOB";
526 | break;
527 | case DbType.Boolean:
528 | dbTypeName = "BIT";
529 | break;
530 | case DbType.Byte:
531 | dbTypeName = "TINYINT UNSIGNED";
532 | break;
533 | case DbType.SByte:
534 | dbTypeName = "TINYINT";
535 | break;
536 | case DbType.Date:
537 | dbTypeName = "DATE";
538 | break;
539 | case DbType.DateTime:
540 | case DbType.DateTime2:
541 | case DbType.DateTimeOffset:
542 | dbTypeName = "DATETIME(6)";
543 | break;
544 | case DbType.Decimal:
545 | case DbType.Currency:
546 | dbTypeName = $"DECIMAL({(length < 1 ? DefaultDecimalLength : length)}, {(precision < 0 ? DefaultDecimalPrecision : precision)})";
547 | break;
548 | case DbType.Double:
549 | dbTypeName = "DOUBLE";
550 | break;
551 | case DbType.Guid:
552 | dbTypeName = "CHAR(36)";
553 | break;
554 | case DbType.Int16:
555 | dbTypeName = "SMALLINT";
556 | break;
557 | case DbType.UInt16:
558 | dbTypeName = "SMALLINT UNSIGNED";
559 | break;
560 | case DbType.Int32:
561 | dbTypeName = "INT";
562 | break;
563 | case DbType.UInt32:
564 | dbTypeName = "INT UNSIGNED";
565 | break;
566 | case DbType.Int64:
567 | dbTypeName = "BIGINT";
568 | break;
569 | case DbType.UInt64:
570 | dbTypeName = "BIGINT UNSIGNED";
571 | break;
572 | case DbType.Single:
573 | dbTypeName = "FLOAT";
574 | break;
575 | case DbType.AnsiString:
576 | case DbType.AnsiStringFixedLength:
577 | case DbType.String:
578 | case DbType.StringFixedLength:
579 | length = getCharLength(length, DefaultCharLength);
580 | if (length > 16000)
581 | {
582 | dbTypeName = "TEXT";
583 | }
584 | else if(notFixedLength || length >200)
585 | {
586 | dbTypeName = $"VARCHAR({length})";
587 | }
588 | else
589 | {
590 | dbTypeName = $"CHAR({length})";
591 | }
592 | break;
593 | case DbType.Time:
594 | dbTypeName = $"TIME({(length < 1 ? 6 : length)})";
595 | break;
596 | default:
597 | throw new NotSupportedException(dbType.ToString());
598 | }
599 | }
600 | return $" {dbTypeName}";
601 | }
602 |
603 | #endregion
604 | }
605 | }
606 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/MySqlDefaultFieldFormatter.cs:
--------------------------------------------------------------------------------
1 | using Sixnet.Development.Data.Field.Formatting;
2 | using Sixnet.Exceptions;
3 |
4 | namespace Sixnet.Database.MySqlConnector
5 | {
6 | ///
7 | /// Defines default field converter for mysql
8 | ///
9 | internal class MySqlDefaultFieldFormatter : ISixnetFieldFormatter
10 | {
11 | public string Format(FormatFieldContext context)
12 | {
13 | var formatOption = context.FormatSetting;
14 | var formatedFieldName = context.FieldName;
15 | formatedFieldName = formatOption.Name switch
16 | {
17 | FieldFormatterNames.CHARLENGTH => $"CHAR_LENGTH({formatedFieldName})",
18 | FieldFormatterNames.COUNT => $"COUNT({formatedFieldName})",
19 | FieldFormatterNames.SUM => $"SUM({formatedFieldName})",
20 | FieldFormatterNames.MAX => $"MAX({formatedFieldName})",
21 | FieldFormatterNames.MIN => $"MIN({formatedFieldName})",
22 | FieldFormatterNames.AVG => $"AVG({formatedFieldName})",
23 | FieldFormatterNames.JSON_VALUE => $"JSON_EXTRACT({formatedFieldName},{formatOption.Parameter})",
24 | FieldFormatterNames.AND => $"({formatedFieldName}&{formatOption.Parameter})",
25 | FieldFormatterNames.OR => $"({formatedFieldName}|{formatOption.Parameter})",
26 | FieldFormatterNames.XOR => $"({formatedFieldName}^{formatOption.Parameter})",
27 | FieldFormatterNames.NOT => $"(~{formatedFieldName})",
28 | FieldFormatterNames.ADD => $"({formatedFieldName}+{formatOption.Parameter})",
29 | FieldFormatterNames.SUBTRACT => $"({formatedFieldName}-{formatOption.Parameter})",
30 | FieldFormatterNames.MULTIPLY => $"({formatedFieldName}*{formatOption.Parameter})",
31 | FieldFormatterNames.DIVIDE => $"({formatedFieldName}/{formatOption.Parameter})",
32 | FieldFormatterNames.MODULO => $"({formatedFieldName}%{formatOption.Parameter})",
33 | FieldFormatterNames.LEFT_SHIFT => $"({formatedFieldName}<<{formatOption.Parameter})",
34 | FieldFormatterNames.RIGHT_SHIFT => $"({formatedFieldName}>>{formatOption.Parameter})",
35 | FieldFormatterNames.TRIM => $"TRIM({formatedFieldName})",
36 | FieldFormatterNames.STRING_CONCAT => $"(CONCAT({formatedFieldName},{formatOption.Parameter}))",
37 | _ => throw new SixnetException($"{MySqlManager.CurrentDatabaseServerType} does not support field formatter: {formatOption.Name}"),
38 | };
39 | return formatedFieldName;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/MySqlManager.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using MySqlConnector;
3 | using Sixnet.Development.Data;
4 | using Sixnet.Development.Data.Database;
5 |
6 | namespace Sixnet.Database.MySqlConnector
7 | {
8 | ///
9 | /// Defines mysql manager
10 | ///
11 | internal static class MySqlManager
12 | {
13 | #region Fields
14 |
15 | ///
16 | /// Gets current database server type
17 | ///
18 | internal const DatabaseType CurrentDatabaseServerType = DatabaseType.MySQL;
19 |
20 | ///
21 | /// Key word prefix
22 | ///
23 | internal const string KeywordPrefix = "`";
24 |
25 | ///
26 | /// Key word suffix
27 | ///
28 | internal const string KeywordSuffix = "`";
29 |
30 | ///
31 | /// Default query translator
32 | ///
33 | static readonly MySqlDataCommandResolver DefaultResolver = new MySqlDataCommandResolver();
34 |
35 | #endregion
36 |
37 | #region Get database connection
38 |
39 | ///
40 | /// Get a database connection
41 | ///
42 | /// Database server
43 | /// Return database connection
44 | public static IDbConnection GetConnection(DatabaseServer server)
45 | {
46 | return SixnetDataManager.GetDatabaseConnection(server) ?? new MySqlConnection(server.ConnectionString);
47 | }
48 |
49 | #endregion
50 |
51 | #region Get command resolver
52 |
53 | ///
54 | /// Get command resolver
55 | ///
56 | /// Return a command resolver
57 | internal static MySqlDataCommandResolver GetCommandResolver()
58 | {
59 | return DefaultResolver;
60 | }
61 |
62 | #endregion
63 |
64 | #region Wrap keyword
65 |
66 | ///
67 | /// Wrap keyword by the KeywordPrefix and the KeywordSuffix
68 | ///
69 | /// Original value
70 | ///
71 | internal static string WrapKeyword(string originalValue)
72 | {
73 | return $"{KeywordPrefix}{originalValue}{KeywordSuffix}";
74 | }
75 |
76 | #endregion
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/MySqlProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Globalization;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using MySqlConnector;
10 | using Sixnet.App;
11 | using Sixnet.Code;
12 | using Sixnet.Development.Data.Command;
13 | using Sixnet.Development.Data.Dapper;
14 | using Sixnet.Development.Data.Database;
15 | using Sixnet.Exceptions;
16 |
17 | namespace Sixnet.Database.MySqlConnector
18 | {
19 | ///
20 | /// Defines database provider implementation for mysql database(8.0+)
21 | ///
22 | public class MySqlProvider : BaseDatabaseProvider
23 | {
24 | #region Constructor
25 |
26 | public MySqlProvider()
27 | {
28 | queryDatabaseTablesScript = "SELECT TABLE_NAME AS TableName FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='{0}' AND TABLE_TYPE='BASE TABLE';";
29 | }
30 |
31 | #endregion
32 |
33 | #region Connection
34 |
35 | ///
36 | /// Get database connection
37 | ///
38 | /// Database server
39 | ///
40 | public override IDbConnection GetDbConnection(DatabaseServer server)
41 | {
42 | return MySqlManager.GetConnection(server);
43 | }
44 |
45 | #endregion
46 |
47 | #region Data command resolver
48 |
49 | ///
50 | /// Get data command resolver
51 | ///
52 | ///
53 | protected override ISixnetDataCommandResolver GetDataCommandResolver()
54 | {
55 | return MySqlManager.GetCommandResolver();
56 | }
57 |
58 | #endregion
59 |
60 | #region Parameter
61 |
62 | ///
63 | /// Convert data command parametes
64 | ///
65 | /// Data command parameters
66 | ///
67 | protected override DynamicParameters ConvertDataCommandParameters(DataCommandParameters parameters)
68 | {
69 | return parameters?.ConvertToDynamicParameters(MySqlManager.CurrentDatabaseServerType);
70 | }
71 |
72 | #endregion
73 |
74 | #region Bulk
75 |
76 | ///
77 | /// Bulk insert datas
78 | ///
79 | /// Database bulk insert command
80 | public override async Task BulkInsertAsync(BulkInsertDatabaseCommand command)
81 | {
82 | var dataTable = command.DataTable;
83 | if (dataTable == null)
84 | {
85 | throw new ArgumentNullException(nameof(dataTable));
86 | }
87 | var bulkInsertOptions = command.BulkInsertionOptions;
88 | var dbConnection = command.Connection.DbConnection as MySqlConnection;
89 | var dataFilePath = WriteTableToFile(dataTable, Path.Combine(Directory.GetCurrentDirectory(), "temp"), ignoreTitle: true);
90 | if (string.IsNullOrWhiteSpace(dataFilePath))
91 | {
92 | throw new SixnetException("Failed to generate temporary data file");
93 | }
94 | dataFilePath = Path.Combine(SixnetApplication.RootPath, dataFilePath);
95 | var loader = new MySqlBulkLoader(dbConnection)
96 | {
97 | Local = true,
98 | TableName = dataTable.TableName,
99 | FieldTerminator = CultureInfo.CurrentCulture.TextInfo.ListSeparator,
100 | LineTerminator = Environment.NewLine,
101 | FileName = dataFilePath,
102 | NumberOfLinesToSkip = 0
103 | };
104 | if (bulkInsertOptions is MySqlBulkInsertionOptions mySqlBulkInsertOptions)
105 | {
106 | loader.Priority = mySqlBulkInsertOptions.Priority;
107 | loader.ConflictOption = mySqlBulkInsertOptions.ConflictOption;
108 | loader.EscapeCharacter = mySqlBulkInsertOptions.EscapeCharacter;
109 | loader.FieldQuotationOptional = mySqlBulkInsertOptions.FieldQuotationOptional;
110 | loader.FieldQuotationCharacter = mySqlBulkInsertOptions.FieldQuotationCharacter;
111 | loader.LineTerminator = mySqlBulkInsertOptions.LineTerminator;
112 | loader.FieldTerminator = mySqlBulkInsertOptions.FieldTerminator;
113 | if (!string.IsNullOrWhiteSpace(mySqlBulkInsertOptions.LinePrefix))
114 | {
115 | loader.LinePrefix = mySqlBulkInsertOptions.LinePrefix;
116 | }
117 | if (mySqlBulkInsertOptions.NumberOfLinesToSkip >= 0)
118 | {
119 | loader.NumberOfLinesToSkip = mySqlBulkInsertOptions.NumberOfLinesToSkip;
120 | }
121 | if (mySqlBulkInsertOptions.Columns.IsNullOrEmpty())
122 | {
123 | loader.Columns.AddRange(mySqlBulkInsertOptions.Columns);
124 | }
125 | if (mySqlBulkInsertOptions.Timeout > 0)
126 | {
127 | loader.Timeout = mySqlBulkInsertOptions.Timeout;
128 | }
129 | if (!string.IsNullOrWhiteSpace(mySqlBulkInsertOptions.CharacterSet))
130 | {
131 | loader.CharacterSet = mySqlBulkInsertOptions.CharacterSet;
132 | }
133 | }
134 | if (loader.Columns.IsNullOrEmpty())
135 | {
136 | loader.Columns.AddRange(dataTable.Columns.Cast().Select(c => c.ColumnName));
137 | }
138 | await loader.LoadAsync().ConfigureAwait(false);
139 | }
140 |
141 | ///
142 | /// Bulk insert datas
143 | ///
144 | /// Database bulk insert command
145 | public override void BulkInsert(BulkInsertDatabaseCommand command)
146 | {
147 | var dataTable = command.DataTable;
148 | if (dataTable == null)
149 | {
150 | throw new ArgumentNullException(nameof(dataTable));
151 | }
152 | var bulkInsertOptions = command.BulkInsertionOptions;
153 | var dbConnection = command.Connection.DbConnection as MySqlConnection;
154 | var dataFilePath = WriteTableToFile(dataTable, Path.Combine(Directory.GetCurrentDirectory(), "temp"), ignoreTitle: true);
155 | if (string.IsNullOrWhiteSpace(dataFilePath))
156 | {
157 | throw new SixnetException("Failed to generate temporary data file");
158 | }
159 | dataFilePath = Path.Combine(SixnetApplication.RootPath, dataFilePath);
160 | var loader = new MySqlBulkLoader(dbConnection)
161 | {
162 | Local = true,
163 | TableName = dataTable.TableName,
164 | FieldTerminator = CultureInfo.CurrentCulture.TextInfo.ListSeparator,
165 | LineTerminator = Environment.NewLine,
166 | FileName = dataFilePath,
167 | NumberOfLinesToSkip = 0
168 | };
169 | if (bulkInsertOptions is MySqlBulkInsertionOptions mySqlBulkInsertOptions)
170 | {
171 | loader.Priority = mySqlBulkInsertOptions.Priority;
172 | loader.ConflictOption = mySqlBulkInsertOptions.ConflictOption;
173 | loader.EscapeCharacter = mySqlBulkInsertOptions.EscapeCharacter;
174 | loader.FieldQuotationOptional = mySqlBulkInsertOptions.FieldQuotationOptional;
175 | loader.FieldQuotationCharacter = mySqlBulkInsertOptions.FieldQuotationCharacter;
176 | loader.LineTerminator = mySqlBulkInsertOptions.LineTerminator;
177 | loader.FieldTerminator = mySqlBulkInsertOptions.FieldTerminator;
178 | if (!string.IsNullOrWhiteSpace(mySqlBulkInsertOptions.LinePrefix))
179 | {
180 | loader.LinePrefix = mySqlBulkInsertOptions.LinePrefix;
181 | }
182 | if (mySqlBulkInsertOptions.NumberOfLinesToSkip >= 0)
183 | {
184 | loader.NumberOfLinesToSkip = mySqlBulkInsertOptions.NumberOfLinesToSkip;
185 | }
186 | if (mySqlBulkInsertOptions.Columns.IsNullOrEmpty())
187 | {
188 | loader.Columns.AddRange(mySqlBulkInsertOptions.Columns);
189 | }
190 | if (mySqlBulkInsertOptions.Timeout > 0)
191 | {
192 | loader.Timeout = mySqlBulkInsertOptions.Timeout;
193 | }
194 | if (!string.IsNullOrWhiteSpace(mySqlBulkInsertOptions.CharacterSet))
195 | {
196 | loader.CharacterSet = mySqlBulkInsertOptions.CharacterSet;
197 | }
198 | }
199 | if (loader.Columns.IsNullOrEmpty())
200 | {
201 | loader.Columns.AddRange(dataTable.Columns.Cast().Select(c => c.ColumnName));
202 | }
203 | loader.Load();
204 | }
205 |
206 | ///
207 | /// Write datatable to a csv file
208 | ///
209 | /// Data table
210 | /// Save path,Will save file to the application root directory when the parameter value is null or empty
211 | /// Without extension file name,Will use a random file name when the parameter value is null or empty
212 | /// Whether ignore column name,Default is false
213 | /// Return the file full path
214 | string WriteTableToFile(DataTable dataTable, string savePath = "", string fileName = "", bool ignoreTitle = false)
215 | {
216 | if (dataTable == null)
217 | {
218 | throw new ArgumentNullException(nameof(dataTable));
219 | }
220 | if (string.IsNullOrWhiteSpace(fileName))
221 | {
222 | fileName = GuidHelper.GetGuidUniqueCode().ToLower();
223 | }
224 | if (!Directory.Exists(savePath))
225 | {
226 | Directory.CreateDirectory(savePath);
227 | }
228 | var filePath = Path.Combine(savePath, $"{fileName}.csv");
229 | using (var sw = new StreamWriter(filePath, false, new UTF8Encoding(false)))
230 | {
231 | var lineDatas = new List(dataTable.Columns.Count);
232 | if (!ignoreTitle)
233 | {
234 | foreach (DataColumn col in dataTable.Columns)
235 | {
236 | lineDatas.Add(col.ColumnName);
237 | }
238 | var titles = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, lineDatas);
239 | sw.WriteLine(titles);
240 | }
241 | var columnCount = dataTable.Columns.Count;
242 | foreach (DataRow row in dataTable.Rows)
243 | {
244 | lineDatas.Clear();
245 | for (int i = 0; i < columnCount; i++)
246 | {
247 | var rowValue = Convert.IsDBNull(row[i]) ? string.Empty : row[i].ToString();
248 | if (dataTable.Columns[i].DataType == typeof(bool))
249 | {
250 | rowValue = string.Equals(rowValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) ? "1" : string.Empty;
251 | }
252 | if (dataTable.Columns[i].DataType == typeof(DateTimeOffset))
253 | {
254 | rowValue = ((DateTimeOffset)row[i]).LocalDateTime.ToString();
255 | }
256 | lineDatas.Add(rowValue);
257 | }
258 | sw.WriteLine(string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, lineDatas));
259 | }
260 | sw.Close();
261 | sw.Dispose();
262 | }
263 | return filePath;
264 | }
265 |
266 | #endregion
267 |
268 | #region Get tables
269 |
270 | ///
271 | /// Get tables
272 | ///
273 | /// Command
274 | ///
275 | public override List GetTables(DatabaseCommand command)
276 | {
277 | return command.Connection.DbConnection.Query(string.Format(queryDatabaseTablesScript, command.Connection.DbConnection.Database)).ToList();
278 | }
279 |
280 | ///
281 | /// Get tables
282 | ///
283 | /// Command
284 | ///
285 | public override async Task> GetTablesAsync(DatabaseCommand command)
286 | {
287 | return (await command.Connection.DbConnection.QueryAsync(string.Format(queryDatabaseTablesScript, command.Connection.DbConnection.Database)).ConfigureAwait(false)).ToList();
288 | }
289 |
290 | #endregion
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/Sixnet.Database.MySqlConnector.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | true
6 | sixnet.snk
7 | DingBin.Li
8 | Copyright © DingBin.Li
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Sixnet.Database.MySqlConnector/sixnet.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/six-net/Sixnet.Database.MySqlConnector/f968ebd8c61658af63cfdd4959e81e79e3569c67/Sixnet.Database.MySqlConnector/sixnet.snk
--------------------------------------------------------------------------------
/nugeticon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/six-net/Sixnet.Database.MySqlConnector/f968ebd8c61658af63cfdd4959e81e79e3569c67/nugeticon.png
--------------------------------------------------------------------------------