diff --git a/src/Core/RevEng.Core.60/ReplacingCandidateNamingService.cs b/src/Core/RevEng.Core.60/ReplacingCandidateNamingService.cs index 2b467b198..143ef2035 100644 --- a/src/Core/RevEng.Core.60/ReplacingCandidateNamingService.cs +++ b/src/Core/RevEng.Core.60/ReplacingCandidateNamingService.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore.Scaffolding.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Scaffolding.Internal; using Microsoft.EntityFrameworkCore.Scaffolding.Metadata; using RevEng.Common; using System; @@ -13,11 +14,16 @@ public class ReplacingCandidateNamingService : CandidateNamingService { private readonly List customNameOptions; private readonly bool preserveCasingUsingRegex; + private readonly bool usePrefixNaming; - public ReplacingCandidateNamingService(List customNameOptions, bool preserveCasingUsingRegex = false) + public ReplacingCandidateNamingService( + List customNameOptions, + bool preserveCasingUsingRegex = false, + bool usePrefixNaming = false) { this.customNameOptions = customNameOptions; this.preserveCasingUsingRegex = preserveCasingUsingRegex; + this.usePrefixNaming = usePrefixNaming; } public override string GenerateCandidateIdentifier(DatabaseTable originalTable) @@ -129,6 +135,85 @@ public override string GenerateCandidateIdentifier(DatabaseColumn originalColumn return base.GenerateCandidateIdentifier(originalColumn); } +#if CORE80 + public override string GetDependentEndCandidateNavigationPropertyName(IReadOnlyForeignKey foreignKey) + { + ArgumentNullException.ThrowIfNull(foreignKey); + + if (!usePrefixNaming) + { + return base.GetDependentEndCandidateNavigationPropertyName(foreignKey); + } + + var candidateName = FindCandidateNavigationName(foreignKey.Properties); + + return !string.IsNullOrEmpty(candidateName) ? candidateName : foreignKey.PrincipalEntityType.ShortName(); + } + + private static string FindCandidateNavigationName(IEnumerable properties) + { + var count = properties.Count(); + if (count == 0) + { + return string.Empty; + } + + var firstProperty = properties.First(); + return StripId( + count == 1 + ? firstProperty.Name + : FindCommonPrefix(firstProperty.Name, properties.Select(p => p.Name))); + } + + private static string FindCommonPrefix(string firstName, IEnumerable propertyNames) + { + var prefixLength = 0; + foreach (var c in firstName) + { + foreach (var s in propertyNames) + { + if (s.Length <= prefixLength + || s[prefixLength] != c) + { + return firstName[..prefixLength]; + } + } + + prefixLength++; + } + + return firstName[..prefixLength]; + } + + private static string StripId(string commonPrefix) + { + if (commonPrefix.Length < 3 + || !commonPrefix.EndsWith("id", StringComparison.OrdinalIgnoreCase)) + { + return commonPrefix; + } + + var ignoredCharacterCount = 2; + if (commonPrefix.Length > 4 + && commonPrefix.EndsWith("guid", StringComparison.OrdinalIgnoreCase)) + { + ignoredCharacterCount = 4; + } + + int i; + for (i = commonPrefix.Length - ignoredCharacterCount - 1; i >= 0; i--) + { + if (char.IsLetterOrDigit(commonPrefix[i])) + { + break; + } + } + + return i != 0 + ? commonPrefix[..(i + 1)] + : commonPrefix; + } +#endif private static string GenerateIdentifier(string value) { var candidateStringBuilder = new StringBuilder(); diff --git a/src/Core/RevEng.Core.60/ServiceProviderBuilder.cs b/src/Core/RevEng.Core.60/ServiceProviderBuilder.cs index 3e93d1595..1d02edd7a 100644 --- a/src/Core/RevEng.Core.60/ServiceProviderBuilder.cs +++ b/src/Core/RevEng.Core.60/ServiceProviderBuilder.cs @@ -59,11 +59,17 @@ public static IServiceCollection AddEfpt(this IServiceCollection serviceCollecti options.DatabaseType, options.UseManyToManyEntity)); +#if CORE80 + if (options.CustomReplacers != null || options.UsePrefixNavigationNaming) + { + serviceCollection.AddSingleton(provider => new ReplacingCandidateNamingService(options.CustomReplacers, options.PreserveCasingWithRegex, options.UsePrefixNavigationNaming)); + } +#else if (options.CustomReplacers != null) { serviceCollection.AddSingleton(provider => new ReplacingCandidateNamingService(options.CustomReplacers, options.PreserveCasingWithRegex)); } - +#endif if (options.UseHandleBars) { serviceCollection.AddHandlebarsScaffolding(hbOptions => diff --git a/src/GUI/RevEng.Shared/ReverseEngineerCommandOptions.cs b/src/GUI/RevEng.Shared/ReverseEngineerCommandOptions.cs index 68603b88d..3e6e7da3d 100644 --- a/src/GUI/RevEng.Shared/ReverseEngineerCommandOptions.cs +++ b/src/GUI/RevEng.Shared/ReverseEngineerCommandOptions.cs @@ -59,5 +59,6 @@ public class ReverseEngineerCommandOptions public bool UseDecimalDataAnnotation { get; set; } public bool PreserveCasingWithRegex { get; set; } public bool UseDateOnlyTimeOnly { get; set; } + public bool UsePrefixNavigationNaming { get; set; } } } diff --git a/src/GUI/Shared/Handlers/ReverseEngineer/EfRevEngLauncher.cs b/src/GUI/Shared/Handlers/ReverseEngineer/EfRevEngLauncher.cs index a097b545b..c536f037e 100644 --- a/src/GUI/Shared/Handlers/ReverseEngineer/EfRevEngLauncher.cs +++ b/src/GUI/Shared/Handlers/ReverseEngineer/EfRevEngLauncher.cs @@ -114,6 +114,7 @@ public static async Task LaunchExternalRunnerAsync(Revers UseDateOnlyTimeOnly = options.UseDateOnlyTimeOnly, UseSchemaNamespaces = options.UseSchemaNamespaces, UseDecimalDataAnnotation = options.UseDecimalDataAnnotationForSprocResult, + UsePrefixNavigationNaming = options.UsePrefixNavigationNaming, }; var launcher = new EfRevEngLauncher(commandOptions, codeGenerationMode); diff --git a/src/GUI/Shared/Handlers/ReverseEngineer/ReverseEngineerOptions.cs b/src/GUI/Shared/Handlers/ReverseEngineer/ReverseEngineerOptions.cs index b39545625..3bc1fe9a4 100644 --- a/src/GUI/Shared/Handlers/ReverseEngineer/ReverseEngineerOptions.cs +++ b/src/GUI/Shared/Handlers/ReverseEngineer/ReverseEngineerOptions.cs @@ -60,5 +60,6 @@ public class ReverseEngineerOptions public bool UseDateOnlyTimeOnly { get; set; } public string T4TemplatePath { get; set; } public bool UseDecimalDataAnnotationForSprocResult { get; set; } = true; + public bool UsePrefixNavigationNaming { get; set; } } } diff --git a/src/GUI/lib/efreveng80.exe.zip b/src/GUI/lib/efreveng80.exe.zip index 1343c0ff8..d282751bc 100644 Binary files a/src/GUI/lib/efreveng80.exe.zip and b/src/GUI/lib/efreveng80.exe.zip differ