diff --git a/src/BlueWest.EfMethods/EfMethods/EfAddMethodsAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddMethodsAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfAddMethodsAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddMethodsAttributeSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddToListAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddToListAttributeSource.cs new file mode 100644 index 0000000..aac5979 --- /dev/null +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddToListAttributeSource.cs @@ -0,0 +1,43 @@ +using BlueWest.EfMethods.Sources; +using static BlueWest.EfMethods.Sources.Constants; + +namespace BlueWest.EfMethods.EfMethods.AttributeSources +{ + public static class EfAddToListAttributeSource + { + internal const string AttributeName = "EfAddToList"; + internal const string AttributeClassName = AttributeName + "Attribute"; + internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; + + internal static SourceCode Generate(SourceGenerationOptions options) + { + using var builder = new SourceBuilder() + .WriteLine(GeneratedFilesHeader) + .WriteLine("using System;") + .WriteLine() + .WriteLine($"namespace {RootNamespace}") + .WriteOpeningBracket(); + + builder + .WriteLine("/// ") + .WriteLine("/// Attribute for generating a function to get many entities.") + .WriteLine("/// "); + + + builder + .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]") + .WriteLine($"public sealed class {AttributeName}Attribute : Attribute") + .WriteOpeningBracket(); + + builder + .WriteLine($"public {AttributeName}Attribute(string listPropertyNameof, Type createType, Type returnType, string primaryKeyNameof){"{}"}") + .WriteLine(); + + builder + .WriteClosingBracket() // class + .WriteClosingBracket(); // namespace + + return new(builder.ToString(), $"{AttributeName}Attribute.g.cs"); + } + } +} \ No newline at end of file diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetListAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetListAttributeSource.cs new file mode 100644 index 0000000..3c91a56 --- /dev/null +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetListAttributeSource.cs @@ -0,0 +1,44 @@ +using BlueWest.EfMethods; +using BlueWest.EfMethods.Sources; +using static BlueWest.EfMethods.Sources.Constants; + +namespace BlueWest.Sources +{ + public class EfGetListAttributeSource + { + internal const string AttributeName = "EfGetList"; + internal const string AttributeClassName = AttributeName + "Attribute"; + internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; + + internal static SourceCode Generate(SourceGenerationOptions options) + { + using var builder = new SourceBuilder() + .WriteLine(GeneratedFilesHeader) + .WriteLine("using System;") + .WriteLine() + .WriteLine($"namespace {RootNamespace}") + .WriteOpeningBracket(); + + builder + .WriteLine("/// ") + .WriteLine("/// Attribute for generating a function to get many entities.") + .WriteLine("/// "); + + + builder + .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]") + .WriteLine($"public sealed class {AttributeName}Attribute : Attribute") + .WriteOpeningBracket(); + + builder + .WriteLine($"public {AttributeName}Attribute(string listPropertyNameof, Type returnType, string primaryKeyNameof){"{}"}") + .WriteLine(); + + builder + .WriteClosingBracket() // class + .WriteClosingBracket(); // namespace + + return new(builder.ToString(), $"{AttributeName}Attribute.g.cs"); + } + } +} \ No newline at end of file diff --git a/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs b/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs index b44f9ed..9153e3c 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs @@ -4,8 +4,10 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; +using BlueWest.EfMethods.EfMethods.AttributeSources; using BlueWest.EfMethods.Extensions; using BlueWest.EfMethods.Sources; +using BlueWest.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -113,14 +115,18 @@ namespace BlueWest.EfMethods return new TypeInfo(); } - protected TypeInfo? FindFirstTypeInfoOfMemberExpressionSyntax(SemanticModel semanticModel, SyntaxNode attributeSyntax) + protected TypeInfo? FindTypeInfoOfMemberExpressionSyntax(SemanticModel semanticModel, SyntaxNode attributeSyntax, int skip = 0) { semanticModel ??= Compilation.GetSemanticModel(attributeSyntax.SyntaxTree); var descendentNodes = attributeSyntax .DescendantNodes(); // Get the type of the key member id - var expression = descendentNodes.OfType().FirstOrDefault(); + var expression = descendentNodes + .OfType() + .Skip(skip) + .FirstOrDefault(); + if (expression != null) { return semanticModel.GetTypeInfo(expression); @@ -130,10 +136,10 @@ namespace BlueWest.EfMethods } - protected string ExtractNameOfMemberName(MemberDeclarationSyntax memberDeclarationSyntax, string attributeName, SemanticModel? semanticModel = null) + protected string ExtractNameOfMemberName(MemberDeclarationSyntax memberDeclarationSyntax, string attributeName, SemanticModel? semanticModel = null, int skip = 0) { var attributeData = memberDeclarationSyntax.GetAttribute(attributeName); - var sourceSymbol = ExtractNameOfMemberName(attributeData, semanticModel); + var sourceSymbol = ExtractNameOfMemberName(attributeData, semanticModel, skip); return sourceSymbol; } @@ -184,7 +190,7 @@ namespace BlueWest.EfMethods return sourceTypeExpressionSyntax; } - protected string ExtractNameOfMemberName(SyntaxNode? attributeSyntax, SemanticModel semanticModel) + protected string ExtractNameOfMemberName(SyntaxNode? attributeSyntax, SemanticModel semanticModel, int skip) { if (attributeSyntax is null) { @@ -195,6 +201,7 @@ namespace BlueWest.EfMethods .DescendantNodes(); var idNode = descendentNodes.OfType() + .Skip(skip) .FirstOrDefault() ?.ChildNodesAndTokens() .FirstOrDefault(x => x.IsKind(SyntaxKind.ArgumentList)) @@ -262,17 +269,19 @@ namespace BlueWest.EfMethods // handles different attributes - var addAttributeSymbols = GetAllowedMemberSyntaxes(EfAddMethodsAttributeSource.AttributeName); - var updateAttributesMembers = GetAllowedMemberSyntaxes(EfUpdateMethodsAttributeSource.AttributeName); - var getOneAttributesMembers = GetAllowedMemberSyntaxes(EfGetOneByAttributeSource.AttributeName); - var getOneWithAttributesMembers = GetAllowedMemberSyntaxes(EfGetOneAttributeSource.AttributeName); - var getManyWithAttributesMembers = GetAllowedMemberSyntaxes(EfGetManyAttributeSource.AttributeName); - + var addMembers = GetAllowedMemberSyntaxes(EfAddMethodsAttributeSource.AttributeName); + var updateMembers = GetAllowedMemberSyntaxes(EfUpdateMethodsAttributeSource.AttributeName); + var getOneByMembers = GetAllowedMemberSyntaxes(EfGetOneByAttributeSource.AttributeName); + var getOneMembers = GetAllowedMemberSyntaxes(EfGetOneAttributeSource.AttributeName); + var efGetManyMembers = GetAllowedMemberSyntaxes(EfGetManyAttributeSource.AttributeName); + var getListMembers = GetAllowedMemberSyntaxes(EfGetListAttributeSource.AttributeName); + var getAddToListMembers = GetAllowedMemberSyntaxes(EfAddToListAttributeSource.AttributeName); + List methodsModels = new List(); - foreach (var uProperty in getOneAttributesMembers) + foreach (var uProperty in getOneByMembers) { var entityDataModel = GetEntityData(uProperty, semanticModel); var newUpdateModel = ExtractEfGetEntityByModel(uProperty, semanticModel, entityDataModel); @@ -280,14 +289,14 @@ namespace BlueWest.EfMethods } - foreach (var uProperty in addAttributeSymbols) + foreach (var uProperty in addMembers) { var entityDataModel = GetEntityData(uProperty, semanticModel); var newAddModel = ExtractEfAddMethodsModel(semanticModel, uProperty, entityDataModel); methodsModels.Add(newAddModel); } - foreach (var uProperty in updateAttributesMembers) + foreach (var uProperty in updateMembers) { var entityDataModel = GetEntityData(uProperty, semanticModel); var newUpdateModel = ExtractEfUpdateMethodsModel(uProperty, semanticModel, entityDataModel); @@ -295,7 +304,7 @@ namespace BlueWest.EfMethods } - foreach (var uProperty in getOneWithAttributesMembers) + foreach (var uProperty in getOneMembers) { var entityDataModel = GetEntityData(uProperty, semanticModel); var newUpdateModel = ExtractEfGetEntityWithModel(uProperty, semanticModel, entityDataModel); @@ -303,12 +312,29 @@ namespace BlueWest.EfMethods } - foreach (var uProperty in getManyWithAttributesMembers) + foreach (var uProperty in efGetManyMembers) { var entityDataModel = GetEntityData(uProperty, semanticModel); var newUpdateModel = ExtractEfGetManyModel(uProperty, semanticModel, entityDataModel); methodsModels.Add(newUpdateModel); } + + foreach (var uProperty in getListMembers) + { + var entityDataModel = GetEntityData(uProperty, semanticModel); + var newModel = ExtractEfGetListModel(uProperty, semanticModel, entityDataModel); + methodsModels.Add(newModel); + } + + + foreach (var uProperty in getAddToListMembers) + { + var entityDataModel = GetEntityData(uProperty, semanticModel); + var newModel = ExtractEfAddToListMethodsModel(uProperty, semanticModel, entityDataModel); + methodsModels.Add(newModel); + } + + //SpinWait.SpinUntil(() => Debugger.IsAttached); @@ -364,7 +390,7 @@ namespace BlueWest.EfMethods GetEntityTypeSymbol(uProperty, EfUpdateMethodsAttributeSource.AttributeName, semanticModel); var keyPropertyName = ExtractNameOfMemberName(uProperty, EfUpdateMethodsAttributeSource.AttributeName, semanticModel); - var keyMemberType = FindFirstTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); + var keyMemberType = FindTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); //var secondTypeInfo = FindSecondTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); var keyMemberFullName = keyMemberType.Value.Type.ToDisplayString(); @@ -453,7 +479,7 @@ namespace BlueWest.EfMethods var efTypeofSymbols = GetEntityTypeSymbol(uProperty, EfGetOneByAttributeSource.AttributeName, semanticModel); var byParamPropertyName = ExtractNameOfMemberName(uProperty, EfGetOneByAttributeSource.AttributeName, semanticModel); - var keyMemberType = FindFirstTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); + var keyMemberType = FindTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); var byParamFullTypeName = keyMemberType.Value.Type.ToDisplayString(); @@ -482,6 +508,99 @@ namespace BlueWest.EfMethods return new EfGetOneByModel(entityDataModel, byParamPropertyName, byParamFullTypeName, returnTypeFullName, returnTypeIdentifierName); } + + private EfGetListModel ExtractEfGetListModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel) + { + + var efTypeofSymbols = GetEntityTypeSymbol(uProperty, EfGetListAttributeSource.AttributeName, semanticModel); + + var listPropertyName = ExtractNameOfMemberName(uProperty, EfGetListAttributeSource.AttributeName, semanticModel); + var keyPropertyName = ExtractNameOfMemberName(uProperty, EfGetListAttributeSource.AttributeName, semanticModel, 1);; + var keyMemberType = FindTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); + var keyFullTypeName = keyMemberType.Value.Type.ToDisplayString(); + + + // Try grabbing string literal if there's no nameof in it + if (keyPropertyName == string.Empty) + { + listPropertyName = GetEntityStringLiteralSymbol(TypeSyntax, EfGetListAttributeSource.AttributeName, semanticModel) + .FirstOrDefault()? + .Token.ValueText ?? "Id"; + } + + + string returnTypeIdentifierName = entityDataModel.EntityTypeIdentifierName; + string returnTypeFullName = entityDataModel.EntityTypeFullName; + + + // Grab return type from attribute argument + if (efTypeofSymbols.Length > 0) + { + returnTypeIdentifierName = efTypeofSymbols[0].Name; + returnTypeFullName = efTypeofSymbols[0].ToDisplayString(); + } + + return new EfGetListModel(entityDataModel, listPropertyName, keyPropertyName, keyFullTypeName, returnTypeIdentifierName, returnTypeFullName); + } + + private EfAddToListModel ExtractEfAddToListMethodsModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel) + { + + //SpinWait.SpinUntil(() => Debugger.IsAttached); + + var efTypeofSymbols = GetEntityTypeSymbol(uProperty, EfAddToListAttributeSource.AttributeName, semanticModel); + + var listPropertyName = ExtractNameOfMemberName(uProperty, EfAddToListAttributeSource.AttributeName, semanticModel); + var keyPropertyName = ExtractNameOfMemberName(uProperty, EfAddToListAttributeSource.AttributeName, semanticModel, 1);; + var keyMemberType = FindTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax); + var keyFullTypeName = keyMemberType.Value.Type.ToDisplayString(); + var listEntityTypeInfo = (FindTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax, 2).Value.Type as INamedTypeSymbol).TypeArguments.FirstOrDefault(); + var listEntityIdentifierName = listEntityTypeInfo.Name; + var listEntityFullTypeName = listEntityTypeInfo.ToDisplayString(); + + + // Try grabbing string literal if there's no nameof in it + if (keyPropertyName == string.Empty) + { + listPropertyName = GetEntityStringLiteralSymbol(TypeSyntax, EfAddToListAttributeSource.AttributeName, semanticModel) + .FirstOrDefault()? + .Token.ValueText ?? "Id"; + } + + + string returnTypeIdentifierName = entityDataModel.EntityTypeIdentifierName; + string returnTypeFullName = entityDataModel.EntityTypeFullName; + + var createTypeIdentifierName =listEntityIdentifierName; + var createTypeFullName = listEntityFullTypeName; + + // Grab create type from attribute argument + if (efTypeofSymbols.Length > 0) + { + createTypeIdentifierName = efTypeofSymbols[0].Name; + createTypeFullName = efTypeofSymbols[0].ToDisplayString(); + } + + // Grab return type from attribute argument + if (efTypeofSymbols.Length > 1) + { + returnTypeIdentifierName = efTypeofSymbols[1].Name; + returnTypeFullName = efTypeofSymbols[1].ToDisplayString(); + } + + return new EfAddToListModel( + entityDataModel, + listPropertyName, + listEntityIdentifierName, + listEntityFullTypeName, + keyPropertyName, + keyFullTypeName, + createTypeIdentifierName, + createTypeFullName, + returnTypeIdentifierName, + returnTypeFullName); + } + private static ITypeSymbol GetEntityTypeData(MemberDeclarationSyntax memberDeclarationSyntax, SemanticModel? semanticModel = null) { diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs index 73f48e9..894c5c2 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using BlueWest.EfMethods.EfMethods.AttributeSources; using BlueWest.EfMethods.Extensions; using BlueWest.EfMethods.Sources; +using BlueWest.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -35,7 +37,11 @@ namespace BlueWest.EfMethods .AddSource(ref context, EfUpdateMethodsAttributeSource.Generate(options)) .AddSource(ref context, EfGetOneByAttributeSource.Generate(options)) .AddSource(ref context, EfGetOneAttributeSource.Generate(options)) - .AddSource(ref context, EfGetManyAttributeSource.Generate(options)); + .AddSource(ref context, EfGetManyAttributeSource.Generate(options)) + .AddSource(ref context, EfGetListAttributeSource.Generate(options)) + .AddSource(ref context, EfAddToListAttributeSource.Generate(options)); + + if (context.SyntaxReceiver is EfMethodsSyntaxReceiver receiver && receiver.CandidateTypes.Any()) @@ -50,15 +56,29 @@ namespace BlueWest.EfMethods } } + private static EfTemplates GetAvailableTemplates(GeneratorExecutionContext context) + { + string addSourceTemplate = GetCsxMethodTemplate(context, "AddToTemplate"); + string updateSourceTemplate = GetCsxMethodTemplate(context, "UpdateTemplate"); + string getOneByTemplate = GetCsxMethodTemplate(context, "GetOneByTemplate"); + string getOneTemplate = GetCsxMethodTemplate(context, "GetOneTemplate"); + string getManyWithTemplate = GetCsxMethodTemplate(context, "GetManyTemplate"); + string getListTemplate = GetCsxMethodTemplate(context, "GetListTemplate"); + string addToListTemplate = GetCsxMethodTemplate(context, "AddToListTemplate"); + + return new EfTemplates( + addSourceTemplate, + updateSourceTemplate, + getOneByTemplate, + getOneTemplate, + getManyWithTemplate, + getListTemplate, + addToListTemplate); + } + private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateMembers, SourceGenerationOptions options) { - string addSourceTemplate = GetCsxMethodTemplate(context, "AddToEntityTemplate"); - string updateSourceTemplate = GetCsxMethodTemplate(context, "UpdateEntityTemplate"); - string getOneByTemplate = GetCsxMethodTemplate(context, "GetOneByTemplate"); - string getOneWithByTemplate = GetCsxMethodTemplate(context, "GetOneTemplate"); - string getManyWithTemplate = GetCsxMethodTemplate(context, "GetManyTemplate"); - - + var templates = GetAvailableTemplates(context); foreach (var candidateMember in candidateMembers) { @@ -76,11 +96,7 @@ namespace BlueWest.EfMethods { ClassDeclarationSyntax => EfMethodsSource.Generate( mappingContext.Model, - addSourceTemplate, - updateSourceTemplate, - getOneByTemplate, - getOneWithByTemplate, - getManyWithTemplate), + templates), _ => throw new ArgumentOutOfRangeException() }; diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs index 9f9a2e4..b054919 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs @@ -11,19 +11,10 @@ namespace BlueWest.EfMethods.Sources { internal static class EfMethodsSource { - internal static SourceCode Generate(EfMethodsModel model, - string addSourceTemplate, - string updateSourceTemplate, - string getOneBySourceTemplate, - string getOneWithSourceTemplate, - string getManyWithSourceTemplate - ) + internal static SourceCode Generate(EfMethodsModel model, EfTemplates templates) { using var builder = new SourceBuilder(); - - - var contextFullName = model.ContextFullType; - + builder .WriteLine(GeneratedFilesHeader) .WriteUsings(model.Usings) @@ -31,7 +22,6 @@ namespace BlueWest.EfMethods.Sources .WriteLine("using System.Linq;") .WriteLine("using System.Linq.Expressions;") .WriteLine("using System.Threading.Tasks;") - .WriteLine() // Namespace declaration .WriteLine($"namespace {model.Namespace}") @@ -41,46 +31,53 @@ namespace BlueWest.EfMethods.Sources .WriteOpeningBracket() .WriteLine(); - foreach (var methodModel in model.MethodsModels.OrderBy(x => x.PropertyName)) { switch (methodModel) { - case EfAddMethodsModel modl: + case EfAddMethodsModel currentModel: builder - .EfAddAddEntityMethod(model, modl, addSourceTemplate) + .EfAddAddMethod(model, currentModel, templates.AddTemplate) + .WriteLine(); + break; + + case EfUpdateMethodsModel currentModel: + builder + .EfAddUpdateMethod(model, currentModel, templates.UpdateTemplate) + .WriteLine(); + break; + + case EfGetOneByModel currentModel: + builder + .EfAddGetOneEntityByMethod(model, currentModel, templates.GetOneByTemplate) + .WriteLine(); + break; + + case EfGetManyModel currentModel: + builder + .EfAddGetManyMethod(model, currentModel, templates.GetManyTemplate) + .WriteLine(); + break; + + case EfGetOneWithModel currentModel: + builder + .EfAddGetOneMethod(model, currentModel, templates.GetOneTemplate) + .WriteLine(); + break; + case EfGetListModel currentModel: + builder + .EfAddGetListMethod(model, currentModel, templates.GetListTemplate) + .WriteLine(); + break; + case EfAddToListModel currentModel: + builder + .EfAddAddToListMethod(model, currentModel, templates.AddToListTemplate) .WriteLine(); break; - case EfUpdateMethodsModel modl: - builder - .EfAddUpdateEntityMethod(model, modl, updateSourceTemplate) - .WriteLine(); - break; - - case EfGetOneByModel modl: - builder - .EfAddGetOneEntityByMethod(model, modl, getOneBySourceTemplate) - .WriteLine(); - break; - case EfGetManyModel modl: - builder - .EfAddGetManyWithMethod(model, modl, getManyWithSourceTemplate) - .WriteLine(); - break; - case EfGetOneWithModel modl: - builder - .EfAddGetOneEntityWithMethod(model, modl, getOneWithSourceTemplate) - .WriteLine(); - break; - - - } - - } - + builder // End class declaration .WriteClosingBracket() @@ -105,9 +102,5 @@ namespace BlueWest.EfMethods.Sources return builder; } - - - - } } \ No newline at end of file diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs index ce22977..19acc91 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs @@ -1,13 +1,12 @@ using BlueWest.EfMethods.Extensions; -using System; -using System.Collections.Generic; using System.Text; +using BlueWest.EfMethods.Sources; -namespace BlueWest.EfMethods.Sources +namespace BlueWest.EfMethods { internal static class EfAddEntityTemplateSource { - internal static SourceBuilder EfAddAddEntityMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfAddMethodsModel model, string addSourceTemplate) + internal static SourceBuilder EfAddAddMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfAddMethodsModel model, string addSourceTemplate) { var returnTypeFullName = model.ReturnTypeFullName; var createTypeFullName = model.CreateTypeFullName; diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs new file mode 100644 index 0000000..8bb68ff --- /dev/null +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs @@ -0,0 +1,78 @@ +using System.Text; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; + +namespace BlueWest.EfMethods +{ + internal static class EfAddToListTemplateSource + { + internal static SourceBuilder EfAddAddToListMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfAddToListModel model, string template) + { + var returnTypeFullName = model.ReturnTypeFullName; + var entityTypeName = model.EntityTypeIdentifierName; + var listPropertyName = model.ListPropertyName; + var listEntityCreateFullName = model.CreateTypeFullName; + var listItemCreateVarName = model.ListEntityIdentifierName.ToCamelCase() + "toCreate"; + var listEntityTypeName = model.ListEntityIdentifierName; + var listEntityFullName = model.ListEntityFullTypeName; + + var contextFullName = methodsModel.ContextFullType; + var propertyName = model.PropertyName; + var primaryKeyFullName = model.KeyFullTypeName; + var primaryKeyPropertyName = model.KeyPropertyName; + var primaryKeyVarName = model.KeyPropertyName.ToCamelCase(); + var entityObjectVarName = model.EntityTypeIdentifierName.ToCamelCase(); + + + if (!template.IsEmpty()) + { + var templateToSourceBuilder = new StringBuilder(template); + templateToSourceBuilder + .Replace("{returnTypeFullName}", returnTypeFullName) + .Replace("{entityTypeName}", entityTypeName) + .Replace("{contextFullName}", contextFullName) + .Replace("{listPropertyName}", listPropertyName) + .Replace("{primaryKeyPropertyName}", primaryKeyPropertyName) + .Replace("{primaryKeyFullName}", primaryKeyFullName) + .Replace("{primaryKeyVarName}", primaryKeyVarName) + .Replace("{entityObjectVarName}", entityObjectVarName) + .Replace("{propertyName}", propertyName) + .Replace("{listEntityCreateFullName}", listEntityCreateFullName) + .Replace("{listEntityTypeName}", listEntityTypeName) + .Replace("{listEntityFullName}", listEntityFullName) + .Replace("{listItemCreateVarName}", listItemCreateVarName) + .Replace("{primaryKeyPropertyName}", primaryKeyPropertyName); + + + builder + .ParseTemplate(templateToSourceBuilder.ToString()); + } + + if (template.IsEmpty()) + { + builder + .WriteComment("Generated body") + //.WriteLine(GeneratedFilesHeader) + .WriteLine($"public static (bool, string, {returnTypeFullName}) Add{listEntityTypeName}To{entityTypeName}( this {contextFullName} dbContext, {primaryKeyFullName} {primaryKeyVarName}, {listEntityCreateFullName} {listItemCreateVarName})") + .WriteOpeningBracket() + .WriteLine($"var entityQuery = from aEntity in dbContext.{propertyName}") + .WriteLine($"where aEntity.{primaryKeyVarName} == {primaryKeyVarName}") + .WriteLine($"let itemsInList = aEntity.{listPropertyName}") + .WriteLine($"select aEntity;") + .WriteLine() + .WriteLine($"var entity = entityQuery.FirstOrDefault();") + .WriteLine($"if (entity == null) return (false, $\"{entityTypeName} not found.\", null);") + .WriteLine($"var newListItem = new {listEntityFullName}({listItemCreateVarName});") + .WriteLine($"entity.{listPropertyName}.Add({listItemCreateVarName});") + .WriteLine($"var success = dbContext.SaveChanges() >= 0;") + .WriteLine($"return !success ? (false, \"Error saving changes in the Database. Action: Create {listEntityTypeName} in {entityTypeName}.\", null) : (true, string.Empty, new {returnTypeFullName}(newListItem));") + .WriteClosingBracket(); + + builder + .WriteLine(); + } + + return builder; + } + } +} \ No newline at end of file diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetListTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetListTemplateSource.cs new file mode 100644 index 0000000..012a889 --- /dev/null +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetListTemplateSource.cs @@ -0,0 +1,68 @@ +using System.Text; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; + +namespace BlueWest.EfMethods +{ + public static class EfGetListTemplateSource + { + internal static SourceBuilder EfAddGetListMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetListModel model, string addSourceTemplate) + { + var returnTypeFullName = model.ReturnTypeFullName; + var entityTypeName = model.EntityTypeIdentifierName; + var listPropertyName = model.ListPropertyName; + var contextFullName = methodsModel.ContextFullType; + var propertyName = model.PropertyName; + var primaryKeyFullName = model.KeyFullTypeName; + var primaryKeyPropertyName = model.KeyPropertyName; + var primaryKeyVarName = model.KeyPropertyName.ToCamelCase(); + var entityObjectVarName = model.EntityTypeIdentifierName.ToCamelCase(); + + if (!addSourceTemplate.IsEmpty()) + { + var templateToSourceBuilder = new StringBuilder(addSourceTemplate); + templateToSourceBuilder + .Replace("{returnTypeFullName}", returnTypeFullName) + .Replace("{entityTypeName}", entityTypeName) + .Replace("{contextFullName}", contextFullName) + .Replace("{listPropertyName}", listPropertyName) + .Replace("{primaryKeyPropertyName}", primaryKeyPropertyName) + .Replace("{primaryKeyFullName}", primaryKeyFullName) + .Replace("{primaryKeyVarName}", primaryKeyVarName) + .Replace("{entityObjectVarName}", entityObjectVarName) + .Replace("{propertyName}", propertyName); + + builder + .ParseTemplate(templateToSourceBuilder.ToString()); + } + + if (addSourceTemplate.IsEmpty()) + { + builder + .WriteComment("Generated body") + //.WriteLine(GeneratedFilesHeader) + .WriteLine($"public static (bool, {returnTypeFullName}[]) Get{entityTypeName}{listPropertyName}(this {contextFullName} dbContext, {primaryKeyFullName} {primaryKeyVarName},") + .WriteLine($"int skip = 0, int take = 50, int orderDir = 1, Expression> orderBy = null)") + .WriteOpeningBracket() + .WriteLine($"var {entityObjectVarName} = dbContext.{propertyName}.FirstOrDefault(d => d.{primaryKeyPropertyName} == {primaryKeyVarName});") + .WriteLine($"if ({entityObjectVarName} == null) return (false, null);") + .WriteLine($"var currentTake = take;") + .WriteLine($"if (take > 200) currentTake = 200;") + .WriteLine($"var query = dbContext") + .WriteLine($".{propertyName}") + .WriteLine($".Where(data => data.{primaryKeyPropertyName} == {primaryKeyVarName})") + .WriteLine($".SelectMany(o => o.{listPropertyName})") + .WriteLine($".Select(x => new {returnTypeFullName}(x))") + .WriteLine($".Skip(skip)") + .WriteLine($"if(orderBy != null) query = orderDir == 1 ? query.OrderBy(orderBy) : query.OrderByDescending(orderBy);") + .WriteLine($"return (query.Any(), query.ToArray());") + .WriteClosingBracket(); + + builder + .WriteLine(); + } + + return builder; + } + } +} \ No newline at end of file diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs index 794be4a..4d6c5ed 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs @@ -28,7 +28,6 @@ namespace BlueWest.EfMethods .Replace("{entityTypeName}", entityTypeName) .Replace("{byParamPropertyName}", byParamPropertyName) .Replace("{contextFullName}", contextFullName) - .Replace("{contextFullName}", contextFullName) .Replace("{byParamFullType}", byParamFullType) .Replace("{byParamVarName}", byParamVarName) diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs index b1551cb..3d57575 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs @@ -8,7 +8,7 @@ namespace BlueWest.EfMethods { internal static class EfGetOneWithTemplateSource { - internal static SourceBuilder EfAddGetOneEntityWithMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetOneWithModel model, string addSourceTemplate) + internal static SourceBuilder EfAddGetOneMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetOneWithModel model, string addSourceTemplate) { var returnTypeFullName = model.ReturnTypeFullName; var entityTypeName = model.EntityTypeIdentifierName; @@ -53,7 +53,7 @@ namespace BlueWest.EfMethods return builder; } - internal static SourceBuilder EfAddGetManyWithMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetManyModel model, string addSourceTemplate) + internal static SourceBuilder EfAddGetManyMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetManyModel model, string addSourceTemplate) { var returnTypeFullName = model.ReturnTypeFullName; var entityTypeName = model.EntityTypeIdentifierName; diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs index 634cb35..1be2132 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs @@ -7,7 +7,7 @@ namespace BlueWest.EfMethods.Sources { internal static class EfUpdateEntityTemplateSource { - internal static SourceBuilder EfAddUpdateEntityMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfUpdateMethodsModel model, string updateSourceTemplate) + internal static SourceBuilder EfAddUpdateMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfUpdateMethodsModel model, string updateSourceTemplate) { var entityTypeName = model.EntityTypeIdentifierName; var contextFullName = methodsModel.ContextFullType; diff --git a/src/BlueWest.EfMethods/Models.cs b/src/BlueWest.EfMethods/Models.cs index fbc772e..be2687f 100644 --- a/src/BlueWest.EfMethods/Models.cs +++ b/src/BlueWest.EfMethods/Models.cs @@ -32,6 +32,17 @@ namespace BlueWest.EfMethods ImmutableArray Usings ); + + record EfTemplates( + string AddTemplate, + string UpdateTemplate, + string GetOneByTemplate, + string GetOneTemplate, + string GetManyTemplate, + string GetListTemplate, + string AddToListTemplate + ); + internal class EfEntityDataModel { public string PropertyName { get; set; } @@ -90,8 +101,8 @@ namespace BlueWest.EfMethods internal class EfGetOneWithModel : EfEntityDataModel { - public string ReturnTypeIdentifierName { get; set; } - public string ReturnTypeFullName { get; set; } + public string ReturnTypeIdentifierName { get; } + public string ReturnTypeFullName { get; } public EfGetOneWithModel(EfEntityDataModel entity, string returnTypeFullName, string returnTypeIdentifierName) : base(entity.PropertyName, entity.EntityTypeFullName, entity.EntityTypeIdentifierName) @@ -119,10 +130,10 @@ namespace BlueWest.EfMethods { public string UpdateTypeFullName; public string UpdateTypeIdentifierName; - public string ReturnTypeFullName; + public readonly string ReturnTypeFullName; public string ReturnTypeIdentifierName; - public string KeyPropertyName; - public string KeyFullTypeName; + public readonly string KeyPropertyName; + public readonly string KeyFullTypeName; public EfUpdateMethodsModel(EfEntityDataModel entity, string updateTypeFullName, string updateTypeIdentifierName, @@ -139,6 +150,80 @@ namespace BlueWest.EfMethods KeyFullTypeName = keyFullTypeName; } } + + + internal class EfGetListModel : EfEntityDataModel + { + public string ListPropertyName { get; set; } + public string KeyPropertyName { get; set; } + public string KeyFullTypeName { get; set; } + public string ReturnTypeIdentifierName { get; set; } + public string ReturnTypeFullName { get; set; } + + + public EfGetListModel( + EfEntityDataModel entity, + string listPropertyName, + string keyPropertyName, + string keyFullTypeName, + string returnTypeIdentifierName, + string returnTypeFullName) + : base(entity.PropertyName, entity.EntityTypeFullName, entity.EntityTypeIdentifierName) + { + ListPropertyName = listPropertyName; + KeyPropertyName = keyPropertyName; + KeyFullTypeName = keyFullTypeName; + ReturnTypeIdentifierName = returnTypeIdentifierName; + ReturnTypeFullName = returnTypeFullName; + } + } + + + internal class EfAddToListModel : EfEntityDataModel + { + public string ListPropertyName { get; set; } + + public string ListEntityIdentifierName { get; set; } + public string ListEntityFullTypeName { get; set; } + + public string KeyPropertyName { get; set; } + public string KeyFullTypeName { get; set; } + + public string CreateTypeIdentifierName { get; set; } + public string CreateTypeFullName { get; set; } + + public string ReturnTypeIdentifierName { get; set; } + public string ReturnTypeFullName { get; set; } + + + + + public EfAddToListModel( + EfEntityDataModel entity, + string listPropertyName, + string listEntityIdentifierName, + string listEntityFullTypeName, + string keyPropertyName, + string keyFullTypeName, + string createTypeIdentifierName, + string createTypeFullName, + string returnTypeIdentifierName, + string returnTypeFullName) + : base(entity.PropertyName, entity.EntityTypeFullName, entity.EntityTypeIdentifierName) + { + ListPropertyName = listPropertyName; + ListEntityIdentifierName = listEntityIdentifierName; + ListEntityFullTypeName = listEntityFullTypeName; + KeyPropertyName = keyPropertyName; + KeyFullTypeName = keyFullTypeName; + CreateTypeIdentifierName = createTypeIdentifierName; + CreateTypeFullName = createTypeFullName; + ReturnTypeIdentifierName = returnTypeIdentifierName; + ReturnTypeFullName = returnTypeFullName; + } + } + + internal record SourceGenerationOptions( AccessModifier ConstructorAccessModifier,