From 25adf8183ada8b98bff1e1818404f09ba22e0d3b Mon Sep 17 00:00:00 2001 From: Joey Bradshaw Date: Sat, 8 Apr 2023 00:53:26 -0500 Subject: [PATCH 1/3] allow proper C# definition for UTC based columns. allow a value generator to be specified for date/time columns. --- docs/configuration.md | 14 +++++---- .../Options/MappingClassOptions.cs | 19 ++++++++++-- .../Templates/MappingClassTemplate.cs | 30 +++++++++++++++++-- .../EntityFrameworkCore.Generator.csproj | 5 +++- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 85dd26d3..be62e852 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -43,7 +43,7 @@ database: # schemas to include or empty to include all schemas: - dbo - + # list of expressions for tables to exclude, source is Schema.TableName exclude: - exact: dbo.SchemaVersions @@ -90,6 +90,8 @@ data: directory: '{Project.Directory}\Data\Mapping' # the mapping class output directory #include XML documentation document: false + dateTimeKind: Local|Utc # configures the conversion of date/time columns as UTC in .Net. Default: Local + dateTimeDefaultValueGenerator: UtcValueGenerator # defines the default date/time value generator. Default: string.Empty # query extension class file configuration query: @@ -172,11 +174,11 @@ model: # script templates script: # collection script template with EntityContext as a variable - context: + context: - templatePath: '.\templates\context.csx' # path to script file fileName: 'ContextScript.cs' # filename to save script output directory: '{Project.Directory}\Domain\Context' # directory to save script output - namespace: '{Project.Namespace}.Domain.Context' + namespace: '{Project.Namespace}.Domain.Context' baseClass: ContextScriptBase overwrite: true # overwrite existing file # collection of script template with current Entity as a variable @@ -184,7 +186,7 @@ script: - templatePath: '.\templates\entity.csx' # path to script file fileName: '{Entity.Name}Script.cs' # filename to save script output directory: '{Project.Directory}\Domain\Entity' # directory to save script output - namespace: '{Project.Namespace}.Domain.Entity' + namespace: '{Project.Namespace}.Domain.Entity' baseClass: EntityScriptBase overwrite: true # overwrite existing file # collection script template with current Model as a variable @@ -192,13 +194,13 @@ script: - templatePath: '.\templates\model.csx' # path to script file fileName: '{Model.Name}Script.cs' # filename to save script output directory: '{Project.Directory}\Domain\Models' # directory to save script output - namespace: '{Project.Namespace}.Domain.Models' + namespace: '{Project.Namespace}.Domain.Models' baseClass: ModelScriptBase overwrite: true # overwrite existing file - templatePath: '.\templates\sample.csx' # path to script file fileName: '{Model.Name}Sample.cs' # filename to save script output directory: '{Project.Directory}\Domain\Models' # directory to save script output - namespace: '{Project.Namespace}.Domain.Models' + namespace: '{Project.Namespace}.Domain.Models' baseClass: ModelSampleBase overwrite: true # overwrite existing file ``` diff --git a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs index 1fd84530..a033c55f 100644 --- a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs +++ b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs @@ -1,4 +1,7 @@ -namespace EntityFrameworkCore.Generator.Options; +using System; +using System.ComponentModel; + +namespace EntityFrameworkCore.Generator.Options; /// /// EntityFramework mapping class generation options @@ -15,4 +18,16 @@ public MappingClassOptions(VariableDictionary variables, string prefix) Namespace = "{Project.Namespace}.Data.Mapping"; Directory = @"{Project.Directory}\Data\Mapping"; } -} \ No newline at end of file + + /// + /// Specifies the to use for date/time columns. + /// + [DefaultValue(DateTimeKind.Local)] + public DateTimeKind DateTimeKind { get; set; } + + /// + /// The name of the class that will generate default values for date/time columns that have default value defined in the database. + /// + [DefaultValue(DateTimeKind.Local)] + public string DateTimeDefaultValueGenerator { get; set; } +} diff --git a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs index d870b261..cbb1199c 100644 --- a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs +++ b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs @@ -1,8 +1,11 @@ +using System; using System.Globalization; using System.Linq; + using EntityFrameworkCore.Generator.Extensions; using EntityFrameworkCore.Generator.Metadata.Generation; using EntityFrameworkCore.Generator.Options; + using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; @@ -104,7 +107,7 @@ private void GenerateConstants() CodeBuilder.AppendLine($"public const string Name = \"{_entity.TableName}\";"); } - + CodeBuilder.AppendLine("}"); CodeBuilder.AppendLine(); @@ -286,6 +289,29 @@ private void GeneratePropertyMapping(Property property) CodeBuilder.Append($".HasDefaultValueSql({property.Default.ToLiteral()})"); } + + if (property.SystemType == typeof(DateTime)) + { + if (Options.Data.Mapping.DateTimeKind == DateTimeKind.Utc) + { + CodeBuilder.AppendLine(); + if (property.IsNullable == false) + { + CodeBuilder.Append($".HasConversion(t => t, t => DateTime.SpecifyKind(t, DateTimeKind.Utc))"); + } + else + { + CodeBuilder.Append($".HasConversion(t => t, t => t == null ? null : DateTime.SpecifyKind(t.GetValueOrDefault(), DateTimeKind.Utc))"); + } + } + + if (!string.IsNullOrEmpty(property.Default) && string.IsNullOrEmpty(Options.Data.Mapping.DateTimeDefaultValueGenerator) == false) + { + CodeBuilder.AppendLine(); + CodeBuilder.Append($".HasValueGenerator<{Options.Data.Mapping.DateTimeDefaultValueGenerator}>()"); + } + } + switch (property.ValueGenerated) { case ValueGenerated.OnAdd: @@ -362,4 +388,4 @@ private void GenerateTableMapping() CodeBuilder.AppendLine(); } -} \ No newline at end of file +} diff --git a/src/EntityFrameworkCore.Generator/EntityFrameworkCore.Generator.csproj b/src/EntityFrameworkCore.Generator/EntityFrameworkCore.Generator.csproj index e6f4512f..61ad0464 100644 --- a/src/EntityFrameworkCore.Generator/EntityFrameworkCore.Generator.csproj +++ b/src/EntityFrameworkCore.Generator/EntityFrameworkCore.Generator.csproj @@ -15,7 +15,10 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 80f03091c2aeced9314c6c6017e3595baa3a0423 Mon Sep 17 00:00:00 2001 From: Joey Bradshaw Date: Sat, 8 Apr 2023 01:04:57 -0500 Subject: [PATCH 2/3] automatic UTC date/time detection based on a column's default value. --- .../Templates/MappingClassTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs index cbb1199c..6236804e 100644 --- a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs +++ b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs @@ -292,7 +292,7 @@ private void GeneratePropertyMapping(Property property) if (property.SystemType == typeof(DateTime)) { - if (Options.Data.Mapping.DateTimeKind == DateTimeKind.Utc) + if (Options.Data.Mapping.DateTimeKind == DateTimeKind.Utc || property.Default?.IndexOf("utc", StringComparison.OrdinalIgnoreCase) > -1) { CodeBuilder.AppendLine(); if (property.IsNullable == false) From bbdcbaae09ca22178d4a03c141c02be4c9c3af10 Mon Sep 17 00:00:00 2001 From: Joey Bradshaw Date: Sat, 8 Apr 2023 01:08:03 -0500 Subject: [PATCH 3/3] set defaults. --- .../Options/MappingClassOptions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs index a033c55f..c33d3ff8 100644 --- a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs +++ b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs @@ -23,11 +23,11 @@ public MappingClassOptions(VariableDictionary variables, string prefix) /// Specifies the to use for date/time columns. /// [DefaultValue(DateTimeKind.Local)] - public DateTimeKind DateTimeKind { get; set; } + public DateTimeKind DateTimeKind { get; set; } = DateTimeKind.Local; /// /// The name of the class that will generate default values for date/time columns that have default value defined in the database. /// [DefaultValue(DateTimeKind.Local)] - public string DateTimeDefaultValueGenerator { get; set; } + public string DateTimeDefaultValueGenerator { get; set; } = string.Empty; }