Update template working
This commit is contained in:
parent
f419c0f8ea
commit
654a54d5ef
|
@ -19,14 +19,12 @@ namespace MapTo
|
|||
protected EfAddGeneratorContext(
|
||||
Compilation compilation,
|
||||
SourceGenerationOptions sourceGenerationOptions,
|
||||
MemberDeclarationSyntax memberSyntax,
|
||||
string addSourceTemplate)
|
||||
MemberDeclarationSyntax memberSyntax)
|
||||
{
|
||||
Compilation = compilation;
|
||||
_ignoredNamespaces = new();
|
||||
Diagnostics = ImmutableArray<Diagnostic>.Empty;
|
||||
Usings = ImmutableArray.Create("System", Constants.RootNamespace);
|
||||
SourceTemplate = addSourceTemplate;
|
||||
SourceGenerationOptions = sourceGenerationOptions;
|
||||
MemberSyntax = memberSyntax;
|
||||
|
||||
|
@ -34,8 +32,7 @@ namespace MapTo
|
|||
|
||||
AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis");
|
||||
}
|
||||
|
||||
public string SourceTemplate { get; }
|
||||
|
||||
|
||||
public ImmutableArray<Diagnostic> Diagnostics { get; private set; }
|
||||
|
||||
|
@ -54,12 +51,11 @@ namespace MapTo
|
|||
public static EfAddGeneratorContext Create(
|
||||
Compilation compilation,
|
||||
SourceGenerationOptions sourceGenerationOptions,
|
||||
MemberDeclarationSyntax typeSyntax,
|
||||
string addSourceTemplate)
|
||||
MemberDeclarationSyntax typeSyntax)
|
||||
{
|
||||
EfAddGeneratorContext context = typeSyntax switch
|
||||
{
|
||||
PropertyDeclarationSyntax => new EfAddGeneratorContext(compilation, sourceGenerationOptions, typeSyntax, addSourceTemplate),
|
||||
PropertyDeclarationSyntax => new EfAddGeneratorContext(compilation, sourceGenerationOptions, typeSyntax),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
|
@ -94,6 +90,13 @@ namespace MapTo
|
|||
return sourceSymbol;
|
||||
}
|
||||
|
||||
protected ImmutableArray<LiteralExpressionSyntax> GetEntityStringLiteralSymbol(MemberDeclarationSyntax memberDeclarationSyntax, string attributeName, SemanticModel? semanticModel = null)
|
||||
{
|
||||
var attributeData = memberDeclarationSyntax.GetAttribute(attributeName);
|
||||
var sourceSymbol = GetEntityStringLiteralSymbol(attributeData, semanticModel);
|
||||
return sourceSymbol;
|
||||
}
|
||||
|
||||
protected ImmutableArray<INamedTypeSymbol> GetEntityTypeSymbols(SyntaxNode? attributeSyntax, SemanticModel? semanticModel = null)
|
||||
{
|
||||
if (attributeSyntax is null)
|
||||
|
@ -123,6 +126,25 @@ namespace MapTo
|
|||
return resultList.ToImmutableArray();
|
||||
}
|
||||
|
||||
protected ImmutableArray<LiteralExpressionSyntax> GetEntityStringLiteralSymbol(SyntaxNode? attributeSyntax, SemanticModel? semanticModel = null)
|
||||
{
|
||||
if (attributeSyntax is null)
|
||||
{
|
||||
return new ImmutableArray<LiteralExpressionSyntax>(){};
|
||||
}
|
||||
|
||||
semanticModel ??= Compilation.GetSemanticModel(attributeSyntax.SyntaxTree);
|
||||
var descendentNodes = attributeSyntax
|
||||
.DescendantNodes();
|
||||
|
||||
var sourceTypeExpressionSyntax = descendentNodes
|
||||
.OfType<LiteralExpressionSyntax>()
|
||||
.ToImmutableArray();
|
||||
|
||||
|
||||
return sourceTypeExpressionSyntax;
|
||||
}
|
||||
|
||||
|
||||
protected bool IsEnumerable(ISymbol property, out INamedTypeSymbol? namedTypeSymbolResult)
|
||||
{
|
||||
|
@ -194,6 +216,16 @@ namespace MapTo
|
|||
return null;
|
||||
}
|
||||
|
||||
var updateConfig = ExtractEfUpdateMethodsModel(semanticModel, entityTypeName, entityTypeFullName);
|
||||
|
||||
if (updateConfig == null)
|
||||
{
|
||||
AddDiagnostic(DiagnosticsFactory.MapFromAttributeNotFoundError(MemberSyntax.GetLocation()));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new EfMethodsModel(
|
||||
SourceGenerationOptions,
|
||||
contextNamespace,
|
||||
|
@ -203,6 +235,7 @@ namespace MapTo
|
|||
entityTypeFullName,
|
||||
entityTypeName,
|
||||
addConfig,
|
||||
updateConfig,
|
||||
Usings);
|
||||
}
|
||||
|
||||
|
@ -233,6 +266,48 @@ namespace MapTo
|
|||
return new EfAddMethodsModel(createTypeFullName, createTypeIdentifierName, returnTypeFullName,
|
||||
returnTypeIdentifierName);
|
||||
}
|
||||
|
||||
private EfUpdateMethodsModel ExtractEfUpdateMethodsModel(SemanticModel semanticModel, string entityTypeName, string entityTypeFullName)
|
||||
{
|
||||
var efAddAttributeTypeSymbols =
|
||||
GetEntityTypeSymbol(MemberSyntax, EfUpdateMethodsAttributeSource.AttributeName, semanticModel);
|
||||
|
||||
var keyPropertyName = GetEntityStringLiteralSymbol(MemberSyntax, EfUpdateMethodsAttributeSource.AttributeName, semanticModel)
|
||||
.FirstOrDefault()?
|
||||
.Token.ValueText ?? "Id";
|
||||
|
||||
if (efAddAttributeTypeSymbols == null) return null;
|
||||
|
||||
string updateTypeIdentifierName = entityTypeName;
|
||||
string updateTypeFullName = entityTypeFullName;
|
||||
string returnTypeIdentifierName = entityTypeName;
|
||||
string returnTypeFullName = entityTypeFullName;
|
||||
string keyPropertyTypeFullName = "int";
|
||||
|
||||
if (efAddAttributeTypeSymbols.Length > 0)
|
||||
{
|
||||
updateTypeIdentifierName = efAddAttributeTypeSymbols[0].Name;
|
||||
updateTypeFullName = efAddAttributeTypeSymbols[0].ToDisplayString();
|
||||
}
|
||||
if (efAddAttributeTypeSymbols.Length > 0)
|
||||
{
|
||||
returnTypeIdentifierName = efAddAttributeTypeSymbols[1].Name;
|
||||
returnTypeFullName = efAddAttributeTypeSymbols[1].ToDisplayString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (efAddAttributeTypeSymbols.Length > 2)
|
||||
{
|
||||
keyPropertyTypeFullName = efAddAttributeTypeSymbols[2].ToDisplayString();
|
||||
}
|
||||
|
||||
|
||||
return new EfUpdateMethodsModel(updateTypeFullName, updateTypeIdentifierName, returnTypeFullName,
|
||||
returnTypeIdentifierName, keyPropertyName, keyPropertyTypeFullName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static ITypeSymbol GetEntityTypeData(MemberDeclarationSyntax memberDeclarationSyntax, SemanticModel? semanticModel = null)
|
||||
{
|
||||
|
|
|
@ -39,7 +39,9 @@ namespace MapTo
|
|||
var options = SourceGenerationOptions.From(context);
|
||||
|
||||
var compilation = context.Compilation
|
||||
.AddSource(ref context, EfAddMethodsAttributeSource.Generate(options));
|
||||
.AddSource(ref context, EfAddMethodsAttributeSource.Generate(options))
|
||||
.AddSource(ref context, EfUpdateMethodsAttributeSource.Generate(options));
|
||||
|
||||
|
||||
if (context.SyntaxReceiver is EfMethodsSyntaxReceiver receiver && receiver.CandidateMembers.Any())
|
||||
{
|
||||
|
@ -59,17 +61,18 @@ namespace MapTo
|
|||
|
||||
foreach (var candidateMember in candidateMembers)
|
||||
{
|
||||
string addSourceTemplate = string.Empty;
|
||||
|
||||
if (context.AdditionalFiles.Length > 0)
|
||||
{
|
||||
addSourceTemplate = context.AdditionalFiles
|
||||
.FirstOrDefault()?
|
||||
string addSourceTemplate = context.AdditionalFiles
|
||||
.FirstOrDefault(x => x.Path.Contains("AddToEntityTemplate"))?
|
||||
.GetText()?
|
||||
.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
var mappingContext = EfAddGeneratorContext.Create(compilation, options, candidateMember.MemberDeclarationSyntax, addSourceTemplate);
|
||||
|
||||
string updateSourceTemplate = context.AdditionalFiles
|
||||
.FirstOrDefault(x => x.Path.Contains("UpdateEntityTemplate"))?
|
||||
.GetText()?
|
||||
.ToString() ?? string.Empty;
|
||||
|
||||
|
||||
var mappingContext = EfAddGeneratorContext.Create(compilation, options, candidateMember.MemberDeclarationSyntax);
|
||||
|
||||
mappingContext.Diagnostics.ForEach(context.ReportDiagnostic);
|
||||
|
||||
|
@ -80,7 +83,7 @@ namespace MapTo
|
|||
|
||||
var (source, hintName) = candidateMember.MemberDeclarationSyntax switch
|
||||
{
|
||||
PropertyDeclarationSyntax => EfMethodsSource.Generate(mappingContext.Model, addSourceTemplate),
|
||||
PropertyDeclarationSyntax => EfMethodsSource.Generate(mappingContext.Model, addSourceTemplate, updateSourceTemplate),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
|
|
|
@ -35,11 +35,10 @@ namespace MapTo.Extensions
|
|||
{
|
||||
var attributeLists = typeDeclarationSyntax.AttributeLists;
|
||||
var selection = attributeLists
|
||||
.SelectMany(al => al.Attributes);
|
||||
var result = selection
|
||||
.FirstOrDefault();
|
||||
.SelectMany(al => al.Attributes)
|
||||
.FirstOrDefault(x => x.Name.ToString().Contains(attributeName));
|
||||
|
||||
return result;
|
||||
return selection;
|
||||
}
|
||||
|
||||
public static bool HasAttribute(this ISymbol symbol, ITypeSymbol attributeSymbol) =>
|
||||
|
|
|
@ -79,10 +79,12 @@ namespace MapTo
|
|||
) : IEfMethodsConfiguration;
|
||||
internal record EfUpdateMethodsModel(
|
||||
|
||||
string CreateTypeFullName,
|
||||
string CreateTypeIdentifierName,
|
||||
string UpdateTypeFullName,
|
||||
string UpdateTypeIdentifierName,
|
||||
string ReturnTypeFullName,
|
||||
string ReturnTypeIdentifierName
|
||||
string ReturnTypeIdentifierName,
|
||||
string keyPropertyName,
|
||||
string keyFullTypeName
|
||||
): IEfMethodsConfiguration;
|
||||
|
||||
|
||||
|
@ -95,6 +97,7 @@ namespace MapTo
|
|||
string EntityTypeFullName,
|
||||
string EntityTypeIdentifierName,
|
||||
EfAddMethodsModel AddMethodsModel,
|
||||
EfUpdateMethodsModel UpdateMethodsModel,
|
||||
ImmutableArray<string> Usings
|
||||
);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MapTo.Sources
|
|||
.WriteOpeningBracket();
|
||||
|
||||
builder
|
||||
.WriteLine($"public {AttributeName}Attribute(Type createDto = null, Type returnType = null)")
|
||||
.WriteLine($"public {AttributeName}Attribute(Type createType = null, Type returnType = null)")
|
||||
.WriteOpeningBracket()
|
||||
.WriteClosingBracket()
|
||||
.WriteLine();
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace MapTo.Sources
|
|||
{
|
||||
internal static class EfMethodsSource
|
||||
{
|
||||
internal static SourceCode Generate(EfMethodsModel model, string addSourceTemplate)
|
||||
internal static SourceCode Generate(EfMethodsModel model, string addSourceTemplate, string updateSourceTemplate)
|
||||
{
|
||||
using var builder = new SourceBuilder();
|
||||
|
||||
|
@ -21,6 +21,10 @@ namespace MapTo.Sources
|
|||
builder
|
||||
.WriteUsings(model.Usings)
|
||||
.WriteLine("using Microsoft.EntityFrameworkCore;")
|
||||
.WriteLine("using System.Linq;")
|
||||
.WriteLine("using System.Linq.Expressions;")
|
||||
.WriteLine("using System.Threading.Tasks;")
|
||||
|
||||
.WriteLine()
|
||||
// Namespace declaration
|
||||
.WriteLine($"namespace {model.Namespace}")
|
||||
|
@ -36,6 +40,12 @@ namespace MapTo.Sources
|
|||
contextFullName,
|
||||
entityTypeFullName,
|
||||
propertyName)
|
||||
.EfAddUpdateEntityMethod(model, updateSourceTemplate,
|
||||
entityTypeName,
|
||||
contextFullName,
|
||||
entityTypeFullName,
|
||||
propertyName)
|
||||
|
||||
// End class declaration
|
||||
.WriteClosingBracket()
|
||||
.WriteLine()
|
||||
|
@ -93,45 +103,53 @@ namespace MapTo.Sources
|
|||
return builder;
|
||||
}
|
||||
|
||||
private static SourceBuilder EfAddUpdateEntityMethod(this SourceBuilder builder, EfMethodsModel model, string addSourceTemplate, string entityTypeName,
|
||||
private static SourceBuilder EfAddUpdateEntityMethod(this SourceBuilder builder, EfMethodsModel model, string updateSourceTemplate, string entityTypeName,
|
||||
string contextFullName, string entityTypeFullName, string propertyName)
|
||||
{
|
||||
var returnTypeFullName = model.AddMethodsModel.ReturnTypeFullName;
|
||||
var updateTypeFullName = model.AddMethodsModel.CreateTypeFullName;
|
||||
var newEntityVarName = $"new{model.EntityTypeIdentifierName}";
|
||||
var toCreateVarName = $"{model.EntityTypeIdentifierName.ToCamelCase()}ToCreate";
|
||||
|
||||
if (!addSourceTemplate.IsEmpty())
|
||||
var returnTypeFullName = model.UpdateMethodsModel.ReturnTypeFullName;
|
||||
var updateTypeFullName = model.UpdateMethodsModel.UpdateTypeFullName;
|
||||
var updateVarName = $"{model.EntityTypeIdentifierName.ToCamelCase()}ToUpdate";
|
||||
var keyPropertyName = model.UpdateMethodsModel.keyPropertyName;
|
||||
var keyTypeFullName = model.UpdateMethodsModel.keyFullTypeName;
|
||||
var existingVarName = entityTypeName.ToCamelCase();
|
||||
var keyVarName = entityTypeName.ToCamelCase() + model.UpdateMethodsModel.keyPropertyName;
|
||||
if (!updateSourceTemplate.IsEmpty())
|
||||
{
|
||||
var templateToSourceBuilder = new StringBuilder(addSourceTemplate);
|
||||
var templateToSourceBuilder = new StringBuilder(updateSourceTemplate);
|
||||
templateToSourceBuilder
|
||||
.Replace("{entityTypeName}", entityTypeName)
|
||||
.Replace("{returnTypeFullName}", returnTypeFullName)
|
||||
.Replace("{updateTypeFullName}", updateTypeFullName)
|
||||
.Replace("{updateVarName}", updateVarName)
|
||||
.Replace("{contextFullName}", contextFullName)
|
||||
.Replace("{propertyName}", propertyName);
|
||||
|
||||
.Replace("{propertyName}", propertyName)
|
||||
.Replace("{keyTypeFullName}", keyTypeFullName)
|
||||
.Replace("{keyPropertyName}", keyPropertyName)
|
||||
.Replace("{keyVarName}", keyVarName)
|
||||
.Replace("{existingEntityVarName}", existingVarName);
|
||||
builder
|
||||
.Write(templateToSourceBuilder.ToString());
|
||||
}
|
||||
|
||||
if (addSourceTemplate.IsEmpty())
|
||||
if (updateSourceTemplate.IsEmpty())
|
||||
{
|
||||
builder
|
||||
.WriteLine(GeneratedFilesHeader)
|
||||
.WriteLine($"public static (bool, {model.AddMethodsModel.ReturnTypeFullName}) Add{entityTypeName}(")
|
||||
.WriteLine($"public static (bool, {returnTypeFullName}) Update{entityTypeName}(")
|
||||
.WriteLine($"this {contextFullName} dbContext,")
|
||||
.WriteLine($"{updateTypeFullName} {toCreateVarName})")
|
||||
.WriteLine($"{updateTypeFullName} {updateVarName},")
|
||||
.WriteLine($"{keyTypeFullName} {keyVarName})")
|
||||
.WriteOpeningBracket()
|
||||
.WriteLine($"var {newEntityVarName} = new {model.EntityTypeFullName}({toCreateVarName});")
|
||||
.WriteLine($"dbContext.{propertyName}.Add({newEntityVarName});")
|
||||
.WriteLine($"var {existingVarName} = dbContext.{propertyName}.FirstOrDefault(x => x.{keyPropertyName} == {keyVarName});")
|
||||
.WriteLine($"if ({existingVarName} == null) return (false, null);")
|
||||
.WriteLine($"var success = dbContext.SaveChanges() >= 0;")
|
||||
.WriteLine($"return (success, new {returnTypeFullName}({newEntityVarName}));")
|
||||
.WriteLine($"return (success, new {returnTypeFullName}({existingVarName}));")
|
||||
.WriteClosingBracket();
|
||||
|
||||
builder
|
||||
.WriteLine();
|
||||
}
|
||||
builder.Indent();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace MapTo.Sources
|
|||
.WriteOpeningBracket();
|
||||
|
||||
builder
|
||||
.WriteLine($"public {AttributeName}Attribute(Type updateType = null, Type returnType = null, string keyPropertyMemberName, Type keyPropertyMemberType)")
|
||||
.WriteLine($"public {AttributeName}Attribute(Type updateType, Type returnType, string keyPropertyMemberName, Type keyPropertyMemberType)")
|
||||
.WriteOpeningBracket()
|
||||
.WriteClosingBracket()
|
||||
.WriteLine();
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
using static MapTo.Sources.Constants;
|
||||
|
||||
namespace MapTo.Sources
|
||||
{
|
||||
/// <summary>
|
||||
/// UpdateData Attribute Source
|
||||
/// </summary>
|
||||
public class UpdateDataAttributeSource
|
||||
{
|
||||
internal const string AttributeName = "UpdateData";
|
||||
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();
|
||||
|
||||
if (options.GenerateXmlDocument)
|
||||
{
|
||||
builder
|
||||
.WriteLine("/// <summary>")
|
||||
.WriteLine("/// Generates CRUD functions to be used with the specified database context and entity.")
|
||||
.WriteLine("/// </summary>");
|
||||
}
|
||||
|
||||
builder
|
||||
.WriteLine("[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]")
|
||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
||||
.WriteOpeningBracket();
|
||||
|
||||
if (options.GenerateXmlDocument)
|
||||
{
|
||||
builder
|
||||
.WriteLine("/// <summary>")
|
||||
.WriteLine($"/// Initializes a new instance of the <see cref=\"{AttributeName}Attribute\"/> class with the specified <paramref name=\"sourceType\"/>.")
|
||||
.WriteLine("/// </summary>")
|
||||
.WriteLine("/// <param name=\"sourceType\">The type of to map from.</param>");
|
||||
}
|
||||
|
||||
builder
|
||||
.WriteLine($"public {AttributeName}Attribute(Type databaseContextType)")
|
||||
.WriteOpeningBracket()
|
||||
.WriteLine("DatabaseContextType = databaseContextType;")
|
||||
.WriteClosingBracket()
|
||||
.WriteLine();
|
||||
|
||||
|
||||
if (options.GenerateXmlDocument)
|
||||
{
|
||||
builder
|
||||
.WriteLine("/// <summary>")
|
||||
.WriteLine("/// Gets the type to map from.")
|
||||
.WriteLine("/// </summary>");
|
||||
}
|
||||
|
||||
builder
|
||||
.WriteLine("public Type DatabaseContextType { get; }")
|
||||
.WriteClosingBracket() // class
|
||||
.WriteClosingBracket(); // namespace
|
||||
|
||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue