Remove EfGenerator, migrated to separate project
This commit is contained in:
parent
b8637c94ab
commit
4e80156708
|
@ -1,41 +0,0 @@
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
public class EfGeneratorAttributeSource
|
|
||||||
{
|
|
||||||
internal const string AttributeName = "EfGenerator";
|
|
||||||
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("/// Generate Add methods for interacting with the entity")
|
|
||||||
.WriteLine("/// </summary>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine("[AttributeUsage(AttributeTargets.Class)]")
|
|
||||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteClosingBracket() // class
|
|
||||||
.WriteClosingBracket(); // namespace
|
|
||||||
|
|
||||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal static class EfGetManyAttributeSource
|
|
||||||
{
|
|
||||||
internal const string AttributeName = "EfGetMany";
|
|
||||||
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("/// Attribute for generating a function to get one EF entity with a predicate as argument and a return type for the projection.")
|
|
||||||
.WriteLine("/// </summary>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]")
|
|
||||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
|
||||||
.WriteOpeningBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine($"public {AttributeName}Attribute(Type returnType){"{}"}")
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteClosingBracket() // class
|
|
||||||
.WriteClosingBracket(); // namespace
|
|
||||||
|
|
||||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal class EfGetOneByAttributeSource
|
|
||||||
{
|
|
||||||
internal const string AttributeName = "EfGetOneBy";
|
|
||||||
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("/// Generate Add methods for interacting with the entity")
|
|
||||||
.WriteLine("/// </summary>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]")
|
|
||||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
|
||||||
.WriteOpeningBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine($"public {AttributeName}Attribute(string bynameof, Type returnType)")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteClosingBracket()
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteClosingBracket() // class
|
|
||||||
.WriteClosingBracket(); // namespace
|
|
||||||
|
|
||||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
public class EfUpdateMethodsAttributeSource
|
|
||||||
{
|
|
||||||
internal const string AttributeName = "EfUpdateMethods";
|
|
||||||
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("/// Generate Add methods for interacting with the entity")
|
|
||||||
.WriteLine("/// </summary>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]")
|
|
||||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
|
||||||
.WriteOpeningBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine($"public {AttributeName}Attribute(Type updateType, Type returnType, string keyPropertyMemberName)")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteClosingBracket()
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteClosingBracket() // class
|
|
||||||
.WriteClosingBracket(); // namespace
|
|
||||||
|
|
||||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal static class EfGetOneAttributeSource
|
|
||||||
{
|
|
||||||
internal const string AttributeName = "EfGetOne";
|
|
||||||
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("/// Attribute for generating a function to get one EF entity with a predicate as argument and a return type for the projection.")
|
|
||||||
.WriteLine("/// </summary>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]")
|
|
||||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
|
||||||
.WriteOpeningBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine($"public {AttributeName}Attribute(Type returnType){"{}"}")
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteClosingBracket() // class
|
|
||||||
.WriteClosingBracket(); // namespace
|
|
||||||
|
|
||||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal static class EfAddMethodsAttributeSource
|
|
||||||
{
|
|
||||||
internal const string AttributeName = "EfAddMethods";
|
|
||||||
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("/// Generate Add methods for interacting with the entity")
|
|
||||||
.WriteLine("/// </summary>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]")
|
|
||||||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
|
|
||||||
.WriteOpeningBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine($"public {AttributeName}Attribute(Type createType = null, Type returnType = null){"{}"}")
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteClosingBracket() // class
|
|
||||||
.WriteClosingBracket(); // namespace
|
|
||||||
|
|
||||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,495 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using MapTo.Extensions;
|
|
||||||
using MapTo.Sources;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using CSharpExtensions = Microsoft.CodeAnalysis.CSharpExtensions;
|
|
||||||
|
|
||||||
#pragma warning disable CS8602
|
|
||||||
|
|
||||||
namespace MapTo
|
|
||||||
{
|
|
||||||
internal class EfGeneratorContext
|
|
||||||
{
|
|
||||||
private readonly List<SymbolDisplayPart> _ignoredNamespaces;
|
|
||||||
|
|
||||||
|
|
||||||
public ImmutableArray<Diagnostic> Diagnostics { get; private set; }
|
|
||||||
public EfMethodsModel? Model { get; private set; }
|
|
||||||
protected Compilation Compilation { get; }
|
|
||||||
protected INamedTypeSymbol MappingContextTypeSymbol { get; }
|
|
||||||
protected SourceGenerationOptions SourceGenerationOptions { get; }
|
|
||||||
protected TypeDeclarationSyntax TypeSyntax { get; }
|
|
||||||
|
|
||||||
protected ImmutableArray<string> Usings { get; private set; }
|
|
||||||
protected INamedTypeSymbol EfUpdateMethodsTypeSymbol { get; }
|
|
||||||
protected INamedTypeSymbol EfAddMethodsTypeSymbol { get; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected EfGeneratorContext(
|
|
||||||
Compilation compilation,
|
|
||||||
SourceGenerationOptions sourceGenerationOptions,
|
|
||||||
TypeDeclarationSyntax typeSyntax)
|
|
||||||
{
|
|
||||||
Compilation = compilation;
|
|
||||||
_ignoredNamespaces = new();
|
|
||||||
Diagnostics = ImmutableArray<Diagnostic>.Empty;
|
|
||||||
Usings = ImmutableArray.Create("System", Constants.RootNamespace);
|
|
||||||
SourceGenerationOptions = sourceGenerationOptions;
|
|
||||||
TypeSyntax = typeSyntax;
|
|
||||||
EfUpdateMethodsTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(EfUpdateMethodsAttributeSource.FullyQualifiedName);
|
|
||||||
EfAddMethodsTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(EfAddMethodsAttributeSource.FullyQualifiedName);
|
|
||||||
AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EfGeneratorContext Create(
|
|
||||||
Compilation compilation,
|
|
||||||
SourceGenerationOptions sourceGenerationOptions,
|
|
||||||
TypeDeclarationSyntax typeSyntax)
|
|
||||||
{
|
|
||||||
EfGeneratorContext context = typeSyntax switch
|
|
||||||
{
|
|
||||||
ClassDeclarationSyntax => new EfGeneratorContext(compilation, sourceGenerationOptions, typeSyntax),
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
|
|
||||||
context.Model = context.CreateMappingModel();
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void AddDiagnostic(Diagnostic diagnostic)
|
|
||||||
{
|
|
||||||
Diagnostics = Diagnostics.Add(diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void AddUsingIfRequired(ISymbol? namedTypeSymbol) =>
|
|
||||||
AddUsingIfRequired(namedTypeSymbol?.ContainingNamespace.IsGlobalNamespace == false, namedTypeSymbol?.ContainingNamespace);
|
|
||||||
|
|
||||||
protected void AddUsingIfRequired(bool condition, INamespaceSymbol? ns) =>
|
|
||||||
AddUsingIfRequired(condition && ns is not null && !_ignoredNamespaces.Contains(ns.ToDisplayParts().First()), ns?.ToDisplayString());
|
|
||||||
|
|
||||||
protected void AddUsingIfRequired(bool condition, string? ns)
|
|
||||||
{
|
|
||||||
if (ns is not null && condition && ns != TypeSyntax.GetNamespace() && !Usings.Contains(ns))
|
|
||||||
{
|
|
||||||
Usings = Usings.Add(ns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ImmutableArray<INamedTypeSymbol> GetEntityTypeSymbol(MemberDeclarationSyntax memberDeclarationSyntax, string attributeName, SemanticModel? semanticModel = null)
|
|
||||||
{
|
|
||||||
var attributeData = memberDeclarationSyntax.GetAttribute(attributeName);
|
|
||||||
var sourceSymbol = GetEntityTypeSymbols(attributeData, semanticModel);
|
|
||||||
return sourceSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ImmutableArray<LiteralExpressionSyntax> GetEntityStringLiteralSymbol(MemberDeclarationSyntax memberDeclarationSyntax, string attributeName, SemanticModel? semanticModel = null)
|
|
||||||
{
|
|
||||||
var attributeData = memberDeclarationSyntax.GetAttribute(attributeName);
|
|
||||||
var sourceSymbol = GetEntityStringLiteralSymbol(attributeData);
|
|
||||||
return sourceSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TypeInfo FindSecondTypeInfoOfMemberExpressionSyntax(SemanticModel semanticModel, SyntaxNode attributeSyntax)
|
|
||||||
{
|
|
||||||
semanticModel ??= Compilation.GetSemanticModel(attributeSyntax.SyntaxTree);
|
|
||||||
var descendentNodes = attributeSyntax
|
|
||||||
.DescendantNodes();
|
|
||||||
|
|
||||||
var expression = descendentNodes.OfType<MemberAccessExpressionSyntax>().FirstOrDefault()?.Expression;
|
|
||||||
if (expression != null)
|
|
||||||
{
|
|
||||||
return semanticModel.GetTypeInfo(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TypeInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TypeInfo? FindFirstTypeInfoOfMemberExpressionSyntax(SemanticModel semanticModel, SyntaxNode attributeSyntax)
|
|
||||||
{
|
|
||||||
semanticModel ??= Compilation.GetSemanticModel(attributeSyntax.SyntaxTree);
|
|
||||||
var descendentNodes = attributeSyntax
|
|
||||||
.DescendantNodes();
|
|
||||||
|
|
||||||
// Get the type of the key member id
|
|
||||||
var expression = descendentNodes.OfType<MemberAccessExpressionSyntax>().FirstOrDefault();
|
|
||||||
if (expression != null)
|
|
||||||
{
|
|
||||||
return semanticModel.GetTypeInfo(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected string ExtractNameOfMemberName(MemberDeclarationSyntax memberDeclarationSyntax, string attributeName, SemanticModel? semanticModel = null)
|
|
||||||
{
|
|
||||||
var attributeData = memberDeclarationSyntax.GetAttribute(attributeName);
|
|
||||||
var sourceSymbol = ExtractNameOfMemberName(attributeData, semanticModel);
|
|
||||||
return sourceSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ImmutableArray<INamedTypeSymbol> GetEntityTypeSymbols(SyntaxNode? attributeSyntax, SemanticModel? semanticModel = null)
|
|
||||||
{
|
|
||||||
if (attributeSyntax is null)
|
|
||||||
{
|
|
||||||
return new ImmutableArray<INamedTypeSymbol>(){};
|
|
||||||
}
|
|
||||||
|
|
||||||
semanticModel ??= Compilation.GetSemanticModel(attributeSyntax.SyntaxTree);
|
|
||||||
var descendentNodes = attributeSyntax
|
|
||||||
.DescendantNodes();
|
|
||||||
|
|
||||||
var sourceTypeExpressionSyntax = descendentNodes
|
|
||||||
.OfType<TypeOfExpressionSyntax>()
|
|
||||||
.ToImmutableArray();
|
|
||||||
|
|
||||||
// cast
|
|
||||||
var resultList = new List<INamedTypeSymbol>();
|
|
||||||
for (int i = 0; i < sourceTypeExpressionSyntax.Length; i++)
|
|
||||||
{
|
|
||||||
var sourceTypeExpression = sourceTypeExpressionSyntax[i];
|
|
||||||
if (semanticModel.GetTypeInfo(sourceTypeExpression.Type).Type is INamedTypeSymbol namedTypeSymbol)
|
|
||||||
{
|
|
||||||
resultList.Add(namedTypeSymbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultList.ToImmutableArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ImmutableArray<LiteralExpressionSyntax> GetEntityStringLiteralSymbol(SyntaxNode? attributeSyntax)
|
|
||||||
{
|
|
||||||
if (attributeSyntax is null)
|
|
||||||
{
|
|
||||||
return new ImmutableArray<LiteralExpressionSyntax>(){};
|
|
||||||
}
|
|
||||||
|
|
||||||
var descendentNodes = attributeSyntax
|
|
||||||
.DescendantNodes();
|
|
||||||
|
|
||||||
var sourceTypeExpressionSyntax = descendentNodes
|
|
||||||
.OfType<LiteralExpressionSyntax>()
|
|
||||||
.ToImmutableArray();
|
|
||||||
|
|
||||||
|
|
||||||
return sourceTypeExpressionSyntax;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string ExtractNameOfMemberName(SyntaxNode? attributeSyntax, SemanticModel semanticModel)
|
|
||||||
{
|
|
||||||
if (attributeSyntax is null)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var descendentNodes = attributeSyntax
|
|
||||||
.DescendantNodes();
|
|
||||||
|
|
||||||
var idNode = descendentNodes.OfType<InvocationExpressionSyntax>()
|
|
||||||
.FirstOrDefault()
|
|
||||||
?.ChildNodesAndTokens()
|
|
||||||
.FirstOrDefault(x => x.IsKind(SyntaxKind.ArgumentList))
|
|
||||||
.ChildNodesAndTokens()
|
|
||||||
.FirstOrDefault(x => x.IsKind(SyntaxKind.Argument))
|
|
||||||
.ChildNodesAndTokens()
|
|
||||||
.FirstOrDefault()
|
|
||||||
.ChildNodesAndTokens()
|
|
||||||
.LastOrDefault();
|
|
||||||
|
|
||||||
return idNode != null ? idNode.ToString() : String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected bool IsEnumerable(ISymbol property, out INamedTypeSymbol? namedTypeSymbolResult)
|
|
||||||
{
|
|
||||||
if (!property.TryGetTypeSymbol(out var propertyType))
|
|
||||||
{
|
|
||||||
AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyTypeFoundError(property));
|
|
||||||
namedTypeSymbolResult = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
propertyType is INamedTypeSymbol namedTypeSymbol &&
|
|
||||||
!propertyType.IsPrimitiveType() &&
|
|
||||||
(Compilation.IsGenericEnumerable(propertyType) || propertyType.AllInterfaces.Any(i => Compilation.IsGenericEnumerable(i))))
|
|
||||||
{
|
|
||||||
namedTypeSymbolResult = namedTypeSymbol;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
namedTypeSymbolResult = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableArray<MemberDeclarationSyntax> GetAllowedMemberSyntaxes(string attributeName)
|
|
||||||
{
|
|
||||||
var classDeclarationSyntax = TypeSyntax;
|
|
||||||
|
|
||||||
var syntaxes = classDeclarationSyntax.DescendantNodes()
|
|
||||||
.OfType<MemberDeclarationSyntax>()
|
|
||||||
.Where(x => x
|
|
||||||
.DescendantNodes()
|
|
||||||
.OfType<AttributeSyntax>().Any(syntax =>
|
|
||||||
syntax.Name.ToString() == attributeName))
|
|
||||||
.ToImmutableArray();
|
|
||||||
|
|
||||||
return syntaxes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EfMethodsModel? CreateMappingModel()
|
|
||||||
{
|
|
||||||
var semanticModel = Compilation.GetSemanticModel(TypeSyntax.SyntaxTree);
|
|
||||||
|
|
||||||
// get containing class type information
|
|
||||||
ClassDeclarationSyntax classDeclarationSyntax = TypeSyntax as ClassDeclarationSyntax;
|
|
||||||
|
|
||||||
// context name
|
|
||||||
var dbContextName = classDeclarationSyntax.Identifier.ValueText;
|
|
||||||
var namespaceDeclaration = classDeclarationSyntax.Parent as NamespaceDeclarationSyntax;
|
|
||||||
var contextNamespace = namespaceDeclaration.Name.ToString();
|
|
||||||
var contextTypeFullName = $"{contextNamespace}.{dbContextName}";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List<EfEntityDataModel> methodsModels = new List<EfEntityDataModel>();
|
|
||||||
|
|
||||||
foreach (var uProperty in getOneAttributesMembers)
|
|
||||||
{
|
|
||||||
var entityDataModel = GetEntityData(uProperty, semanticModel);
|
|
||||||
var newUpdateModel = ExtractEfGetEntityByModel(uProperty, semanticModel, entityDataModel);
|
|
||||||
methodsModels.Add(newUpdateModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var uProperty in addAttributeSymbols)
|
|
||||||
{
|
|
||||||
var entityDataModel = GetEntityData(uProperty, semanticModel);
|
|
||||||
var newAddModel = ExtractEfAddMethodsModel(semanticModel, uProperty, entityDataModel);
|
|
||||||
methodsModels.Add(newAddModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var uProperty in updateAttributesMembers)
|
|
||||||
{
|
|
||||||
var entityDataModel = GetEntityData(uProperty, semanticModel);
|
|
||||||
var newUpdateModel = ExtractEfUpdateMethodsModel(uProperty, semanticModel, entityDataModel);
|
|
||||||
methodsModels.Add(newUpdateModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var uProperty in getOneWithAttributesMembers)
|
|
||||||
{
|
|
||||||
var entityDataModel = GetEntityData(uProperty, semanticModel);
|
|
||||||
var newUpdateModel = ExtractEfGetEntityWithModel(uProperty, semanticModel, entityDataModel);
|
|
||||||
methodsModels.Add(newUpdateModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var uProperty in getManyWithAttributesMembers)
|
|
||||||
{
|
|
||||||
var entityDataModel = GetEntityData(uProperty, semanticModel);
|
|
||||||
var newUpdateModel = ExtractEfGetManyModel(uProperty, semanticModel, entityDataModel);
|
|
||||||
methodsModels.Add(newUpdateModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//SpinWait.SpinUntil(() => Debugger.IsAttached);
|
|
||||||
|
|
||||||
return new EfMethodsModel(
|
|
||||||
SourceGenerationOptions,
|
|
||||||
contextNamespace,
|
|
||||||
dbContextName, contextTypeFullName,
|
|
||||||
methodsModels.ToImmutableArray(),
|
|
||||||
Usings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EfEntityDataModel GetEntityData(MemberDeclarationSyntax uProperty, SemanticModel semanticModel)
|
|
||||||
{
|
|
||||||
var entityTypeData = GetEntityTypeData(uProperty, semanticModel);
|
|
||||||
string entityIdentifierName = entityTypeData.Name;
|
|
||||||
string entityFullName = entityTypeData.ToDisplayString();
|
|
||||||
var propertyNamex = (uProperty as PropertyDeclarationSyntax).Identifier.ValueText;
|
|
||||||
return new EfEntityDataModel(propertyNamex, entityFullName, entityIdentifierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EfAddMethodsModel ExtractEfAddMethodsModel(SemanticModel semanticModel, MemberDeclarationSyntax memberSyntax, EfEntityDataModel efEntityDataModel)
|
|
||||||
{
|
|
||||||
var efAddAttributeTypeSymbols =
|
|
||||||
GetEntityTypeSymbol(memberSyntax, EfAddMethodsAttributeSource.AttributeName, semanticModel);
|
|
||||||
|
|
||||||
string createTypeIdentifierName = efEntityDataModel.EntityTypeIdentifierName;
|
|
||||||
string createTypeFullName = efEntityDataModel.EntityTypeFullName;
|
|
||||||
string returnTypeIdentifierName = efEntityDataModel.EntityTypeIdentifierName;
|
|
||||||
string returnTypeFullName = efEntityDataModel.EntityTypeFullName;
|
|
||||||
|
|
||||||
|
|
||||||
if (efAddAttributeTypeSymbols.Length > 0)
|
|
||||||
{
|
|
||||||
createTypeIdentifierName = efAddAttributeTypeSymbols[0].Name;
|
|
||||||
createTypeFullName = efAddAttributeTypeSymbols[0].ToDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (efAddAttributeTypeSymbols.Length > 1)
|
|
||||||
{
|
|
||||||
returnTypeIdentifierName = efAddAttributeTypeSymbols[1].Name;
|
|
||||||
returnTypeFullName = efAddAttributeTypeSymbols[1].ToDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return new EfAddMethodsModel(efEntityDataModel, createTypeFullName, createTypeIdentifierName, returnTypeFullName,
|
|
||||||
returnTypeIdentifierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EfUpdateMethodsModel ExtractEfUpdateMethodsModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel)
|
|
||||||
{
|
|
||||||
var efAddAttributeTypeSymbols =
|
|
||||||
GetEntityTypeSymbol(uProperty, EfUpdateMethodsAttributeSource.AttributeName, semanticModel);
|
|
||||||
|
|
||||||
var keyPropertyName = ExtractNameOfMemberName(uProperty, EfUpdateMethodsAttributeSource.AttributeName, semanticModel);
|
|
||||||
var keyMemberType = FindFirstTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax);
|
|
||||||
//var secondTypeInfo = FindSecondTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax);
|
|
||||||
|
|
||||||
var keyMemberFullName = keyMemberType.Value.Type.ToDisplayString();
|
|
||||||
//var keyMemberName = keyMemberType.Value.Type.Name;
|
|
||||||
|
|
||||||
// Try grabbing string literal if there's no nameof in it
|
|
||||||
if (keyPropertyName == string.Empty)
|
|
||||||
{
|
|
||||||
keyPropertyName = GetEntityStringLiteralSymbol(TypeSyntax, EfUpdateMethodsAttributeSource.AttributeName, semanticModel)
|
|
||||||
.FirstOrDefault()?
|
|
||||||
.Token.ValueText ?? "Id";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (efAddAttributeTypeSymbols == null) return null;
|
|
||||||
|
|
||||||
string updateTypeIdentifierName = entityDataModel.EntityTypeIdentifierName;
|
|
||||||
string updateTypeFullName = entityDataModel.EntityTypeFullName;
|
|
||||||
string returnTypeIdentifierName = entityDataModel.EntityTypeIdentifierName;
|
|
||||||
string returnTypeFullName = entityDataModel.EntityTypeFullName;
|
|
||||||
|
|
||||||
if (efAddAttributeTypeSymbols.Length > 0)
|
|
||||||
{
|
|
||||||
updateTypeIdentifierName = efAddAttributeTypeSymbols[0].Name;
|
|
||||||
updateTypeFullName = efAddAttributeTypeSymbols[0].ToDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab return type from attribute argument
|
|
||||||
if (efAddAttributeTypeSymbols.Length > 1)
|
|
||||||
{
|
|
||||||
returnTypeIdentifierName = efAddAttributeTypeSymbols[1].Name;
|
|
||||||
returnTypeFullName = efAddAttributeTypeSymbols[1].ToDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return new EfUpdateMethodsModel(entityDataModel, updateTypeFullName, updateTypeIdentifierName, returnTypeFullName,
|
|
||||||
returnTypeIdentifierName, keyPropertyName, keyMemberFullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EfGetOneWithModel ExtractEfGetEntityWithModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel)
|
|
||||||
{
|
|
||||||
|
|
||||||
var efTypeofSymbols =GetEntityTypeSymbol(uProperty, EfGetOneByAttributeSource.AttributeName, semanticModel);
|
|
||||||
if (efTypeofSymbols == null) return null;
|
|
||||||
|
|
||||||
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 EfGetOneWithModel(entityDataModel, returnTypeFullName, returnTypeIdentifierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private EfGetManyModel ExtractEfGetManyModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel)
|
|
||||||
{
|
|
||||||
|
|
||||||
var efTypeofSymbols =GetEntityTypeSymbol(uProperty, EfGetOneByAttributeSource.AttributeName, semanticModel);
|
|
||||||
if (efTypeofSymbols == null) return null;
|
|
||||||
|
|
||||||
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 EfGetManyModel(entityDataModel, returnTypeFullName, returnTypeIdentifierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private EfGetOneByModel ExtractEfGetEntityByModel(MemberDeclarationSyntax uProperty, SemanticModel semanticModel, EfEntityDataModel entityDataModel)
|
|
||||||
{
|
|
||||||
|
|
||||||
var efTypeofSymbols = GetEntityTypeSymbol(uProperty, EfGetOneByAttributeSource.AttributeName, semanticModel);
|
|
||||||
var byParamPropertyName = ExtractNameOfMemberName(uProperty, EfGetOneByAttributeSource.AttributeName, semanticModel);
|
|
||||||
var keyMemberType = FindFirstTypeInfoOfMemberExpressionSyntax(semanticModel, TypeSyntax);
|
|
||||||
|
|
||||||
var byParamFullTypeName = keyMemberType.Value.Type.ToDisplayString();
|
|
||||||
|
|
||||||
// Try grabbing string literal if there's no nameof in it
|
|
||||||
if (byParamPropertyName == string.Empty)
|
|
||||||
{
|
|
||||||
byParamPropertyName = GetEntityStringLiteralSymbol(TypeSyntax, EfGetOneByAttributeSource.AttributeName, semanticModel)
|
|
||||||
.FirstOrDefault()?
|
|
||||||
.Token.ValueText ?? "Id";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (efTypeofSymbols == null) return null;
|
|
||||||
|
|
||||||
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 EfGetOneByModel(entityDataModel, byParamPropertyName, byParamFullTypeName, returnTypeFullName, returnTypeIdentifierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static ITypeSymbol GetEntityTypeData(MemberDeclarationSyntax memberDeclarationSyntax, SemanticModel? semanticModel = null)
|
|
||||||
{
|
|
||||||
var member = (memberDeclarationSyntax as PropertyDeclarationSyntax).Type;
|
|
||||||
var genericSyntax = member as GenericNameSyntax;
|
|
||||||
var typeInfo = semanticModel.GetTypeInfo( genericSyntax);
|
|
||||||
var firstTypeArgument = (typeInfo.ConvertedType as INamedTypeSymbol).TypeArguments[0];
|
|
||||||
return firstTypeArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using MapTo.Extensions;
|
|
||||||
using MapTo.Sources;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
|
|
||||||
namespace MapTo
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Ef methods source generator
|
|
||||||
/// </summary>
|
|
||||||
[Generator]
|
|
||||||
public class EfMethodsGenerator: ISourceGenerator
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Initialize(GeneratorInitializationContext context)
|
|
||||||
{
|
|
||||||
context.RegisterForSyntaxNotifications(() => new EfMethodsSyntaxReceiver());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Execute(GeneratorExecutionContext context)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var options = SourceGenerationOptions.From(context);
|
|
||||||
|
|
||||||
var compilation = context.Compilation
|
|
||||||
.AddSource(ref context, EfGeneratorAttributeSource.Generate(options))
|
|
||||||
.AddSource(ref context, EfAddMethodsAttributeSource.Generate(options))
|
|
||||||
.AddSource(ref context, EfUpdateMethodsAttributeSource.Generate(options))
|
|
||||||
.AddSource(ref context, EfGetOneByAttributeSource.Generate(options))
|
|
||||||
.AddSource(ref context, EfGetOneAttributeSource.Generate(options))
|
|
||||||
.AddSource(ref context, EfGetManyAttributeSource.Generate(options));
|
|
||||||
|
|
||||||
|
|
||||||
if (context.SyntaxReceiver is EfMethodsSyntaxReceiver receiver && receiver.CandidateTypes.Any())
|
|
||||||
{
|
|
||||||
AddGeneratedExtensions(context, compilation, receiver.CandidateTypes, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable<TypeDeclarationSyntax> 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");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var candidateMember in candidateMembers)
|
|
||||||
{
|
|
||||||
|
|
||||||
var mappingContext = EfGeneratorContext.Create(compilation, options, candidateMember);
|
|
||||||
|
|
||||||
mappingContext.Diagnostics.ForEach(context.ReportDiagnostic);
|
|
||||||
|
|
||||||
if (mappingContext.Model is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var (source, hintName) = candidateMember switch
|
|
||||||
{
|
|
||||||
ClassDeclarationSyntax => EfMethodsSource.Generate(
|
|
||||||
mappingContext.Model,
|
|
||||||
addSourceTemplate,
|
|
||||||
updateSourceTemplate,
|
|
||||||
getOneByTemplate,
|
|
||||||
getOneWithByTemplate,
|
|
||||||
getManyWithTemplate),
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
|
|
||||||
context.AddSource(hintName, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetCsxMethodTemplate(GeneratorExecutionContext context, string templateName)
|
|
||||||
{
|
|
||||||
return context
|
|
||||||
.AdditionalFiles
|
|
||||||
.FirstOrDefault(x => x.Path.Contains(templateName))?
|
|
||||||
.GetText()?
|
|
||||||
.ToString() ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using MapTo.Extensions;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using static MapTo.Sources.Constants;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal static class EfMethodsSource
|
|
||||||
{
|
|
||||||
internal static SourceCode Generate(EfMethodsModel model,
|
|
||||||
string addSourceTemplate,
|
|
||||||
string updateSourceTemplate,
|
|
||||||
string getOneBySourceTemplate,
|
|
||||||
string getOneWithSourceTemplate,
|
|
||||||
string getManyWithSourceTemplate
|
|
||||||
)
|
|
||||||
{
|
|
||||||
using var builder = new SourceBuilder();
|
|
||||||
|
|
||||||
|
|
||||||
var contextFullName = model.ContextFullType;
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine(GeneratedFilesHeader)
|
|
||||||
.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}")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
// Class declaration
|
|
||||||
.WriteLine($"public static partial class {model.ContextTypeName}Extensions")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine();
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var methodModel in model.MethodsModels.OrderBy(x => x.PropertyName))
|
|
||||||
{
|
|
||||||
switch (methodModel)
|
|
||||||
{
|
|
||||||
case EfAddMethodsModel modl:
|
|
||||||
builder
|
|
||||||
.EfAddAddEntityMethod(model, modl, addSourceTemplate)
|
|
||||||
.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()
|
|
||||||
.WriteLine()
|
|
||||||
// End namespace declaration
|
|
||||||
.WriteClosingBracket();
|
|
||||||
|
|
||||||
var generatedCode = builder.ToString();
|
|
||||||
var hintName = $"{model.ContextTypeName}Extensions.g.cs";
|
|
||||||
return new(generatedCode, hintName);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static SourceBuilder ParseTemplate(this SourceBuilder builder, string finalTemplate)
|
|
||||||
{
|
|
||||||
string[] array = finalTemplate.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
for (var i = 0; i < array.Length; i++)
|
|
||||||
{
|
|
||||||
var line = array[i];
|
|
||||||
builder.WriteLine(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
using MapTo.Extensions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal static class EfAddEntityTemplateSource
|
|
||||||
{
|
|
||||||
internal static SourceBuilder EfAddAddEntityMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfAddMethodsModel model, string addSourceTemplate)
|
|
||||||
{
|
|
||||||
var returnTypeFullName = model.ReturnTypeFullName;
|
|
||||||
var createTypeFullName = model.CreateTypeFullName;
|
|
||||||
var newEntityVarName = $"new{model.EntityTypeIdentifierName}";
|
|
||||||
var toCreateVarName = $"{model.EntityTypeIdentifierName.ToCamelCase()}ToCreate";
|
|
||||||
|
|
||||||
var entityTypeName = model.EntityTypeIdentifierName;
|
|
||||||
var contextFullName = methodsModel.ContextFullType;
|
|
||||||
var propertyName = model.PropertyName;
|
|
||||||
|
|
||||||
if (!addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
var templateToSourceBuilder = new StringBuilder(addSourceTemplate);
|
|
||||||
templateToSourceBuilder
|
|
||||||
.Replace("{entityTypeName}", model.EntityTypeIdentifierName)
|
|
||||||
.Replace("{returnTypeFullName}", returnTypeFullName)
|
|
||||||
.Replace("{createTypeFullName}", createTypeFullName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
.Replace("{toCreateVarName}", toCreateVarName)
|
|
||||||
.Replace("{newEntityVarName}", newEntityVarName)
|
|
||||||
.Replace("{entityTypeFullName}", model.EntityTypeFullName)
|
|
||||||
.Replace("{propertyName}", propertyName);
|
|
||||||
|
|
||||||
builder
|
|
||||||
.ParseTemplate(templateToSourceBuilder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.WriteComment("Generated body")
|
|
||||||
//.WriteLine(GeneratedFilesHeader)
|
|
||||||
.WriteLine($"public static (bool, {model.ReturnTypeFullName}) Add{entityTypeName}(")
|
|
||||||
.WriteLine($"this {contextFullName} dbContext,")
|
|
||||||
.WriteLine($"{createTypeFullName} {toCreateVarName})")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine($"var {newEntityVarName} = new {model.EntityTypeFullName}({toCreateVarName});")
|
|
||||||
.WriteLine($"dbContext.{propertyName}.Add({newEntityVarName});")
|
|
||||||
.WriteLine($"var success = dbContext.SaveChanges() >= 0;")
|
|
||||||
.WriteLine($"return (success, new {returnTypeFullName}({newEntityVarName}));")
|
|
||||||
.WriteClosingBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
using MapTo.Extensions;
|
|
||||||
using MapTo.Sources;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MapTo
|
|
||||||
{
|
|
||||||
internal static class EfGetOneEntityByTemplate
|
|
||||||
{
|
|
||||||
internal static SourceBuilder EfAddGetOneEntityByMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetOneByModel model, string addSourceTemplate)
|
|
||||||
{
|
|
||||||
var returnTypeFullName = model.ReturnTypeFullName;
|
|
||||||
var entityTypeName = model.EntityTypeIdentifierName;
|
|
||||||
var byParamPropertyName = model.ByParamPropertyName;
|
|
||||||
var byParamFullType = model.ByParamFullTypeName;
|
|
||||||
|
|
||||||
var contextFullName = methodsModel.ContextFullType;
|
|
||||||
var byParamVarName = model.ByParamPropertyName.ToCamelCase();
|
|
||||||
var findEntityVarName = model.EntityTypeIdentifierName.ToCamelCase();
|
|
||||||
var propertyName = model.PropertyName;
|
|
||||||
|
|
||||||
if (!addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
var templateToSourceBuilder = new StringBuilder(addSourceTemplate);
|
|
||||||
templateToSourceBuilder
|
|
||||||
.Replace("{returnTypeFullName}", returnTypeFullName)
|
|
||||||
.Replace("{entityTypeName}", entityTypeName)
|
|
||||||
.Replace("{byParamPropertyName}", byParamPropertyName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
|
|
||||||
.Replace("{byParamFullType}", byParamFullType)
|
|
||||||
.Replace("{byParamVarName}", byParamVarName)
|
|
||||||
.Replace("{findEntityVarName}", findEntityVarName)
|
|
||||||
.Replace("{propertyName}", propertyName);
|
|
||||||
|
|
||||||
builder
|
|
||||||
.ParseTemplate(templateToSourceBuilder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.WriteComment("Generated body")
|
|
||||||
//.WriteLine(GeneratedFilesHeader)
|
|
||||||
.WriteLine($"public static (bool, {returnTypeFullName}) GetOne{entityTypeName}By{byParamPropertyName} (this {contextFullName} dbContext, {byParamFullType} {byParamVarName})")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine($"var {findEntityVarName} = dbContext.{propertyName}")
|
|
||||||
.WriteLine($".Where(x => x.{byParamPropertyName} == {byParamVarName})")
|
|
||||||
.WriteLine($".Select(x => new {returnTypeFullName}(x))")
|
|
||||||
.WriteLine($".FirstOrDefault();")
|
|
||||||
.WriteLine($"return ({findEntityVarName} != null, {findEntityVarName});")
|
|
||||||
.WriteClosingBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
using MapTo.Extensions;
|
|
||||||
using MapTo.Sources;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MapTo
|
|
||||||
{
|
|
||||||
internal static class EfGetOneWithTemplateSource
|
|
||||||
{
|
|
||||||
internal static SourceBuilder EfAddGetOneEntityWithMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetOneWithModel model, string addSourceTemplate)
|
|
||||||
{
|
|
||||||
var returnTypeFullName = model.ReturnTypeFullName;
|
|
||||||
var entityTypeName = model.EntityTypeIdentifierName;
|
|
||||||
|
|
||||||
var contextFullName = methodsModel.ContextFullType;
|
|
||||||
var findEntityVarName = model.EntityTypeIdentifierName.ToCamelCase();
|
|
||||||
var propertyName = model.PropertyName;
|
|
||||||
|
|
||||||
if (!addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
var templateToSourceBuilder = new StringBuilder(addSourceTemplate);
|
|
||||||
templateToSourceBuilder
|
|
||||||
.Replace("{returnTypeFullName}", returnTypeFullName)
|
|
||||||
.Replace("{entityTypeName}", entityTypeName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
.Replace("{findEntityVarName}", findEntityVarName)
|
|
||||||
.Replace("{propertyName}", propertyName);
|
|
||||||
|
|
||||||
builder
|
|
||||||
.ParseTemplate(templateToSourceBuilder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.WriteComment("Generated body")
|
|
||||||
//.WriteLine(GeneratedFilesHeader)
|
|
||||||
.WriteLine($"public static (bool, {returnTypeFullName}) GetOne{entityTypeName}With (this {contextFullName} dbContext, Expression<Func<{returnTypeFullName},bool>> with) ")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine($"var {findEntityVarName} =")
|
|
||||||
.WriteLine($".Select(x => new {returnTypeFullName}(x))")
|
|
||||||
.WriteLine($".FirstOrDefault(with);")
|
|
||||||
.WriteLine()
|
|
||||||
.WriteLine($"return ({findEntityVarName} != null, {findEntityVarName});")
|
|
||||||
.WriteClosingBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static SourceBuilder EfAddGetManyWithMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfGetManyModel model, string addSourceTemplate)
|
|
||||||
{
|
|
||||||
var returnTypeFullName = model.ReturnTypeFullName;
|
|
||||||
var entityTypeName = model.EntityTypeIdentifierName;
|
|
||||||
|
|
||||||
var contextFullName = methodsModel.ContextFullType;
|
|
||||||
var findEntityVarName = model.EntityTypeIdentifierName.ToCamelCase();
|
|
||||||
var propertyName = model.PropertyName;
|
|
||||||
|
|
||||||
if (!addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
var templateToSourceBuilder = new StringBuilder(addSourceTemplate);
|
|
||||||
templateToSourceBuilder
|
|
||||||
.Replace("{returnTypeFullName}", returnTypeFullName)
|
|
||||||
.Replace("{entityTypeName}", entityTypeName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
.Replace("{contextFullName}", contextFullName)
|
|
||||||
.Replace("{findEntityVarName}", findEntityVarName)
|
|
||||||
.Replace("{propertyName}", propertyName);
|
|
||||||
|
|
||||||
builder
|
|
||||||
.ParseTemplate(templateToSourceBuilder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.WriteComment("Generated body")
|
|
||||||
//.WriteLine(GeneratedFilesHeader)
|
|
||||||
.WriteLine($"public static (bool, ImmutableArray<{returnTypeFullName}>) Get{propertyName}(this {contextFullName}dbContext, Expression <Func<{returnTypeFullName}, bool> > where = null,")
|
|
||||||
.WriteLine($"Expression <Func<{returnTypeFullName}, object> > orderBy = null, int skip = 0, int take = 50, int orderDir = 1)")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.WriteLine($"var query = dbContext")
|
|
||||||
.WriteLine($".{propertyName}")
|
|
||||||
.WriteLine($".Select(x => new {returnTypeFullName}(x));")
|
|
||||||
.WriteComment("limit take by 200 records")
|
|
||||||
.WriteLine("if (take > 200) take = 200;")
|
|
||||||
.WriteLine("query.Skip(skip).Take(take);")
|
|
||||||
.WriteLine("if (where != null) query.Where(where);")
|
|
||||||
.WriteLine("if (orderDir == 1) query.OrderBy(orderBy);")
|
|
||||||
.WriteLine("else query.OrderByDescending(orderBy);")
|
|
||||||
.WriteLine($"return query.ToImmutableArray();")
|
|
||||||
.WriteClosingBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
using MapTo.Extensions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MapTo.Sources
|
|
||||||
{
|
|
||||||
internal static class EfUpdateEntityTemplateSource
|
|
||||||
{
|
|
||||||
internal static SourceBuilder EfAddUpdateEntityMethod(this SourceBuilder builder, EfMethodsModel methodsModel, EfUpdateMethodsModel model, string updateSourceTemplate)
|
|
||||||
{
|
|
||||||
var entityTypeName = model.EntityTypeIdentifierName;
|
|
||||||
var contextFullName = methodsModel.ContextFullType;
|
|
||||||
var propertyName = model.PropertyName;
|
|
||||||
var returnTypeFullName = model.ReturnTypeFullName;
|
|
||||||
var updateTypeFullName = model.UpdateTypeFullName;
|
|
||||||
var updateVarName = $"{entityTypeName.ToCamelCase()}ToUpdate";
|
|
||||||
var keyPropertyName = model.KeyPropertyName;
|
|
||||||
var keyTypeFullName = model.KeyFullTypeName;
|
|
||||||
var existingVarName = entityTypeName.ToCamelCase();
|
|
||||||
var keyVarName = entityTypeName.ToCamelCase() + model.KeyPropertyName;
|
|
||||||
if (!updateSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
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("{keyTypeFullName}", keyTypeFullName)
|
|
||||||
.Replace("{keyPropertyName}", keyPropertyName)
|
|
||||||
.Replace("{keyVarName}", keyVarName)
|
|
||||||
.Replace("{existingEntityVarName}", existingVarName);
|
|
||||||
builder
|
|
||||||
.ParseTemplate(templateToSourceBuilder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateSourceTemplate.IsEmpty())
|
|
||||||
{
|
|
||||||
builder
|
|
||||||
.WriteComment("Generated body")
|
|
||||||
.WriteLine($"public static (bool, {returnTypeFullName}) Update{entityTypeName}(")
|
|
||||||
.WriteLine($"this {contextFullName} dbContext,")
|
|
||||||
.WriteLine($"{updateTypeFullName} {updateVarName},")
|
|
||||||
.WriteLine($"{keyTypeFullName} {keyVarName})")
|
|
||||||
.WriteOpeningBracket()
|
|
||||||
.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}({existingVarName}));")
|
|
||||||
.WriteClosingBracket();
|
|
||||||
|
|
||||||
builder
|
|
||||||
.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using MapTo.Sources;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
|
|
||||||
namespace MapTo
|
|
||||||
{
|
|
||||||
internal enum EfMethodsAttributeType
|
|
||||||
{
|
|
||||||
Add,
|
|
||||||
Update,
|
|
||||||
Invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
internal record CandidateMember(
|
|
||||||
MemberDeclarationSyntax MemberDeclarationSyntax,
|
|
||||||
EfMethodsAttributeType AttributeType
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
internal class EfMethodsSyntaxReceiver : ISyntaxReceiver
|
|
||||||
{
|
|
||||||
public List<TypeDeclarationSyntax> CandidateTypes { get; } = new();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
|
||||||
{
|
|
||||||
if (syntaxNode is not TypeDeclarationSyntax { AttributeLists: { Count: >= 1 } attributes } typeDeclarationSyntax)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var attributeSyntax = attributes
|
|
||||||
.SelectMany(a => a.Attributes)
|
|
||||||
.FirstOrDefault(a => a.Name is
|
|
||||||
IdentifierNameSyntax { Identifier: { ValueText: EfGeneratorAttributeSource.AttributeName } }
|
|
||||||
or
|
|
||||||
QualifiedNameSyntax
|
|
||||||
{
|
|
||||||
Left: IdentifierNameSyntax { Identifier: { ValueText: Constants.RootNamespace } },
|
|
||||||
Right: IdentifierNameSyntax { Identifier: { ValueText: EfGeneratorAttributeSource.AttributeName } }
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (attributeSyntax is not null)
|
|
||||||
{
|
|
||||||
CandidateTypes.Add(typeDeclarationSyntax);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -50,7 +50,7 @@ namespace MapTo.Tests.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
var driver = CSharpGeneratorDriver.Create(
|
var driver = CSharpGeneratorDriver.Create(
|
||||||
new List<ISourceGenerator>() { new MapToGenerator(), new EfMethodsGenerator() },
|
new List<ISourceGenerator>() { new MapToGenerator()},
|
||||||
optionsProvider: new TestAnalyzerConfigOptionsProvider(analyzerConfigOptions),
|
optionsProvider: new TestAnalyzerConfigOptionsProvider(analyzerConfigOptions),
|
||||||
parseOptions: new CSharpParseOptions(languageVersion)
|
parseOptions: new CSharpParseOptions(languageVersion)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue