diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddMethodsAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfAddMethodsAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddMethodsAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfAddMethodsAttributeSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddToListAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfAddToListAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfAddToListAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfAddToListAttributeSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfGeneratorAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfGeneratorAttributeSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetListAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfGetListAttributeSource.cs similarity index 96% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetListAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfGetListAttributeSource.cs index 3c91a56..19a3ebb 100644 --- a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetListAttributeSource.cs +++ b/src/BlueWest.EfMethods/AttributeSources/EfGetListAttributeSource.cs @@ -4,7 +4,7 @@ using static BlueWest.EfMethods.Sources.Constants; namespace BlueWest.Sources { - public class EfGetListAttributeSource + public static class EfGetListAttributeSource { internal const string AttributeName = "EfGetList"; internal const string AttributeClassName = AttributeName + "Attribute"; diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetManyAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfGetManyAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetManyAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfGetManyAttributeSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfGetOneByAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfGetOneByAttributeSource.cs diff --git a/src/BlueWest.EfMethods/AttributeSources/EfGetOneFromListAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfGetOneFromListAttributeSource.cs new file mode 100644 index 0000000..3892e2a --- /dev/null +++ b/src/BlueWest.EfMethods/AttributeSources/EfGetOneFromListAttributeSource.cs @@ -0,0 +1,47 @@ +using BlueWest.EfMethods.Sources; +using BlueWest.EfMethods; +using System; +using System.Collections.Generic; +using System.Text; +using static BlueWest.EfMethods.Sources.Constants; + +namespace BlueWest.Sources +{ + internal static class EfGetOneFromListAttributeSource + { + internal const string AttributeName = "EfGetOneFromList"; + 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 entityPrimaryKeyNameof, string listMemberNameof, string listPrimaryKeyNameof, Type returnType){"{}"}") + .WriteLine(); + + builder + .WriteClosingBracket() // class + .WriteClosingBracket(); // namespace + + return new(builder.ToString(), $"{AttributeName}Attribute.g.cs"); + } + } +} diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs similarity index 94% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs index 4476ae9..f81cbfa 100644 --- a/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs +++ b/src/BlueWest.EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs @@ -19,7 +19,7 @@ namespace BlueWest.EfMethods.Sources builder .WriteLine("/// ") - .WriteLine("/// Generate update methods for this Db property") + .WriteLine("/// Generate update methods this EF entity") .WriteLine("/// "); builder diff --git a/src/BlueWest.EfMethods/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs b/src/BlueWest.EfMethods/AttributeSources/GetOneWIthAttributeSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs rename to src/BlueWest.EfMethods/AttributeSources/GetOneWIthAttributeSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs b/src/BlueWest.EfMethods/EfGeneratorContext.cs similarity index 90% rename from src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs rename to src/BlueWest.EfMethods/EfGeneratorContext.cs index 9153e3c..148ca74 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs +++ b/src/BlueWest.EfMethods/EfGeneratorContext.cs @@ -276,6 +276,7 @@ namespace BlueWest.EfMethods var efGetManyMembers = GetAllowedMemberSyntaxes(EfGetManyAttributeSource.AttributeName); var getListMembers = GetAllowedMemberSyntaxes(EfGetListAttributeSource.AttributeName); var getAddToListMembers = GetAllowedMemberSyntaxes(EfAddToListAttributeSource.AttributeName); + var getOneFromListMembers = GetAllowedMemberSyntaxes(EfGetOneFromListAttributeSource.AttributeName); @@ -333,12 +334,14 @@ namespace BlueWest.EfMethods var newModel = ExtractEfAddToListMethodsModel(uProperty, semanticModel, entityDataModel); methodsModels.Add(newModel); } + + foreach (var uProperty in getOneFromListMembers) + { + var entityDataModel = GetEntityData(uProperty, semanticModel); + var newModel = ExtractEfGetOneFromListMethodsModel(uProperty, semanticModel, entityDataModel); + methodsModels.Add(newModel); + } - - - - //SpinWait.SpinUntil(() => Debugger.IsAttached); - return new EfMethodsModel( SourceGenerationOptions, contextNamespace, @@ -545,10 +548,7 @@ namespace BlueWest.EfMethods 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);; @@ -574,6 +574,8 @@ namespace BlueWest.EfMethods var createTypeIdentifierName =listEntityIdentifierName; var createTypeFullName = listEntityFullTypeName; + var efTypeofSymbols = GetEntityTypeSymbol(uProperty, EfAddToListAttributeSource.AttributeName, semanticModel); + // Grab create type from attribute argument if (efTypeofSymbols.Length > 0) { @@ -601,6 +603,51 @@ namespace BlueWest.EfMethods returnTypeFullName); } + + private EfGetOneFromListModel ExtractEfGetOneFromListMethodsModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel) + { + + //SpinWait.SpinUntil(() => Debugger.IsAttached); + + var efTypeofSymbols = GetEntityTypeSymbol(uProperty, EfGetOneFromListAttributeSource.AttributeName, semanticModel); + var attributeSymbol = uProperty.GetAttribute(EfGetOneFromListAttributeSource.AttributeName); + var keyPropertyName = ExtractNameOfMemberName(uProperty, EfGetOneFromListAttributeSource.AttributeName, semanticModel); + var listKeyPropertyName = ExtractNameOfMemberName(uProperty, EfGetOneFromListAttributeSource.AttributeName, semanticModel, 2); + var keyMemberType = FindTypeInfoOfMemberExpressionSyntax(semanticModel, attributeSymbol); + var listKeyTypeInfo = FindTypeInfoOfMemberExpressionSyntax(semanticModel, attributeSymbol, 0); + var keyFullTypeName = keyMemberType.Value.Type.ToDisplayString(); + var listEntityTypeInfo = (FindTypeInfoOfMemberExpressionSyntax(semanticModel, attributeSymbol, 1).Value.Type as INamedTypeSymbol).TypeArguments.FirstOrDefault(); + var listEntityIdentifierName = listEntityTypeInfo.Name; + var listEntityFullTypeName = listEntityTypeInfo.ToDisplayString(); + var listKeyFullTypeName =listKeyTypeInfo.Value.Type.ToDisplayString();; + var listKeyTypeName = listKeyTypeInfo.Value.Type.Name; + var listPropertyName = ExtractNameOfMemberName(uProperty, EfGetOneFromListAttributeSource.AttributeName, semanticModel, 1);; + + + 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 EfGetOneFromListModel( + entityDataModel, + listPropertyName, + listEntityIdentifierName, + listEntityFullTypeName, + keyPropertyName, + keyFullTypeName, + listKeyPropertyName, + listKeyFullTypeName, + listKeyTypeName, + returnTypeIdentifierName, + returnTypeFullName); + } + private static ITypeSymbol GetEntityTypeData(MemberDeclarationSyntax memberDeclarationSyntax, SemanticModel? semanticModel = null) { diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs b/src/BlueWest.EfMethods/EfMethodsGenerator.cs similarity index 93% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs rename to src/BlueWest.EfMethods/EfMethodsGenerator.cs index 894c5c2..4cf7aa0 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs +++ b/src/BlueWest.EfMethods/EfMethodsGenerator.cs @@ -39,7 +39,8 @@ namespace BlueWest.EfMethods .AddSource(ref context, EfGetOneAttributeSource.Generate(options)) .AddSource(ref context, EfGetManyAttributeSource.Generate(options)) .AddSource(ref context, EfGetListAttributeSource.Generate(options)) - .AddSource(ref context, EfAddToListAttributeSource.Generate(options)); + .AddSource(ref context, EfAddToListAttributeSource.Generate(options)) + .AddSource(ref context, EfGetOneFromListAttributeSource.Generate(options)); @@ -65,6 +66,8 @@ namespace BlueWest.EfMethods string getManyWithTemplate = GetCsxMethodTemplate(context, "GetManyTemplate"); string getListTemplate = GetCsxMethodTemplate(context, "GetListTemplate"); string addToListTemplate = GetCsxMethodTemplate(context, "AddToListTemplate"); + string getOneFromListTemplate = GetCsxMethodTemplate(context, "GetOneFromListTemplate"); + return new EfTemplates( addSourceTemplate, @@ -73,7 +76,9 @@ namespace BlueWest.EfMethods getOneTemplate, getManyWithTemplate, getListTemplate, - addToListTemplate); + addToListTemplate, + getOneFromListTemplate + ); } private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateMembers, SourceGenerationOptions options) diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs b/src/BlueWest.EfMethods/EfMethodsSource.cs similarity index 93% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs rename to src/BlueWest.EfMethods/EfMethodsSource.cs index b054919..dd2c6b1 100644 --- a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs +++ b/src/BlueWest.EfMethods/EfMethodsSource.cs @@ -64,6 +64,7 @@ namespace BlueWest.EfMethods.Sources .EfAddGetOneMethod(model, currentModel, templates.GetOneTemplate) .WriteLine(); break; + case EfGetListModel currentModel: builder .EfAddGetListMethod(model, currentModel, templates.GetListTemplate) @@ -74,7 +75,13 @@ namespace BlueWest.EfMethods.Sources .EfAddAddToListMethod(model, currentModel, templates.AddToListTemplate) .WriteLine(); break; - + + case EfGetOneFromListModel currentModel: + builder + .EfAddGetOneFromListMethod(model, currentModel, templates.GetOneFromListTemplate) + .WriteLine(); + break; + } } diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethodsSource/EfAddToListTemplateSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetListTemplateSource.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfGetListTemplateSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetListTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethodsSource/EfGetListTemplateSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs rename to src/BlueWest.EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs diff --git a/src/BlueWest.EfMethods/EfMethodsSource/EfGetOneFromListTemplateSource.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfGetOneFromListTemplateSource.cs new file mode 100644 index 0000000..b1995fe --- /dev/null +++ b/src/BlueWest.EfMethods/EfMethodsSource/EfGetOneFromListTemplateSource.cs @@ -0,0 +1,79 @@ +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BlueWest.EfMethods +{ + internal static class EfGetOneFromListTemplateSource + { + internal static SourceBuilder EfAddGetOneFromListMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetOneFromListModel model, string addSourceTemplate) + { + var returnTypeFullName = model.ReturnTypeFullName; + var entityTypeName = model.EntityTypeIdentifierName; + var propertyName = model.PropertyName; + var listItemTypeName = model.ListEntityIdentifierName; + + var contextFullName = methodsModel.ContextFullType; + var primaryKeyFullName = model.KeyFullTypeName; + var primaryKeyVarName = $"{model.EntityTypeIdentifierName}{model.KeyPropertyName}".ToCamelCase(); + + var listPrimaryKeyFullName = model.ListKeyFullTypeName; + var listPrimaryKeyVarName = $"{model.ListEntityIdentifierName}{model.ListKeyPropertyName}".ToCamelCase(); + var listPropertyName = model.ListPropertyName; + var listPrimaryKeyPropertyName = model.ListKeyPropertyName; + + var primaryKeyPropertyName = model.KeyPropertyName; + + if (!addSourceTemplate.IsEmpty()) + { + var templateToSourceBuilder = new StringBuilder(addSourceTemplate); + templateToSourceBuilder + .Replace("{returnTypeFullName}", returnTypeFullName) + .Replace("{entityTypeName}", entityTypeName) + .Replace("{listItemTypeName}", listItemTypeName) + .Replace("{contextFullName}", contextFullName) + .Replace("{primaryKeyPropertyName}", primaryKeyPropertyName) + .Replace("{listPrimaryKeyPropertyName}", listPrimaryKeyPropertyName) + + .Replace("{primaryKeyFullName}", primaryKeyFullName) + .Replace("{primaryKeyVarName}", primaryKeyVarName) + .Replace("{listPrimaryKeyFullName}", listPrimaryKeyFullName) + .Replace("{listPrimaryKeyVarName}", listPrimaryKeyVarName) + .Replace("{listPropertyName}", listPropertyName) + + .Replace("{propertyName}", propertyName); + + builder + .ParseTemplate(templateToSourceBuilder.ToString()); + } + + if (addSourceTemplate.IsEmpty()) + { + builder + .WriteComment("Generated body") + //.WriteLine(GeneratedFilesHeader) + .WriteLine($"public static (bool, {returnTypeFullName}) Get{listItemTypeName}From{entityTypeName} (this {contextFullName} dbContext, {primaryKeyFullName} {primaryKeyVarName},") + .WriteLine($"{listPrimaryKeyFullName} {listPrimaryKeyVarName})") + .WriteOpeningBracket() + .WriteLine($"var query =") + .WriteLine($"from mainEntity in dbContext.{propertyName}") + .WriteLine($"where mainEntity.{primaryKeyPropertyName} == {primaryKeyVarName}") + .WriteLine($"let list = mainEntity.{listPropertyName}") + .WriteLine($"from listItem in list") + .WriteLine($"where listItem.{listPrimaryKeyPropertyName} == {listPrimaryKeyVarName}") + .WriteLine($"select new {returnTypeFullName}(listItem);") + .WriteLine() + .WriteLine("query = countryQuery.SingleOrDefault();") + .WriteLine("return (query != null, query);") + .WriteClosingBracket(); + + builder + .WriteLine(); + } + + return builder; + } + } +} diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs diff --git a/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs b/src/BlueWest.EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs similarity index 100% rename from src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs diff --git a/src/BlueWest.EfMethods/Models.cs b/src/BlueWest.EfMethods/Models.cs index be2687f..86100e7 100644 --- a/src/BlueWest.EfMethods/Models.cs +++ b/src/BlueWest.EfMethods/Models.cs @@ -40,7 +40,8 @@ namespace BlueWest.EfMethods string GetOneTemplate, string GetManyTemplate, string GetListTemplate, - string AddToListTemplate + string AddToListTemplate, + string GetOneFromListTemplate ); internal class EfEntityDataModel @@ -222,6 +223,56 @@ namespace BlueWest.EfMethods ReturnTypeFullName = returnTypeFullName; } } + + internal class EfGetOneFromListModel : 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 ListKeyPropertyName { get; set; } + public string ListKeyFullTypeName { get; set; } + + public string ListKeyTypeName { get; set; } + + + public string ReturnTypeIdentifierName { get; set; } + public string ReturnTypeFullName { get; set; } + + + + + public EfGetOneFromListModel( + EfEntityDataModel entity, + string listPropertyName, + string listEntityIdentifierName, + string listEntityFullTypeName, + string keyPropertyName, + string keyFullTypeName, + string listKeyPropertyName, + string listKeyFullTypeName, + string listKeyTypeName, + string returnTypeIdentifierName, + string returnTypeFullName) + : base(entity.PropertyName, entity.EntityTypeFullName, entity.EntityTypeIdentifierName) + { + ListPropertyName = listPropertyName; + ListEntityIdentifierName = listEntityIdentifierName; + ListEntityFullTypeName = listEntityFullTypeName; + KeyPropertyName = keyPropertyName; + KeyFullTypeName = keyFullTypeName; + ListKeyPropertyName = listKeyPropertyName; + ListKeyFullTypeName = listKeyFullTypeName; + ListKeyTypeName = listKeyTypeName; + ReturnTypeIdentifierName = returnTypeIdentifierName; + ReturnTypeFullName = returnTypeFullName; + } + }