diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d4788f1..1490b7b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -17,7 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 9.x + dotnet-version: 10.x - name: Version (release) run: | OLD=$(yq ".Project.PropertyGroup[0].Version" -p xml -o xml src/Pingmint.CodeGen.Sql/Pingmint.CodeGen.Sql.csproj) diff --git a/README.md b/README.md index fd5638d..8eed94a 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,10 @@ sqlclient: async: false ``` +### `trim char` + +Set `trim char: true` to remove trailing spaces on CHAR(n) columns. + ## Extended properties Extended properties may be added to database objects to include metadata in the generated code. diff --git a/src/Pingmint.CodeGen.Sql/Analyzer.cs b/src/Pingmint.CodeGen.Sql/Analyzer.cs index 9209b27..77cdeb7 100644 --- a/src/Pingmint.CodeGen.Sql/Analyzer.cs +++ b/src/Pingmint.CodeGen.Sql/Analyzer.cs @@ -328,6 +328,7 @@ public async Task AnalyzeStatementAsync(String database, String name, String com var propertyType = isNullable ? csharpTypeInfo.TypeRefNullable : csharpTypeInfo.TypeRef; var propertyTypeWithoutNullable = csharpTypeInfo.TypeRef; var isValueType = csharpTypeInfo.IsValueType; + var sqlDbType = csharpTypeInfo.SqlDbType; var recordProperty = new RecordProperty { @@ -337,6 +338,7 @@ public async Task AnalyzeStatementAsync(String database, String name, String com FieldTypeIsValueType = isValueType, ColumnName = columnName, ColumnIsNullable = isNullable, + SqlDbType = sqlDbType, }; return recordProperty; } diff --git a/src/Pingmint.CodeGen.Sql/CodeFile.cs b/src/Pingmint.CodeGen.Sql/CodeFile.cs index 5751f6c..952f946 100644 --- a/src/Pingmint.CodeGen.Sql/CodeFile.cs +++ b/src/Pingmint.CodeGen.Sql/CodeFile.cs @@ -29,6 +29,7 @@ public class CodeFile public String TypeKeyword { get; set; } = "record class"; public Boolean AllowAsync { get; set; } = true; + public Boolean TrimChar { get; set; } = false; public String GenerateCode() { @@ -140,6 +141,15 @@ public String GenerateCode() int i = 1; foreach (var property in record.Properties) { + String trim = String.Empty; + if (TrimChar && property.SqlDbType == SqlDbType.Char) + { + trim = ".TrimEnd()"; + if (property.ColumnIsNullable) + { + trim = "?" + trim; + } + } var fieldName = property.FieldName; var IsValueType = property.FieldTypeIsValueType; var ColumnIsNullable = property.ColumnIsNullable; @@ -148,10 +158,10 @@ public String GenerateCode() var ordinalVarName = record.Properties.Count == 1 ? ordinalsArg : $"{ordinalsArg}.Item{i++}"; var line = (IsValueType, ColumnIsNullable) switch { - (false, true) => String.Format("{0} = OptionalClass<{2}>(reader, {1}),", fieldName, ordinalVarName, fieldTypeWithoutNullable), - (true, true) => String.Format("{0} = OptionalValue<{2}>(reader, {1}),", fieldName, ordinalVarName, fieldTypeWithoutNullable), - (false, false) => String.Format("{0} = RequiredClass<{2}>(reader, {1}),", fieldName, ordinalVarName, fieldTypeWithoutNullable), - (true, false) => String.Format("{0} = RequiredValue<{2}>(reader, {1}),", fieldName, ordinalVarName, fieldTypeWithoutNullable), + (false, true) => String.Format("{0} = OptionalClass<{2}>(reader, {1}){3},", fieldName, ordinalVarName, fieldTypeWithoutNullable, trim), + (true, true) => String.Format("{0} = OptionalValue<{2}>(reader, {1}){3},", fieldName, ordinalVarName, fieldTypeWithoutNullable, trim), + (false, false) => String.Format("{0} = RequiredClass<{2}>(reader, {1}){3},", fieldName, ordinalVarName, fieldTypeWithoutNullable, trim), + (true, false) => String.Format("{0} = RequiredValue<{2}>(reader, {1}){3},", fieldName, ordinalVarName, fieldTypeWithoutNullable, trim), }; code.Line(line); } diff --git a/src/Pingmint.CodeGen.Sql/Model/Databases.cs b/src/Pingmint.CodeGen.Sql/Model/Databases.cs index 03190e3..71bf4fd 100644 --- a/src/Pingmint.CodeGen.Sql/Model/Databases.cs +++ b/src/Pingmint.CodeGen.Sql/Model/Databases.cs @@ -41,6 +41,7 @@ public class SqlClient // mapping public String? Async { get; set; } + public String? TrimChar { get; set; } // sequence } diff --git a/src/Pingmint.CodeGen.Sql/Pingmint.CodeGen.Sql.csproj b/src/Pingmint.CodeGen.Sql/Pingmint.CodeGen.Sql.csproj index 191a1d4..a680669 100644 --- a/src/Pingmint.CodeGen.Sql/Pingmint.CodeGen.Sql.csproj +++ b/src/Pingmint.CodeGen.Sql/Pingmint.CodeGen.Sql.csproj @@ -1,9 +1,9 @@ - 0.46 + 0.47 Exe - net9.0 + net10.0 latest enable enable @@ -35,7 +35,7 @@ - + diff --git a/src/Pingmint.CodeGen.Sql/Program.cs b/src/Pingmint.CodeGen.Sql/Program.cs index 5d7da8a..5b46149 100644 --- a/src/Pingmint.CodeGen.Sql/Program.cs +++ b/src/Pingmint.CodeGen.Sql/Program.cs @@ -42,6 +42,7 @@ internal static async Task Main(string[] args) codeFile.ClassName = config.CSharp.ClassName; codeFile.TypeKeyword = config.CSharp.TypeKeyword; codeFile.AllowAsync = config?.SqlClient?.Async != "false"; + codeFile.TrimChar = config?.SqlClient?.TrimChar?.ToUpperInvariant() == "TRUE"; if (config?.Databases?.Items is { } databases) { diff --git a/src/Pingmint.CodeGen.Sql/Types.cs b/src/Pingmint.CodeGen.Sql/Types.cs index d8aabae..9e49177 100644 --- a/src/Pingmint.CodeGen.Sql/Types.cs +++ b/src/Pingmint.CodeGen.Sql/Types.cs @@ -132,4 +132,5 @@ public class RecordProperty public bool FieldTypeIsValueType { get; internal set; } public bool ColumnIsNullable { get; internal set; } public short? MaxLength { get; init; } + public SqlDbType? SqlDbType { get; init; } } diff --git a/src/Pingmint.CodeGen.Sql/Yaml/Serialization.cs b/src/Pingmint.CodeGen.Sql/Yaml/Serialization.cs index ae5eed3..fda28d2 100644 --- a/src/Pingmint.CodeGen.Sql/Yaml/Serialization.cs +++ b/src/Pingmint.CodeGen.Sql/Yaml/Serialization.cs @@ -65,6 +65,7 @@ protected override bool Add(string key, string value) switch (key) { case "async": this.Model.Async = value; return true; + case "trim char": this.Model.TrimChar = value; return true; default: return false; } } diff --git a/src/Pingmint.CodeGen.Sql/packages.lock.json b/src/Pingmint.CodeGen.Sql/packages.lock.json index 65e44c0..bb5f704 100644 --- a/src/Pingmint.CodeGen.Sql/packages.lock.json +++ b/src/Pingmint.CodeGen.Sql/packages.lock.json @@ -1,19 +1,20 @@ { "version": 1, "dependencies": { - "net9.0": { + "net10.0": { "Microsoft.Data.SqlClient": { "type": "Direct", - "requested": "[6.0.2, )", - "resolved": "6.0.2", - "contentHash": "RDqwzNu5slSqGy0eSgnN4fuLdGI1w9ZHBRNALrbUsykOIbXtGCpyotG0r5zz+HHtzxbe6LtcAyWcOiu0a+Fx/A==", + "requested": "[6.1.3, )", + "resolved": "6.1.3", + "contentHash": "ys/z8Tx8074CDU20EilNvBRJuJdwKSthpHkzUpt3JghnjB6GjbZusoOcCtNbhPCCWsEJqN8bxaT7HnS3UZuUDQ==", "dependencies": { - "Azure.Identity": "1.11.4", + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", "Microsoft.Bcl.Cryptography": "9.0.4", "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", "Microsoft.Extensions.Caching.Memory": "9.0.4", - "Microsoft.IdentityModel.JsonWebTokens": "7.5.0", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.5.0", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", "Microsoft.SqlServer.Server": "1.0.0", "System.Configuration.ConfigurationManager": "9.0.4", "System.Security.Cryptography.Pkcs": "9.0.4" @@ -27,37 +28,28 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.38.0", - "contentHash": "IuEgCoVA0ef7E4pQtpC3+TkPbzaoQfa77HlfJDmfuaJUCVJmn7fT0izamZiryW5sYUFKizsftIxMkXKbgIcPMQ==", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "System.ClientModel": "1.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Memory.Data": "1.0.2", - "System.Numerics.Vectors": "4.5.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" } }, "Azure.Identity": { "type": "Transitive", - "resolved": "1.11.4", - "contentHash": "Sf4BoE6Q3jTgFkgBkx7qztYOFELBCo+wQgpYDwal/qJ1unBH73ywPztIJKXBXORRzAeNijsuxhk94h0TIMvfYg==", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", "dependencies": { - "Azure.Core": "1.38.0", - "Microsoft.Identity.Client": "4.61.3", - "Microsoft.Identity.Client.Extensions.Msal": "4.61.3", - "System.Memory": "4.5.4", - "System.Security.Cryptography.ProtectedData": "4.7.0", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, "Microsoft.Bcl.Cryptography": { "type": "Transitive", @@ -118,66 +110,65 @@ }, "Microsoft.Identity.Client": { "type": "Transitive", - "resolved": "4.61.3", - "contentHash": "naJo/Qm35Caaoxp5utcw+R8eU8ZtLz2ALh8S+gkekOYQ1oazfCQMWVT4NJ/FnHzdIJlm8dMz0oMpMGCabx5odA==", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0", - "System.Diagnostics.DiagnosticSource": "6.0.1" + "Microsoft.IdentityModel.Abstractions": "6.35.0" } }, "Microsoft.Identity.Client.Extensions.Msal": { "type": "Transitive", - "resolved": "4.61.3", - "contentHash": "PWnJcznrSGr25MN8ajlc2XIDW4zCFu0U6FkpaNLEWLgd1NgFCp5uDY3mqLDgM8zCN8hqj8yo5wHYfLB2HjcdGw==", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", "dependencies": { - "Microsoft.Identity.Client": "4.61.3", + "Microsoft.Identity.Client": "4.73.1", "System.Security.Cryptography.ProtectedData": "4.5.0" } }, "Microsoft.IdentityModel.Abstractions": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "seOFPaBQh2K683eFujAuDsrO2XbOA+SvxRli+wu7kl+ZymuGQzjmmUKfyFHmDazpPOBnmOX1ZnjX7zFDZHyNIA==" + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" }, "Microsoft.IdentityModel.JsonWebTokens": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "mfyiGptbcH+oYrzAtWWwuV+7MoM0G0si+9owaj6DGWInhq/N/KDj/pWHhq1ShdmBu332gjP+cppjgwBpsOj7Fg==", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.5.0" + "Microsoft.IdentityModel.Tokens": "7.7.1" } }, "Microsoft.IdentityModel.Logging": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "3BInZEajJvnTDP/YRrmJ3Fyw8XAWWR9jG+3FkhhzRJJYItVL+BEH9qlgxSmtrxp7S7N6TOv+Y+X8BG61viiehQ==", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", "dependencies": { - "Microsoft.IdentityModel.Abstractions": "7.5.0" + "Microsoft.IdentityModel.Abstractions": "7.7.1" } }, "Microsoft.IdentityModel.Protocols": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "ugyb0Nm+I+UrHGYg28mL8oCV31xZrOEbs8fQkcShUoKvgk22HroD2odCnqEf56CoAFYTwoDExz8deXzrFC+TyA==", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.5.0" + "Microsoft.IdentityModel.Tokens": "7.7.1" } }, "Microsoft.IdentityModel.Protocols.OpenIdConnect": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "/U3I/8uutTqZr2n/zt0q08bluYklq+5VWP7ZuOGpTUR1ln5bSbrexAzdSGzrhxTxNNbHMCU8Mn2bNQvcmehAxg==", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", "dependencies": { - "Microsoft.IdentityModel.Protocols": "7.5.0", - "System.IdentityModel.Tokens.Jwt": "7.5.0" + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" } }, "Microsoft.IdentityModel.Tokens": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "owe33wqe0ZbwBxM3D90I0XotxNyTdl85jud03d+OrUOJNnTiqnYePwBk3WU9yW0Rk5CYX+sfSim7frmu6jeEzQ==", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", "dependencies": { - "Microsoft.IdentityModel.Logging": "7.5.0" + "Microsoft.IdentityModel.Logging": "7.7.1" } }, "Microsoft.SqlServer.Server": { @@ -187,11 +178,11 @@ }, "System.ClientModel": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "I3CVkvxeqFYjIVEP59DnjbeoGNfo/+SZrCLpRz2v/g0gpCHaEMPtWSY0s9k/7jR1rAsLNg2z2u1JRB76tPjnIw==", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", "dependencies": { - "System.Memory.Data": "1.0.2", - "System.Text.Json": "4.7.2" + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" } }, "System.Configuration.ConfigurationManager": { @@ -203,14 +194,6 @@ "System.Security.Cryptography.ProtectedData": "9.0.4" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "9.0.4", @@ -218,36 +201,17 @@ }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", - "resolved": "7.5.0", - "contentHash": "D0TtrWOfoPdyYSlvOGaU9F1QR+qrbgJ/4eiEsQkIz7YQKIKkGXQldXukn6cYG9OahSq5UVMvyAIObECpH6Wglg==", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "7.5.0", - "Microsoft.IdentityModel.Tokens": "7.5.0" + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" } }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, "System.Memory.Data": { "type": "Transitive", - "resolved": "1.0.2", - "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", - "dependencies": { - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.6.0" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" }, "System.Security.Cryptography.Pkcs": { "type": "Transitive", @@ -258,22 +222,7 @@ "type": "Transitive", "resolved": "9.0.4", "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" } } } -} +} \ No newline at end of file