From 5c84905f32524f0007dbd71cfbc590ed47c710b2 Mon Sep 17 00:00:00 2001 From: Zohar Peled Date: Sun, 29 Jun 2025 20:59:48 +0300 Subject: [PATCH] Fixed typo, updated C# features throughout the AutoEntityGenerator project --- AutoEntityGenerator/AppSettings.cs | 17 +- .../AutoEntityGenerator.csproj | 2 +- ...oEntityGeneratorCodeRefactoringProvider.cs | 75 +++--- AutoEntityGenerator/CodeActionFactory.cs | 93 ++++--- .../CodeOperations/GenerateCodeOperation.cs | 139 ++++++----- .../CodeOperations/GetEntityInfoOperation.cs | 53 ++-- .../CodeOperations/GetUserInputOperation.cs | 35 ++- AutoEntityGenerator/ConfigurationService.cs | 133 +++++----- AutoEntityGenerator/EntityGenerator.cs | 229 +++++++++--------- .../EntityGeneratorCodeAction.cs | 67 +++-- AutoEntityGenerator/Services.cs | 189 +++++++-------- 11 files changed, 511 insertions(+), 521 deletions(-) diff --git a/AutoEntityGenerator/AppSettings.cs b/AutoEntityGenerator/AppSettings.cs index 0139746..bf95977 100644 --- a/AutoEntityGenerator/AppSettings.cs +++ b/AutoEntityGenerator/AppSettings.cs @@ -1,14 +1,13 @@ using AutoEntityGenerator.Common.Interfaces; using Microsoft.Extensions.Logging; -namespace AutoEntityGenerator +namespace AutoEntityGenerator; + +internal class AppSettings : IAppSettings { - internal class AppSettings : IAppSettings - { - public LogLevel MinimumLogLevel { get; set; } - public string DestinationFolder { get; set; } - public string RequestSuffix { get; set; } - public string ResponseSuffix { get; set; } - public bool OpenGeneratedFiles { get; set; } - } + public LogLevel MinimumLogLevel { get; set; } + public string DestinationFolder { get; set; } + public string RequestSuffix { get; set; } + public string ResponseSuffix { get; set; } + public bool OpenGeneratedFiles { get; set; } } diff --git a/AutoEntityGenerator/AutoEntityGenerator.csproj b/AutoEntityGenerator/AutoEntityGenerator.csproj index d73c4db..fa44eb9 100644 --- a/AutoEntityGenerator/AutoEntityGenerator.csproj +++ b/AutoEntityGenerator/AutoEntityGenerator.csproj @@ -3,7 +3,7 @@ netstandard2.0 true - latests + latest diff --git a/AutoEntityGenerator/AutoEntityGeneratorCodeRefactoringProvider.cs b/AutoEntityGenerator/AutoEntityGeneratorCodeRefactoringProvider.cs index e54434f..c3cbabd 100644 --- a/AutoEntityGenerator/AutoEntityGeneratorCodeRefactoringProvider.cs +++ b/AutoEntityGenerator/AutoEntityGeneratorCodeRefactoringProvider.cs @@ -8,51 +8,50 @@ using System.Threading.Tasks; -namespace AutoEntityGenerator +namespace AutoEntityGenerator; + +[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(AutoEntityGeneratorCodeRefactoringProvider)), Shared] +internal class AutoEntityGeneratorCodeRefactoringProvider : CodeRefactoringProvider { - [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(AutoEntityGeneratorCodeRefactoringProvider)), Shared] - internal class AutoEntityGeneratorCodeRefactoringProvider : CodeRefactoringProvider + private readonly ILogger _logger; + private readonly ICodeActionFactory _codeActionFactory; + private readonly IServices _services; + + public AutoEntityGeneratorCodeRefactoringProvider() + { + _services = Services.Instance; + _logger = _services.GetService>(); + _codeActionFactory = _services.GetService(); + _logger.LogInformation("AutoEntityGenerator started."); + } + + public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { - private readonly ILogger _logger; - private readonly ICodeActionFactory _codeActionFactory; - private readonly IServices _services; + _logger.LogDebug("ComputeRefactoringsAsync called."); + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(context.Span); - public AutoEntityGeneratorCodeRefactoringProvider() + if (!(node is ClassDeclarationSyntax || node is RecordDeclarationSyntax)) { - _services = Services.Instance; - _logger = _services.GetService>(); - _codeActionFactory = _services.GetService(); - _logger.LogInformation("AutoEntityGenerator started."); + return; + } + var typeDecleration = node as TypeDeclarationSyntax; + var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (semanticModel.GetDeclaredSymbol(typeDecleration) is not INamedTypeSymbol typeSymbol) + { + return; } - public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + var hasPublicProperties = typeSymbol.GetMembers().OfType().Any(p => p.DeclaredAccessibility == Accessibility.Public); + + if (!hasPublicProperties) { - _logger.LogDebug("ComputeRefactoringsAsync called."); - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - var node = root.FindNode(context.Span); - - if (!(node is ClassDeclarationSyntax || node is RecordDeclarationSyntax)) - { - return; - } - var typeDecleration = node as TypeDeclarationSyntax; - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - if (!(semanticModel.GetDeclaredSymbol(typeDecleration) is INamedTypeSymbol typeSymbol)) - { - return; - } - - var hasPublicProperties = typeSymbol.GetMembers().OfType().Any(p => p.DeclaredAccessibility == Accessibility.Public); - - if (!hasPublicProperties) - { - return; - } - - _logger.LogDebug("attempting to register refactoring."); - var action = _codeActionFactory.CreateEntityGeneratorCodeAction(context.Document, typeDecleration, typeSymbol); - context.RegisterRefactoring(action); - _logger.LogDebug("refactoring registered."); + return; } + + _logger.LogDebug("attempting to register refactoring."); + var action = _codeActionFactory.CreateEntityGeneratorCodeAction(context.Document, typeDecleration, typeSymbol); + context.RegisterRefactoring(action); + _logger.LogDebug("refactoring registered."); } } diff --git a/AutoEntityGenerator/CodeActionFactory.cs b/AutoEntityGenerator/CodeActionFactory.cs index 6614a0c..7d2ac38 100644 --- a/AutoEntityGenerator/CodeActionFactory.cs +++ b/AutoEntityGenerator/CodeActionFactory.cs @@ -4,55 +4,54 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Logging; -namespace AutoEntityGenerator +namespace AutoEntityGenerator; + +internal interface ICodeActionFactory { - internal interface ICodeActionFactory - { - EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol); - GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document); - GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol); - GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider); - } + EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol); + GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document); + GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol); + GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider); +} + +internal class CodeActionFactory : ICodeActionFactory +{ + private readonly ILogger _entityGeneratorCodeActionLogger; + private readonly ILogger _generateCodeOperationLogger; + private readonly ILogger _getEntityInfoOperationLogger; + private readonly IEntityGenerator _entityGenerator; + private readonly IUserInteraction _userInteraction; + private readonly ICodeFileGenerator _codeGenerator; + private readonly IDocumentOpener _documentOpener; - internal class CodeActionFactory : ICodeActionFactory + public CodeActionFactory(ILogger entityGeneratorCodeActionLogger, ILogger generateCodeOperationLogger, ILogger getEntityInfoOperationLogger, IEntityGenerator entityGenerator, IUserInteraction userInteraction, ICodeFileGenerator codeGenerator, IDocumentOpener documentOpener) { - private readonly ILogger _entityGeneratorCodeActionLogger; - private readonly ILogger _generateCodeOperationLogger; - private readonly ILogger _getEntityInfoOperationLogger; - private readonly IEntityGenerator _entityGenerator; - private readonly IUserInteraction _userInteraction; - private readonly ICodeFileGenerator _codeGenerator; - private readonly IDocumentOpener _documentOpener; - - public CodeActionFactory(ILogger entityGeneratorCodeActionLogger, ILogger generateCodeOperationLogger, ILogger getEntityInfoOperationLogger, IEntityGenerator entityGenerator, IUserInteraction userInteraction, ICodeFileGenerator codeGenerator, IDocumentOpener documentOpener) - { - _entityGeneratorCodeActionLogger = entityGeneratorCodeActionLogger; - _generateCodeOperationLogger = generateCodeOperationLogger; - _getEntityInfoOperationLogger = getEntityInfoOperationLogger; - _entityGenerator = entityGenerator; - _userInteraction = userInteraction; - _codeGenerator = codeGenerator; - _documentOpener = documentOpener; - } - - public EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol) - => new EntityGeneratorCodeAction(_entityGeneratorCodeActionLogger, document, typeDeclaration, this, typeSymbol); - - public GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider) - => new GetUserInputOperation(entityProvider, _userInteraction); - - public GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document) - => new GenerateCodeOperation( - uiResultProvider, - _entityGenerator, - _codeGenerator, - entityProvider, - document, - _generateCodeOperationLogger, - _documentOpener - ); - - public GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol) - => new GetEntityInfoOperation(_getEntityInfoOperationLogger, document, typeDeclaration, typeSymbol, _entityGenerator); + _entityGeneratorCodeActionLogger = entityGeneratorCodeActionLogger; + _generateCodeOperationLogger = generateCodeOperationLogger; + _getEntityInfoOperationLogger = getEntityInfoOperationLogger; + _entityGenerator = entityGenerator; + _userInteraction = userInteraction; + _codeGenerator = codeGenerator; + _documentOpener = documentOpener; } + + public EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol) + => new(_entityGeneratorCodeActionLogger, document, typeDeclaration, this, typeSymbol); + + public GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider) + => new(entityProvider, _userInteraction); + + public GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document) + => new( + uiResultProvider, + _entityGenerator, + _codeGenerator, + entityProvider, + document, + _generateCodeOperationLogger, + _documentOpener + ); + + public GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol) + => new(_getEntityInfoOperationLogger, document, typeDeclaration, typeSymbol, _entityGenerator); } \ No newline at end of file diff --git a/AutoEntityGenerator/CodeOperations/GenerateCodeOperation.cs b/AutoEntityGenerator/CodeOperations/GenerateCodeOperation.cs index fd8a6ae..1f06752 100644 --- a/AutoEntityGenerator/CodeOperations/GenerateCodeOperation.cs +++ b/AutoEntityGenerator/CodeOperations/GenerateCodeOperation.cs @@ -8,96 +8,95 @@ using System.Runtime.InteropServices; using System.Threading; -namespace AutoEntityGenerator.CodeOperations +namespace AutoEntityGenerator.CodeOperations; + +internal class GenerateCodeOperation : CodeActionOperation { - internal class GenerateCodeOperation : CodeActionOperation + private readonly ILogger _logger; + private readonly IUIResultProvider _resultProvider; + private readonly IEntityGenerator _entityGenerator; + private readonly IEntityProvider _entityProvider; + private readonly ICodeFileGenerator _codeGenerator; + private readonly IDocumentOpener _documentOpener; + private readonly Document _document; + + public GenerateCodeOperation(IUIResultProvider getUserInputOperation, IEntityGenerator entityGenerator, ICodeFileGenerator codeGenerator, IEntityProvider entityProvider, Document document, ILogger logger, IDocumentOpener documentOpener) { - private readonly ILogger _logger; - private readonly IUIResultProvider _resultProvider; - private readonly IEntityGenerator _entityGenerator; - private readonly IEntityProvider _entityProvider; - private readonly ICodeFileGenerator _codeGenerator; - private readonly IDocumentOpener _documentOpener; - private readonly Document _document; + _resultProvider = getUserInputOperation; + _codeGenerator = codeGenerator; + _entityProvider = entityProvider; + _document = document; + _entityGenerator = entityGenerator; + _logger = logger; + _documentOpener = documentOpener; + } - public GenerateCodeOperation(IUIResultProvider getUserInputOperation, IEntityGenerator entityGenerator, ICodeFileGenerator codeGenerator, IEntityProvider entityProvider, Document document, ILogger logger, IDocumentOpener documentOpener) + public override void Apply(Workspace workspace, CancellationToken cancellationToken) + { + var result = _resultProvider.UserInteractionResult; + if (!result.IsOk) { - _resultProvider = getUserInputOperation; - _codeGenerator = codeGenerator; - _entityProvider = entityProvider; - _document = document; - _entityGenerator = entityGenerator; - _logger = logger; - _documentOpener = documentOpener; + _logger.LogInformation("The user cancelled the operation."); + return; } - public override void Apply(Workspace workspace, CancellationToken cancellationToken) - { - var result = _resultProvider.UserInteractionResult; - if (!result.IsOk) - { - _logger.LogInformation("The user cancelled the operation."); - return; - } - - _logger.LogInformation("Attempting to generate dto {dtoName} and mapping extension.", result.EntityName); + _logger.LogInformation("Attempting to generate dto {dtoName} and mapping extension.", result.EntityName); - var sourceEntity = _entityProvider.Entity; - var dtoEntity = _entityGenerator.GenerateFromUIResult(result, sourceEntity); + var sourceEntity = _entityProvider.Entity; + var dtoEntity = _entityGenerator.GenerateFromUIResult(result, sourceEntity); - var (from, to) = result.MappingDirection == MappingDirection.FromDtoToModel - ? (dtoEntity, sourceEntity) - : (sourceEntity, dtoEntity); + var (from, to) = result.MappingDirection == MappingDirection.FromDtoToModel + ? (dtoEntity, sourceEntity) + : (sourceEntity, dtoEntity); - var dto = _codeGenerator.GenerateEntityCodeFile(dtoEntity); - var mapping = _codeGenerator.GenerateMappingCodeFile(from, to); - _logger.LogDebug("Dto and Mapping generated."); - var dtoDocument = AddDocument(_document, dto.FileName, dto.Content, result.TargetDirectory, sourceEntity); - var mappingDocument = AddDocument(dtoDocument, mapping.FileName, mapping.Content, result.TargetDirectory, sourceEntity); + var dto = _codeGenerator.GenerateEntityCodeFile(dtoEntity); + var mapping = _codeGenerator.GenerateMappingCodeFile(from, to); + _logger.LogDebug("Dto and Mapping generated."); + var dtoDocument = AddDocument(_document, dto.FileName, dto.Content, result.TargetDirectory, sourceEntity); + var mappingDocument = AddDocument(dtoDocument, mapping.FileName, mapping.Content, result.TargetDirectory, sourceEntity); - _logger.LogDebug("Attempting to save changes."); + _logger.LogDebug("Attempting to save changes."); - try + try + { + if (workspace.TryApplyChanges(mappingDocument.Project.Solution)) { - if (workspace.TryApplyChanges(mappingDocument.Project.Solution)) - { - _logger.LogInformation("Dto and mapping extension class saved to {TargetDirectory}.", result.TargetDirectory); - if (result.OpenFiles) - { - _documentOpener.OpenDocument(mappingDocument.FilePath); - _documentOpener.OpenDocument(dtoDocument.FilePath); - } - } - else + _logger.LogInformation("Dto and mapping extension class saved to {TargetDirectory}.", result.TargetDirectory); + if (result.OpenFiles) { - _logger.LogWarning("TryApplyChanges returned false. Changes was not saved."); + _documentOpener.OpenDocument(mappingDocument.FilePath); + _documentOpener.OpenDocument(dtoDocument.FilePath); } } - // This will be thrown if the Apply method doesn't run on the UI thread. - catch (COMException ex) + else { - _logger.LogError(ex, "Failed to open documents."); - throw; - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to apply changes."); - throw; + _logger.LogWarning("TryApplyChanges returned false. Changes was not saved."); } } - - private Document AddDocument(Document document, string fileName, string code, string targetFolder, Entity sourceEntity) + // This will be thrown if the Apply method doesn't run on the UI thread. + catch (COMException ex) { - _logger.LogDebug("Attempting to add document {Filename}.", fileName); - var filePath = Path.Combine(targetFolder, fileName); - string[] folders = targetFolder == Path.GetDirectoryName(sourceEntity.SourceFilePath) - ? null - : new[] { targetFolder }; - - var newDocument = document.Project.AddDocument(fileName, code, folders, filePath); - _logger.LogDebug("document {Filename} Added.", fileName); - return newDocument; + _logger.LogError(ex, "Failed to open documents."); + throw; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to apply changes."); + throw; } + } + private Document AddDocument(Document document, string fileName, string code, string targetFolder, Entity sourceEntity) + { + _logger.LogDebug("Attempting to add document {Filename}.", fileName); + var filePath = Path.Combine(targetFolder, fileName); + string[] folders = targetFolder == Path.GetDirectoryName(sourceEntity.SourceFilePath) + ? null + : new[] { targetFolder }; + + var newDocument = document.Project.AddDocument(fileName, code, folders, filePath); + _logger.LogDebug("document {Filename} Added.", fileName); + return newDocument; } + } diff --git a/AutoEntityGenerator/CodeOperations/GetEntityInfoOperation.cs b/AutoEntityGenerator/CodeOperations/GetEntityInfoOperation.cs index e87dcfb..c864eed 100644 --- a/AutoEntityGenerator/CodeOperations/GetEntityInfoOperation.cs +++ b/AutoEntityGenerator/CodeOperations/GetEntityInfoOperation.cs @@ -5,37 +5,36 @@ using Microsoft.Extensions.Logging; using System.Threading; -namespace AutoEntityGenerator.CodeOperations +namespace AutoEntityGenerator.CodeOperations; + +public interface IEntityProvider { - public interface IEntityProvider + Entity Entity { get; } +} + +internal class GetEntityInfoOperation : CodeActionOperation, IEntityProvider +{ + private readonly ILogger _logger; + private readonly Document _document; + private readonly TypeDeclarationSyntax _typeDeclaration; + private readonly INamedTypeSymbol _typeSymbol; + private readonly IEntityGenerator _entityGenerator; + + public GetEntityInfoOperation(ILogger logger, Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, IEntityGenerator entityGenerator) { - Entity Entity { get; } + _logger = logger; + _document = document; + _typeDeclaration = typeDeclaration; + _typeSymbol = typeSymbol; + _entityGenerator = entityGenerator; } - internal class GetEntityInfoOperation : CodeActionOperation, IEntityProvider + public override void Apply(Workspace workspace, CancellationToken cancellationToken) { - private readonly ILogger _logger; - private readonly Document _document; - private readonly TypeDeclarationSyntax _typeDeclaration; - private readonly INamedTypeSymbol _typeSymbol; - private readonly IEntityGenerator _entityGenerator; - - public GetEntityInfoOperation(ILogger logger, Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, IEntityGenerator entityGenerator) - { - _logger = logger; - _document = document; - _typeDeclaration = typeDeclaration; - _typeSymbol = typeSymbol; - _entityGenerator = entityGenerator; - } - - public override void Apply(Workspace workspace, CancellationToken cancellationToken) - { - _logger.LogDebug("Attempting to generate entity from document"); - Entity = _entityGenerator.GenerateFromDocument(_document, _typeDeclaration, _typeSymbol, cancellationToken); - _logger.LogDebug("Entity generated from document"); - } - - public Entity Entity { get; private set; } + _logger.LogDebug("Attempting to generate entity from document"); + Entity = _entityGenerator.GenerateFromDocument(_document, _typeDeclaration, _typeSymbol, cancellationToken); + _logger.LogDebug("Entity generated from document"); } + + public Entity Entity { get; private set; } } diff --git a/AutoEntityGenerator/CodeOperations/GetUserInputOperation.cs b/AutoEntityGenerator/CodeOperations/GetUserInputOperation.cs index a1e6fbc..80f557c 100644 --- a/AutoEntityGenerator/CodeOperations/GetUserInputOperation.cs +++ b/AutoEntityGenerator/CodeOperations/GetUserInputOperation.cs @@ -3,27 +3,26 @@ using Microsoft.CodeAnalysis.CodeActions; using System.Threading; -namespace AutoEntityGenerator.CodeOperations +namespace AutoEntityGenerator.CodeOperations; + +internal interface IUIResultProvider { - internal interface IUIResultProvider - { - IUserInteractionResult UserInteractionResult { get; } - } + IUserInteractionResult UserInteractionResult { get; } +} - internal class GetUserInputOperation : CodeActionOperation, IUIResultProvider - { - private readonly IEntityProvider _entityProvider; - private readonly IUserInteraction _userInteraction; +internal class GetUserInputOperation : CodeActionOperation, IUIResultProvider +{ + private readonly IEntityProvider _entityProvider; + private readonly IUserInteraction _userInteraction; - public GetUserInputOperation(IEntityProvider entityProvider, IUserInteraction userInteraction) - { - _entityProvider = entityProvider; - _userInteraction = userInteraction; - } + public GetUserInputOperation(IEntityProvider entityProvider, IUserInteraction userInteraction) + { + _entityProvider = entityProvider; + _userInteraction = userInteraction; + } - public override void Apply(Workspace workspace, CancellationToken cancellationToken) - => UserInteractionResult = _userInteraction.ShowUIForm(_entityProvider.Entity); + public override void Apply(Workspace workspace, CancellationToken cancellationToken) + => UserInteractionResult = _userInteraction.ShowUIForm(_entityProvider.Entity); - public IUserInteractionResult UserInteractionResult { get; private set; } - } + public IUserInteractionResult UserInteractionResult { get; private set; } } diff --git a/AutoEntityGenerator/ConfigurationService.cs b/AutoEntityGenerator/ConfigurationService.cs index 19f4d06..188173a 100644 --- a/AutoEntityGenerator/ConfigurationService.cs +++ b/AutoEntityGenerator/ConfigurationService.cs @@ -6,88 +6,87 @@ using System.Linq; using System.Text.Json; -namespace AutoEntityGenerator +namespace AutoEntityGenerator; + +internal class ConfigurationService : IConfigurationSaver { - internal class ConfigurationService : IConfigurationSaver - { - private const string FileName = "appSettings.json"; - private const string DefaultDestinationFolder = "Generated"; - private readonly string _basePath; - private readonly string _fullFilePath; - private readonly JsonSerializerOptions _jsonOptions; - private Exception _deferredException; + private const string FileName = "appSettings.json"; + private const string DefaultDestinationFolder = "Generated"; + private readonly string _basePath; + private readonly string _fullFilePath; + private readonly JsonSerializerOptions _jsonOptions; + private Exception _deferredException; - public ConfigurationService() + public ConfigurationService() + { + _basePath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Zohar Peled", + nameof(AutoEntityGenerator)); + _fullFilePath = Path.Combine(_basePath, FileName); + _jsonOptions = new JsonSerializerOptions() { - _basePath = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "Zohar Peled", - nameof(AutoEntityGenerator)); - _fullFilePath = Path.Combine(_basePath, FileName); - _jsonOptions = new JsonSerializerOptions() - { - WriteIndented = true - }; - } + WriteIndented = true + }; + } - public IAppSettings Load() - => LoadFromFile() ?? new AppSettings() - { - DestinationFolder = DefaultDestinationFolder, - MinimumLogLevel = LogLevel.Information, - RequestSuffix = "Request", - ResponseSuffix = "Response", - OpenGeneratedFiles = true - }; - - private IAppSettings LoadFromFile() + public IAppSettings Load() + => LoadFromFile() ?? new AppSettings() { - IAppSettings settings = null; - if (File.Exists(_fullFilePath)) + DestinationFolder = DefaultDestinationFolder, + MinimumLogLevel = LogLevel.Information, + RequestSuffix = "Request", + ResponseSuffix = "Response", + OpenGeneratedFiles = true + }; + + private IAppSettings LoadFromFile() + { + IAppSettings settings = null; + if (File.Exists(_fullFilePath)) + { + try { - try - { - var builder = new ConfigurationBuilder() - .SetBasePath(_basePath) - .AddJsonFile(FileName) - .Build(); - settings = builder.Get(); + var builder = new ConfigurationBuilder() + .SetBasePath(_basePath) + .AddJsonFile(FileName) + .Build(); + settings = builder.Get(); - if (!IsDestinationFolderValid(settings.DestinationFolder)) - { - settings.DestinationFolder = DefaultDestinationFolder; - } - } - catch (Exception ex) + if (!IsDestinationFolderValid(settings.DestinationFolder)) { - _deferredException = ex; + settings.DestinationFolder = DefaultDestinationFolder; } } - return settings; + catch (Exception ex) + { + _deferredException = ex; + } } + return settings; + } - // TO Consider: Move validation from UI to common to reduce code repitition. - private bool IsDestinationFolderValid(string destinationFolder) - { - return !destinationFolder.Any(c => Path.GetInvalidPathChars().Contains(c)) && - (string.IsNullOrWhiteSpace(destinationFolder) || !Path.IsPathRooted(destinationFolder.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))); - } + // TO Consider: Move validation from UI to common to reduce code repitition. + private bool IsDestinationFolderValid(string destinationFolder) + { + return !destinationFolder.Any(c => Path.GetInvalidPathChars().Contains(c)) && + (string.IsNullOrWhiteSpace(destinationFolder) || !Path.IsPathRooted(destinationFolder.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))); + } - public void Save(IAppSettings settings) - { - string json = JsonSerializer.Serialize(settings, _jsonOptions); - Directory.CreateDirectory(_basePath); - File.WriteAllText(_fullFilePath, json); - } + public void Save(IAppSettings settings) + { + string json = JsonSerializer.Serialize(settings, _jsonOptions); + Directory.CreateDirectory(_basePath); + File.WriteAllText(_fullFilePath, json); + } - public void LogDeferredException(ILogger logger) + public void LogDeferredException(ILogger logger) + { + if (logger is null || _deferredException is null) { - if (logger is null || _deferredException is null) - { - return; - } - logger.LogError(_deferredException, "Deffered exception"); - _deferredException = null; + return; } + logger.LogError(_deferredException, "Deffered exception"); + _deferredException = null; } } diff --git a/AutoEntityGenerator/EntityGenerator.cs b/AutoEntityGenerator/EntityGenerator.cs index 6e21c3b..454e4a6 100644 --- a/AutoEntityGenerator/EntityGenerator.cs +++ b/AutoEntityGenerator/EntityGenerator.cs @@ -9,150 +9,149 @@ using System.Linq; using System.Threading; -namespace AutoEntityGenerator -{ - internal interface IEntityGenerator - { - Entity GenerateFromDocument(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken); +namespace AutoEntityGenerator; - Entity GenerateFromUIResult(IUserInteractionResult userInteractionResult, Entity sourceEntity); - } +internal interface IEntityGenerator +{ + Entity GenerateFromDocument(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken); - internal class EntityGenerator : IEntityGenerator - { - private readonly ILogger _logger; + Entity GenerateFromUIResult(IUserInteractionResult userInteractionResult, Entity sourceEntity); +} - public EntityGenerator(ILogger logger) => _logger = logger; +internal class EntityGenerator : IEntityGenerator +{ + private readonly ILogger _logger; - public Entity GenerateFromUIResult(IUserInteractionResult userInteractionResult, Entity sourceEntity) - { - _logger.LogDebug("Attempting to create {entityName} entity based on user interaction result source entity.", userInteractionResult.EntityName); + public EntityGenerator(ILogger logger) => _logger = logger; - var newNamespace = new Namespace() - { - IsFileScoped = sourceEntity.Namespace.IsFileScoped, - Name = sourceEntity.Project.DefaultNamespace + userInteractionResult.TargetDirectory - .Replace(Path.GetDirectoryName(sourceEntity.Project.FilePath), "") - .Replace(Path.DirectorySeparatorChar, '.') - .Replace(Path.AltDirectorySeparatorChar, '.') - }; - var entity = new Entity - { - Constructors = new List(), - Name = userInteractionResult.EntityName, - Namespace = newNamespace, - Project = sourceEntity.Project, - Properties = userInteractionResult.EntityProperties, - SourceFilePath = Path.Combine(userInteractionResult.TargetDirectory, userInteractionResult.FileName), - GenericConstraints = sourceEntity.GenericConstraints, - TypeParameters = sourceEntity.TypeParameters, - }; - _logger.LogDebug("Entity {entityName} created from source entity and user interaction result.", entity.Name); - return entity; - } + public Entity GenerateFromUIResult(IUserInteractionResult userInteractionResult, Entity sourceEntity) + { + _logger.LogDebug("Attempting to create {entityName} entity based on user interaction result source entity.", userInteractionResult.EntityName); - public Entity GenerateFromDocument(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + var newNamespace = new Namespace() + { + IsFileScoped = sourceEntity.Namespace.IsFileScoped, + Name = sourceEntity.Project.DefaultNamespace + userInteractionResult.TargetDirectory + .Replace(Path.GetDirectoryName(sourceEntity.Project.FilePath), "") + .Replace(Path.DirectorySeparatorChar, '.') + .Replace(Path.AltDirectorySeparatorChar, '.') + }; + var entity = new Entity { - _logger.LogDebug("Attempting to create entity from document info."); + Constructors = new List(), + Name = userInteractionResult.EntityName, + Namespace = newNamespace, + Project = sourceEntity.Project, + Properties = userInteractionResult.EntityProperties, + SourceFilePath = Path.Combine(userInteractionResult.TargetDirectory, userInteractionResult.FileName), + GenericConstraints = sourceEntity.GenericConstraints, + TypeParameters = sourceEntity.TypeParameters, + }; + _logger.LogDebug("Entity {entityName} created from source entity and user interaction result.", entity.Name); + return entity; + } - var typeParameters = typeDeclaration.TypeParameterList is null - ? Enumerable.Empty() - : typeDeclaration.TypeParameterList.Parameters.Select(p => p.Identifier.Text); - var genericConstraints = typeDeclaration.ConstraintClauses.Select(c => c.ToString()); + public Entity GenerateFromDocument(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + { + _logger.LogDebug("Attempting to create entity from document info."); + var typeParameters = typeDeclaration.TypeParameterList is null + ? Enumerable.Empty() + : typeDeclaration.TypeParameterList.Parameters.Select(p => p.Identifier.Text); + var genericConstraints = typeDeclaration.ConstraintClauses.Select(c => c.ToString()); - var entity = new Entity - { - Constructors = GenerateConstructors(typeSymbol, cancellationToken), - GenericConstraints = genericConstraints.ToList(), - Name = typeSymbol.Name, - Namespace = GenerateNamespace(typeDeclaration), - Project = GenerateProject(document), - Properties = GenerateProperties(typeSymbol, cancellationToken), - SourceFilePath = document.FilePath, - TypeParameters = typeParameters.ToList() - }; - _logger.LogDebug("Entity {entityName} created from document info.", entity.Name); - return entity; - } - private Namespace GenerateNamespace(TypeDeclarationSyntax typeDeclaration) + var entity = new Entity { - var namespaceDeclaration = typeDeclaration.FirstAncestorOrSelf(); + Constructors = GenerateConstructors(typeSymbol, cancellationToken), + GenericConstraints = genericConstraints.ToList(), + Name = typeSymbol.Name, + Namespace = GenerateNamespace(typeDeclaration), + Project = GenerateProject(document), + Properties = GenerateProperties(typeSymbol, cancellationToken), + SourceFilePath = document.FilePath, + TypeParameters = typeParameters.ToList() + }; + _logger.LogDebug("Entity {entityName} created from document info.", entity.Name); + return entity; + } - return new Namespace() - { - Name = namespaceDeclaration?.Name.ToString() ?? "", - IsFileScoped = namespaceDeclaration is FileScopedNamespaceDeclarationSyntax - }; - } + private Namespace GenerateNamespace(TypeDeclarationSyntax typeDeclaration) + { + var namespaceDeclaration = typeDeclaration.FirstAncestorOrSelf(); - private Common.CodeInfo.Project GenerateProject(Document document) + return new Namespace() { - var lanugageVersion = (int?)((document.Project.ParseOptions as CSharpParseOptions)?.LanguageVersion); - return new Common.CodeInfo.Project - { - DefaultNamespace = document.Project.DefaultNamespace, - FilePath = document.Project.FilePath, - CSharpVersion = (CSharpVersion)(lanugageVersion ?? -1) - }; - } + Name = namespaceDeclaration?.Name.ToString() ?? "", + IsFileScoped = namespaceDeclaration is FileScopedNamespaceDeclarationSyntax + }; + } - private List GenerateConstructors(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + private Common.CodeInfo.Project GenerateProject(Document document) + { + var lanugageVersion = (int?)((document.Project.ParseOptions as CSharpParseOptions)?.LanguageVersion); + return new Common.CodeInfo.Project { - var constructors = typeSymbol.Constructors; - var constructorInfos = new List(); + DefaultNamespace = document.Project.DefaultNamespace, + FilePath = document.Project.FilePath, + CSharpVersion = (CSharpVersion)(lanugageVersion ?? -1) + }; + } - foreach (var constructor in constructors) + private List GenerateConstructors(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + { + var constructors = typeSymbol.Constructors; + var constructorInfos = new List(); + + foreach (var constructor in constructors) + { + if (cancellationToken.IsCancellationRequested) { - if (cancellationToken.IsCancellationRequested) + _logger.LogDebug("Cancellation requested while generating {TypeSymbolName} constructors.", typeSymbol.Name); + break; + } + + var parameters = constructor.Parameters + .Select(parameter => new Parameter { - _logger.LogDebug("Cancellation requested while generating {TypeSymbolName} constructors.", typeSymbol.Name); - break; - } + Name = parameter.Name, + Type = parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + }); - var parameters = constructor.Parameters - .Select(parameter => new Parameter - { - Name = parameter.Name, - Type = parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) - }); + var constructorInfo = new Constructor(parameters); - var constructorInfo = new Constructor(parameters); + constructorInfos.Add(constructorInfo); + } - constructorInfos.Add(constructorInfo); - } + return constructorInfos; + } - return constructorInfos; - } + private List GenerateProperties(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + { + var properties = new List(); - private List GenerateProperties(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + while (typeSymbol != null && typeSymbol.Name != nameof(System.Object)) { - var properties = new List(); - - while (typeSymbol != null && typeSymbol.Name != nameof(System.Object)) + if (cancellationToken.IsCancellationRequested) { - if (cancellationToken.IsCancellationRequested) - { - _logger.LogDebug("Cancellation requested while generating {TypeSymbolName} properties.", typeSymbol.Name); - break; - } - - var classProperties = typeSymbol.GetMembers() - .OfType() - .Where(p => p.DeclaredAccessibility == Accessibility.Public) - .Select(p => new Property - { - Name = p.Name, - Type = p.Type.ToString(), - IsReadonly = p.SetMethod is null || p.SetMethod.DeclaredAccessibility != Accessibility.Public - }); - - properties.AddRange(classProperties); - typeSymbol = typeSymbol.BaseType; + _logger.LogDebug("Cancellation requested while generating {TypeSymbolName} properties.", typeSymbol.Name); + break; } - return properties; + var classProperties = typeSymbol.GetMembers() + .OfType() + .Where(p => p.DeclaredAccessibility == Accessibility.Public) + .Select(p => new Property + { + Name = p.Name, + Type = p.Type.ToString(), + IsReadonly = p.SetMethod is null || p.SetMethod.DeclaredAccessibility != Accessibility.Public + }); + + properties.AddRange(classProperties); + typeSymbol = typeSymbol.BaseType; } + + return properties; } } diff --git a/AutoEntityGenerator/EntityGeneratorCodeAction.cs b/AutoEntityGenerator/EntityGeneratorCodeAction.cs index 1e1d98a..a76e07c 100644 --- a/AutoEntityGenerator/EntityGeneratorCodeAction.cs +++ b/AutoEntityGenerator/EntityGeneratorCodeAction.cs @@ -8,44 +8,43 @@ using System.Threading; using System.Threading.Tasks; -namespace AutoEntityGenerator +namespace AutoEntityGenerator; + +internal class EntityGeneratorCodeAction : CodeAction { - internal class EntityGeneratorCodeAction : CodeAction - { - private readonly ILogger _logger; - private readonly Document _document; - private readonly TypeDeclarationSyntax _typeDeclaration; - private readonly ICodeActionFactory _codeActionFactory; - private readonly INamedTypeSymbol _typeSymbol; + private readonly ILogger _logger; + private readonly Document _document; + private readonly TypeDeclarationSyntax _typeDeclaration; + private readonly ICodeActionFactory _codeActionFactory; + private readonly INamedTypeSymbol _typeSymbol; - public EntityGeneratorCodeAction(ILogger logger, Document document, TypeDeclarationSyntax typeDeclaration, ICodeActionFactory codeActionFactory, INamedTypeSymbol typeSymbol) - { - _logger = logger; - _document = document; - _typeDeclaration = typeDeclaration; - _codeActionFactory = codeActionFactory; - _typeSymbol = typeSymbol; - } + public EntityGeneratorCodeAction(ILogger logger, Document document, TypeDeclarationSyntax typeDeclaration, ICodeActionFactory codeActionFactory, INamedTypeSymbol typeSymbol) + { + _logger = logger; + _document = document; + _typeDeclaration = typeDeclaration; + _codeActionFactory = codeActionFactory; + _typeSymbol = typeSymbol; + } - public override string Title => "🔧 Generate DTO and mapping 🛠️"; + public override string Title => "🔧 Generate DTO and mapping 🛠️"; - protected override Task> ComputeOperationsAsync(CancellationToken cancellationToken) - { - var getEntityInfoOperation = _codeActionFactory.CreateGetEntityInfoOperation(_document, _typeDeclaration, _typeSymbol); - var getUserInputOperation = _codeActionFactory.CreateGetUserInputOperation(getEntityInfoOperation); - var generateCodeOperation = _codeActionFactory.CreateGenerateCodeOperation(getUserInputOperation, getEntityInfoOperation, _document); - _logger.LogDebug($"Code action operations created: {nameof(GetEntityInfoOperation)}, {nameof(GetUserInputOperation)}, {nameof(GenerateCodeOperation)}"); - return Task.FromResult>(new CodeActionOperation[] - { - getEntityInfoOperation, - getUserInputOperation, - generateCodeOperation - }); - } + protected override Task> ComputeOperationsAsync(CancellationToken cancellationToken) + { + var getEntityInfoOperation = _codeActionFactory.CreateGetEntityInfoOperation(_document, _typeDeclaration, _typeSymbol); + var getUserInputOperation = _codeActionFactory.CreateGetUserInputOperation(getEntityInfoOperation); + var generateCodeOperation = _codeActionFactory.CreateGenerateCodeOperation(getUserInputOperation, getEntityInfoOperation, _document); + _logger.LogDebug($"Code action operations created: {nameof(GetEntityInfoOperation)}, {nameof(GetUserInputOperation)}, {nameof(GenerateCodeOperation)}"); + return Task.FromResult>( + [ + getEntityInfoOperation, + getUserInputOperation, + generateCodeOperation + ]); + } - protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) - { - return Task.FromResult(Enumerable.Empty()); - } + protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + { + return Task.FromResult(Enumerable.Empty()); } } diff --git a/AutoEntityGenerator/Services.cs b/AutoEntityGenerator/Services.cs index 9ec4352..35f76c1 100644 --- a/AutoEntityGenerator/Services.cs +++ b/AutoEntityGenerator/Services.cs @@ -7,123 +7,122 @@ using System.Diagnostics; using System.Threading.Tasks; -namespace AutoEntityGenerator +namespace AutoEntityGenerator; + +public class Services : IServices { - public class Services : IServices - { - private readonly ServiceCollection _services; - private readonly ConfigurationService _configurationService; - private readonly IAppSettings _appSettings; + private readonly ServiceCollection _services; + private readonly ConfigurationService _configurationService; + private readonly IAppSettings _appSettings; - private IServiceProvider _serviceProvider; - private int _numberOfservices; - private static readonly Lazy _lazy = new Lazy(() => new Services()); + private IServiceProvider _serviceProvider; + private int _numberOfservices; + private static readonly Lazy _lazy = new(() => new Services()); - public static IServices Instance => _lazy.Value; + public static IServices Instance => _lazy.Value; - private Services() - { - _services = new ServiceCollection(); - _configurationService = new ConfigurationService(); - _appSettings = _configurationService.Load(); - ConfigureServices(); - _serviceProvider = _services.BuildServiceProvider(); - _numberOfservices = _services.Count; - ConfigureGlobalExceptionHandling(); - LogServiceConfigurationDeferredError(); - } + private Services() + { + _services = new ServiceCollection(); + _configurationService = new ConfigurationService(); + _appSettings = _configurationService.Load(); + ConfigureServices(); + _serviceProvider = _services.BuildServiceProvider(); + _numberOfservices = _services.Count; + ConfigureGlobalExceptionHandling(); + LogServiceConfigurationDeferredError(); + } - private void ConfigureServices() - { - AddLogger() - .AddSingleton(_configurationService) - .AddSingleton(_appSettings) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddCodeGenerator() - .AddUI(); - } + private void ConfigureServices() + { + AddLogger() + .AddSingleton(_configurationService) + .AddSingleton(_appSettings) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddCodeGenerator() + .AddUI(); + } - // TO Consider: Switch to using file based log: Serilog / Nlog / Visual studio's Activity log. - private Services AddLogger() + // TO Consider: Switch to using file based log: Serilog / Nlog / Visual studio's Activity log. + private Services AddLogger() + { + const string sourceName = nameof(AutoEntityGenerator); + if (!EventLog.SourceExists(sourceName)) { - const string sourceName = nameof(AutoEntityGenerator); - if (!EventLog.SourceExists(sourceName)) - { - EventLog.CreateEventSource(sourceName, "Application"); - } - - _services.AddLogging(buider => buider - .SetMinimumLevel(_appSettings.MinimumLogLevel) - .AddEventLog(settings => - { - settings.SourceName = sourceName; - }) - ); - return this; + EventLog.CreateEventSource(sourceName, "Application"); } - private void ConfigureGlobalExceptionHandling() - { - var logger = GetService>(); - - AppDomain.CurrentDomain.UnhandledException += (sender, args) => + _services.AddLogging(buider => buider + .SetMinimumLevel(_appSettings.MinimumLogLevel) + .AddEventLog(settings => { - logger.LogError(args.ExceptionObject as Exception, "Unhandled domain exception"); - }; + settings.SourceName = sourceName; + }) + ); + return this; + } - TaskScheduler.UnobservedTaskException += (sender, args) => - { - logger.LogError(args.Exception, "Unobserved task exception"); - foreach (var inner in args.Exception.InnerExceptions) - { - logger.LogError(inner, "inner exception"); - } - args.SetObserved(); - }; - - logger.AddUIRelatedGlobalExceptionHandling(); - } + private void ConfigureGlobalExceptionHandling() + { + var logger = GetService>(); - private void LogServiceConfigurationDeferredError() + AppDomain.CurrentDomain.UnhandledException += (sender, args) => { - var logger = GetService>(); - _configurationService.LogDeferredException(logger); - } + logger.LogError(args.ExceptionObject as Exception, "Unhandled domain exception"); + }; - public T GetService() + TaskScheduler.UnobservedTaskException += (sender, args) => { - if (_services.Count > _numberOfservices) + logger.LogError(args.Exception, "Unobserved task exception"); + foreach (var inner in args.Exception.InnerExceptions) { - _serviceProvider = _services.BuildServiceProvider(); - _numberOfservices = _services.Count; + logger.LogError(inner, "inner exception"); } - return _serviceProvider.GetService(); - } + args.SetObserved(); + }; - public IServices AddSingleton() where TService : class where TImplementation : class, TService - { - _services.AddSingleton(); - return this; - } + logger.AddUIRelatedGlobalExceptionHandling(); + } - public IServices AddSingleton() where TImplementation : class - { - _services.AddSingleton(); - return this; - } + private void LogServiceConfigurationDeferredError() + { + var logger = GetService>(); + _configurationService.LogDeferredException(logger); + } - public IServices AddSingleton(TImplementation instance) where TImplementation : class + public T GetService() + { + if (_services.Count > _numberOfservices) { - _services.AddSingleton(instance); - return this; + _serviceProvider = _services.BuildServiceProvider(); + _numberOfservices = _services.Count; } + return _serviceProvider.GetService(); + } - public IServices AddTransient() where TService : class - { - _services.AddTransient(); - return this; - } + public IServices AddSingleton() where TService : class where TImplementation : class, TService + { + _services.AddSingleton(); + return this; + } + + public IServices AddSingleton() where TImplementation : class + { + _services.AddSingleton(); + return this; + } + + public IServices AddSingleton(TImplementation instance) where TImplementation : class + { + _services.AddSingleton(instance); + return this; + } + + public IServices AddTransient() where TService : class + { + _services.AddTransient(); + return this; } }