From d6d1906aea32cd06f57e6af7ea4180d786110c7b Mon Sep 17 00:00:00 2001 From: Joey Bradshaw Date: Fri, 7 Apr 2023 18:55:06 -0500 Subject: [PATCH 1/3] add support for defining cascade delete. upgrade ThisAssembly to latest, as the current version/configuration prevented compilation. --- docs/configuration.md | 18 ++++++++++++------ .../ModelGenerator.cs | 5 +++-- .../Options/MappingClassOptions.cs | 15 +++++++++++++-- .../Templates/MappingClassTemplate.cs | 14 ++++++++++++-- .../EntityFrameworkCore.Generator.csproj | 5 ++++- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 85dd26d3..e4a2e466 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,12 @@ data: directory: '{Project.Directory}\Data\Mapping' # the mapping class output directory #include XML documentation document: false + # indicates how a delete operation is applied to dependent entities in a relationship when the + # principal is deleted or the relationship is severed. + # Defaults: + # NoAction - when a foreign key has its Delete Rule set to No Action. + # Cascade - when a foreign key has its Delete Rule set to Cascade. + relationshipDeleteBehavior: ClientSetNull|Restrict|SetNull|Cascade|ClientCascade|NoAction|ClientNoAction # query extension class file configuration query: @@ -172,11 +178,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 +190,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 +198,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/ModelGenerator.cs b/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs index 07890545..103c7628 100644 --- a/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs +++ b/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -259,6 +259,7 @@ private void CreateRelationship(EntityContext entityContext, Entity foreignEntit }; foreignEntity.Relationships.Add(foreignRelationship); } + foreignRelationship.CascadeDelete = tableKeySchema.OnDelete == Microsoft.EntityFrameworkCore.Migrations.ReferentialAction.Cascade; foreignRelationship.IsMapped = true; foreignRelationship.IsForeignKey = true; foreignRelationship.Cardinality = foreignMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne; @@ -679,4 +680,4 @@ private static bool IsIgnored(string name, IEnumerable excludeExpr return false; } -} \ No newline at end of file +} diff --git a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs index 1fd84530..c13ec5c4 100644 --- a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs +++ b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs @@ -1,4 +1,8 @@ -namespace EntityFrameworkCore.Generator.Options; +using System.ComponentModel; + +using Microsoft.EntityFrameworkCore; + +namespace EntityFrameworkCore.Generator.Options; /// /// EntityFramework mapping class generation options @@ -14,5 +18,12 @@ public MappingClassOptions(VariableDictionary variables, string prefix) { Namespace = "{Project.Namespace}.Data.Mapping"; Directory = @"{Project.Directory}\Data\Mapping"; + RelationshipDeleteBehavior = DeleteBehavior.Cascade; } -} \ No newline at end of file + + /// + /// Gets or sets the delete behavior for all foreign keys that have cascade deletes. + /// + [DefaultValue(DeleteBehavior.Cascade)] + public DeleteBehavior RelationshipDeleteBehavior { get; set; } +} diff --git a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs index d870b261..bc345268 100644 --- a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs +++ b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs @@ -1,8 +1,10 @@ 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 +106,7 @@ private void GenerateConstants() CodeBuilder.AppendLine($"public const string Name = \"{_entity.TableName}\";"); } - + CodeBuilder.AppendLine("}"); CodeBuilder.AppendLine(); @@ -229,6 +231,14 @@ private void GenerateRelationshipMapping(Relationship relationship) CodeBuilder.Append("\")"); } + var cascadeOption = $"{nameof(DeleteBehavior)}.{nameof(DeleteBehavior.NoAction)}"; + if (relationship.CascadeDelete == true) + { + cascadeOption = $"{nameof(DeleteBehavior)}.{Options.Data.Mapping.RelationshipDeleteBehavior}"; + } + CodeBuilder.AppendLine(); + CodeBuilder.Append($".OnDelete({cascadeOption})"); + CodeBuilder.DecrementIndent(); CodeBuilder.AppendLine(";"); @@ -362,4 +372,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 0e1d32bcbffb463fbba1f7dc54176314a7705e97 Mon Sep 17 00:00:00 2001 From: Joey Bradshaw Date: Fri, 7 Apr 2023 20:23:24 -0500 Subject: [PATCH 2/3] splitting delete behavior into three buckets for finer global control. --- docs/configuration.md | 22 ++++--- .../Metadata/Generation/Relationship.cs | 8 ++- .../ModelGenerator.cs | 2 +- .../Options/MappingClassOptions.cs | 66 ++++++++++++++++++- .../Templates/MappingClassTemplate.cs | 28 ++++++-- 5 files changed, 105 insertions(+), 21 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index e4a2e466..d0011caa 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -65,7 +65,7 @@ data: # how to generate names for the DbSet properties on the data context. Default: Plural propertyNaming: Preserve|Plural|Suffix - #include XML documentation + # include XML documentation document: false # entity class file configuration @@ -78,7 +78,7 @@ data: # how to generate relationship collections names for the entity. Default: Plural relationshipNaming: Preserve|Plural|Suffix - #include XML documentation + # include XML documentation document: false # Generate class names with prefixed schema name eg. dbo.MyTable = DboMyTable @@ -88,14 +88,16 @@ data: mapping: namespace: '{Project.Namespace}.Data.Mapping' # the mapping class namespace directory: '{Project.Directory}\Data\Mapping' # the mapping class output directory - #include XML documentation + # include XML documentation document: false - # indicates how a delete operation is applied to dependent entities in a relationship when the - # principal is deleted or the relationship is severed. - # Defaults: - # NoAction - when a foreign key has its Delete Rule set to No Action. - # Cascade - when a foreign key has its Delete Rule set to Cascade. - relationshipDeleteBehavior: ClientSetNull|Restrict|SetNull|Cascade|ClientCascade|NoAction|ClientNoAction + # globally indicates how a delete operation is applied to dependent entities in a relationship + # when the principal is deleted or the relationship is severed. + # Default: Cascade + globalRelationshipCascadeDeleteBehavior: Cascade|ClientCascade + # Default: SetNull + globalRelationshipSetNullDeleteBehavior: SetNull|ClientSetNull + # Default: NoAction + globalRelationshipNoActionDeleteBehavior: NoAction|ClientNoAction # query extension class file configuration query: @@ -104,7 +106,7 @@ data: uniquePrefix: GetBy # Prefix for queries built from unique indexes namespace: '{Project.Namespace}.Data.Queries' # the mapping class namespace directory: '{Project.Directory}\Data\Queries' # the mapping class output directory - #include XML documentation + # include XML documentation document: false #---------------------------------# diff --git a/src/EntityFrameworkCore.Generator.Core/Metadata/Generation/Relationship.cs b/src/EntityFrameworkCore.Generator.Core/Metadata/Generation/Relationship.cs index a1666ac6..56cff1a9 100644 --- a/src/EntityFrameworkCore.Generator.Core/Metadata/Generation/Relationship.cs +++ b/src/EntityFrameworkCore.Generator.Core/Metadata/Generation/Relationship.cs @@ -1,4 +1,6 @@ -using System.Diagnostics; +using System.Diagnostics; + +using Microsoft.EntityFrameworkCore.Migrations; namespace EntityFrameworkCore.Generator.Metadata.Generation; @@ -32,9 +34,9 @@ public Relationship() public Cardinality PrimaryCardinality { get; set; } - public bool? CascadeDelete { get; set; } + public ReferentialAction? ReferentialAction { get; set; } public bool IsForeignKey { get; set; } public bool IsMapped { get; set; } public bool IsOneToOne => Cardinality != Cardinality.Many && PrimaryCardinality != Cardinality.Many; -} \ No newline at end of file +} diff --git a/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs b/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs index 103c7628..91042870 100644 --- a/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs +++ b/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs @@ -259,7 +259,7 @@ private void CreateRelationship(EntityContext entityContext, Entity foreignEntit }; foreignEntity.Relationships.Add(foreignRelationship); } - foreignRelationship.CascadeDelete = tableKeySchema.OnDelete == Microsoft.EntityFrameworkCore.Migrations.ReferentialAction.Cascade; + foreignRelationship.ReferentialAction = tableKeySchema.OnDelete; foreignRelationship.IsMapped = true; foreignRelationship.IsForeignKey = true; foreignRelationship.Cardinality = foreignMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne; diff --git a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs index c13ec5c4..ca2dd63f 100644 --- a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs +++ b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs @@ -10,6 +10,10 @@ namespace EntityFrameworkCore.Generator.Options; /// public class MappingClassOptions : ClassOptionsBase { + DeleteBehavior _globalRelationshipCascadeDeleteBehavior; + DeleteBehavior _globalRelationshipSetNullDeleteBehavior; + DeleteBehavior _globalRelationshipNoActionDeleteBehavior; + /// /// Initializes a new instance of the class. /// @@ -18,12 +22,68 @@ public MappingClassOptions(VariableDictionary variables, string prefix) { Namespace = "{Project.Namespace}.Data.Mapping"; Directory = @"{Project.Directory}\Data\Mapping"; - RelationshipDeleteBehavior = DeleteBehavior.Cascade; + GlobalRelationshipCascadeDeleteBehavior = DeleteBehavior.Cascade; + GlobalRelationshipSetNullDeleteBehavior = DeleteBehavior.SetNull; + GlobalRelationshipNoActionDeleteBehavior = DeleteBehavior.NoAction; } /// - /// Gets or sets the delete behavior for all foreign keys that have cascade deletes. + /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete Cascade rule set. /// [DefaultValue(DeleteBehavior.Cascade)] - public DeleteBehavior RelationshipDeleteBehavior { get; set; } + public DeleteBehavior GlobalRelationshipCascadeDeleteBehavior + { + get => _globalRelationshipCascadeDeleteBehavior; + set + { + if (value == DeleteBehavior.Cascade || value == DeleteBehavior.ClientCascade) + { + _globalRelationshipCascadeDeleteBehavior = value; + } + else + { + throw new InvalidEnumArgumentException($"{nameof(GlobalRelationshipCascadeDeleteBehavior)} can only be set to {nameof(DeleteBehavior.Cascade)} or {nameof(DeleteBehavior.ClientCascade)}. Received {value}"); + } + } + } + + /// + /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete Set Null rule set. + /// + [DefaultValue(DeleteBehavior.SetNull)] + public DeleteBehavior GlobalRelationshipSetNullDeleteBehavior + { + get => _globalRelationshipSetNullDeleteBehavior; + set + { + if (value == DeleteBehavior.SetNull || value == DeleteBehavior.ClientSetNull) + { + _globalRelationshipSetNullDeleteBehavior = value; + } + else + { + throw new InvalidEnumArgumentException($"{nameof(GlobalRelationshipSetNullDeleteBehavior)} can only be set to {nameof(DeleteBehavior.SetNull)} or {nameof(DeleteBehavior.ClientSetNull)}. Received {value}"); + } + } + } + + /// + /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete No Action rule set. + /// + [DefaultValue(DeleteBehavior.NoAction)] + public DeleteBehavior GlobalRelationshipNoActionDeleteBehavior + { + get => _globalRelationshipNoActionDeleteBehavior; + set + { + if (value != DeleteBehavior.NoAction || value != DeleteBehavior.ClientNoAction) + { + _globalRelationshipNoActionDeleteBehavior = value; + } + else + { + throw new InvalidEnumArgumentException($"{nameof(GlobalRelationshipNoActionDeleteBehavior)} can only be set to {nameof(DeleteBehavior.NoAction)} or {nameof(DeleteBehavior.ClientNoAction)}. Received {value}"); + } + } + } } diff --git a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs index bc345268..7bfac45f 100644 --- a/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs +++ b/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; namespace EntityFrameworkCore.Generator.Templates; @@ -231,13 +232,32 @@ private void GenerateRelationshipMapping(Relationship relationship) CodeBuilder.Append("\")"); } - var cascadeOption = $"{nameof(DeleteBehavior)}.{nameof(DeleteBehavior.NoAction)}"; - if (relationship.CascadeDelete == true) + /* + * applies global delete behavior options to ALL foreign keys/relationships. individual + * foreign key configuration may be needed in the future, and would need mapping at a more + * granular level - similar to exclude.entities + */ + var onDeleteOption = Options.Data.Mapping.GlobalRelationshipNoActionDeleteBehavior.ToString(); + if (relationship.ReferentialAction == ReferentialAction.Cascade) { - cascadeOption = $"{nameof(DeleteBehavior)}.{Options.Data.Mapping.RelationshipDeleteBehavior}"; + // possible options: Cascade | ClientCascade + onDeleteOption = Options.Data.Mapping.GlobalRelationshipCascadeDeleteBehavior.ToString(); + } + else if (relationship.ReferentialAction == ReferentialAction.SetNull) + { + // possible options: SetNull | ClientSetNull + onDeleteOption = Options.Data.Mapping.GlobalRelationshipSetNullDeleteBehavior.ToString(); + } + else if (relationship.ReferentialAction == ReferentialAction.Restrict) + { + onDeleteOption = nameof(DeleteBehavior.Restrict); + } + else if (relationship.ReferentialAction == ReferentialAction.SetDefault) + { + // it's not clear what SetDefault should map to. assuming NoAction, and taking the default } CodeBuilder.AppendLine(); - CodeBuilder.Append($".OnDelete({cascadeOption})"); + CodeBuilder.Append($".OnDelete({nameof(DeleteBehavior)}.{onDeleteOption})"); CodeBuilder.DecrementIndent(); From 33d7dd290166abe8290ca76647cc24dd73357fa1 Mon Sep 17 00:00:00 2001 From: Joey Bradshaw Date: Fri, 7 Apr 2023 21:38:35 -0500 Subject: [PATCH 3/3] setup a way to validate beyond the default serialization. --- .../Options/MappingClassOptions.cs | 90 +++++++++---------- .../Options/OptionsBase.cs | 8 +- .../GenerateCommand.cs | 24 ++++- 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs index ca2dd63f..d08e49ed 100644 --- a/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs +++ b/src/EntityFrameworkCore.Generator.Core/Options/MappingClassOptions.cs @@ -1,6 +1,9 @@ +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace EntityFrameworkCore.Generator.Options; @@ -10,9 +13,9 @@ namespace EntityFrameworkCore.Generator.Options; /// public class MappingClassOptions : ClassOptionsBase { - DeleteBehavior _globalRelationshipCascadeDeleteBehavior; - DeleteBehavior _globalRelationshipSetNullDeleteBehavior; - DeleteBehavior _globalRelationshipNoActionDeleteBehavior; + static List _validCascadeBehaviors = new List { DeleteBehavior.Cascade, DeleteBehavior.ClientCascade }; + static List _validSetNullBehaviors = new List { DeleteBehavior.SetNull, DeleteBehavior.ClientSetNull }; + static List _validNoActionBehaviors = new List { DeleteBehavior.NoAction, DeleteBehavior.ClientNoAction }; /// /// Initializes a new instance of the class. @@ -27,63 +30,56 @@ public MappingClassOptions(VariableDictionary variables, string prefix) GlobalRelationshipNoActionDeleteBehavior = DeleteBehavior.NoAction; } - /// - /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete Cascade rule set. - /// - [DefaultValue(DeleteBehavior.Cascade)] - public DeleteBehavior GlobalRelationshipCascadeDeleteBehavior + public override bool Validate(ILogger logger) { - get => _globalRelationshipCascadeDeleteBehavior; - set + var errors = new List(); + if (_validCascadeBehaviors.Any(a => a == GlobalRelationshipCascadeDeleteBehavior) == false) { - if (value == DeleteBehavior.Cascade || value == DeleteBehavior.ClientCascade) - { - _globalRelationshipCascadeDeleteBehavior = value; - } - else + errors.Add(GetBehaviorValidationError(nameof(GlobalRelationshipCascadeDeleteBehavior), GlobalRelationshipCascadeDeleteBehavior, _validCascadeBehaviors)); + } + if (_validSetNullBehaviors.Any(a => a == GlobalRelationshipSetNullDeleteBehavior) == false) + { + errors.Add(GetBehaviorValidationError(nameof(GlobalRelationshipSetNullDeleteBehavior), GlobalRelationshipSetNullDeleteBehavior, _validSetNullBehaviors)); + } + if (_validNoActionBehaviors.Any(a => a == GlobalRelationshipNoActionDeleteBehavior) == false) + { + errors.Add(GetBehaviorValidationError(nameof(GlobalRelationshipNoActionDeleteBehavior), GlobalRelationshipNoActionDeleteBehavior, _validNoActionBehaviors)); + } + + if (errors.Any()) + { + errors.ForEach(err => { - throw new InvalidEnumArgumentException($"{nameof(GlobalRelationshipCascadeDeleteBehavior)} can only be set to {nameof(DeleteBehavior.Cascade)} or {nameof(DeleteBehavior.ClientCascade)}. Received {value}"); - } + logger.LogError(err); + }); + throw new InvalidEnumArgumentException(errors.FirstOrDefault()); + } + + // always call the base + return base.Validate(logger); + + string GetBehaviorValidationError(string propertyName, DeleteBehavior behavior, List behaviors) + { + var error = $"{propertyName} can only be set to {string.Join(" or ", behaviors)}. Received {behavior}"; + return error; } } + /// + /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete Cascade rule set. + /// + [DefaultValue(DeleteBehavior.Cascade)] + public DeleteBehavior GlobalRelationshipCascadeDeleteBehavior { get; set; } + /// /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete Set Null rule set. /// [DefaultValue(DeleteBehavior.SetNull)] - public DeleteBehavior GlobalRelationshipSetNullDeleteBehavior - { - get => _globalRelationshipSetNullDeleteBehavior; - set - { - if (value == DeleteBehavior.SetNull || value == DeleteBehavior.ClientSetNull) - { - _globalRelationshipSetNullDeleteBehavior = value; - } - else - { - throw new InvalidEnumArgumentException($"{nameof(GlobalRelationshipSetNullDeleteBehavior)} can only be set to {nameof(DeleteBehavior.SetNull)} or {nameof(DeleteBehavior.ClientSetNull)}. Received {value}"); - } - } - } + public DeleteBehavior GlobalRelationshipSetNullDeleteBehavior { get; set; } /// /// Gets or sets the delete behavior globally for all relationships who's foreign keys have a insert delete No Action rule set. /// [DefaultValue(DeleteBehavior.NoAction)] - public DeleteBehavior GlobalRelationshipNoActionDeleteBehavior - { - get => _globalRelationshipNoActionDeleteBehavior; - set - { - if (value != DeleteBehavior.NoAction || value != DeleteBehavior.ClientNoAction) - { - _globalRelationshipNoActionDeleteBehavior = value; - } - else - { - throw new InvalidEnumArgumentException($"{nameof(GlobalRelationshipNoActionDeleteBehavior)} can only be set to {nameof(DeleteBehavior.NoAction)} or {nameof(DeleteBehavior.ClientNoAction)}. Received {value}"); - } - } - } + public DeleteBehavior GlobalRelationshipNoActionDeleteBehavior { get; set; } } diff --git a/src/EntityFrameworkCore.Generator.Core/Options/OptionsBase.cs b/src/EntityFrameworkCore.Generator.Core/Options/OptionsBase.cs index 512769fb..58e3bf25 100644 --- a/src/EntityFrameworkCore.Generator.Core/Options/OptionsBase.cs +++ b/src/EntityFrameworkCore.Generator.Core/Options/OptionsBase.cs @@ -1,6 +1,9 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; + using EntityFrameworkCore.Generator.Extensions; +using Microsoft.Extensions.Logging; + namespace EntityFrameworkCore.Generator.Options; public class OptionsBase @@ -20,6 +23,7 @@ public OptionsBase(VariableDictionary variables, string prefix) protected string Prefix { get; } + public virtual bool Validate(ILogger logger) => true; protected string GetProperty([CallerMemberName] string propertyName = null) { @@ -43,4 +47,4 @@ protected static string AppendPrefix(string root, string prefix) ? $"{root}.{prefix}" : prefix; } -} \ No newline at end of file +} diff --git a/src/EntityFrameworkCore.Generator/GenerateCommand.cs b/src/EntityFrameworkCore.Generator/GenerateCommand.cs index aa1f7925..9fd40cf0 100644 --- a/src/EntityFrameworkCore.Generator/GenerateCommand.cs +++ b/src/EntityFrameworkCore.Generator/GenerateCommand.cs @@ -1,8 +1,11 @@ -using EntityFrameworkCore.Generator.Extensions; +using System; + +using EntityFrameworkCore.Generator.Extensions; using EntityFrameworkCore.Generator.Options; + using McMaster.Extensions.CommandLineUtils; + using Microsoft.Extensions.Logging; -using System; namespace EntityFrameworkCore.Generator; @@ -73,9 +76,24 @@ protected override int OnExecute(CommandLineApplication application) if (Validator.HasValue) options.Model.Validator.Generate = Validator.Value; + options.Model.Read.Validate(Logger); + options.Model.Create.Validate(Logger); + options.Model.Update.Validate(Logger); + options.Model.Mapper.Validate(Logger); + options.Model.Shared.Validate(Logger); + options.Model.Update.Validate(Logger); + options.Model.Validator.Validate(Logger); + options.Data.Mapping.Validate(Logger); + options.Data.Query.Validate(Logger); + options.Data.Entity.Validate(Logger); + options.Data.Context.Validate(Logger); + options.Database.Validate(Logger); + options.Project.Validate(Logger); + options.Script.Validate(Logger); + var result = _codeGenerator.Generate(options); return result ? 0 : 1; } -} \ No newline at end of file +}