Skip to content

Instantly share code, notes, and snippets.

@explorer14
Last active Aug 17, 2021
Embed
What would you like to do?
[Generator]
public class DtoGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var targetTypeTracker = context.SyntaxContextReceiver as TargetTypeTracker;
var codeBuilder = new StringBuilder();
foreach (var typeNode in targetTypeTracker.TypesNeedingDtoGening)
{
// Use the semantic model to get the symbol for this type
var typeNodeSymbol = context.Compilation
.GetSemanticModel(typeNode.SyntaxTree)
.GetDeclaredSymbol(typeNode);
// get the namespace of the entity class
var entityClassNamespace = typeNodeSymbol.ContainingNamespace?.ToDisplayString() ?? "NoNamespace";
// give each DTO a name, just suffix the entity class name with "Dto"
var generatedDtoClassName = $"{typeNodeSymbol.Name}Dto";
// Add usings
codeBuilder.AppendLine("using System;");
codeBuilder.AppendLine("using System.Collections.Generic;");
codeBuilder.AppendLine("using System.Linq;");
// Add target namespace
codeBuilder.AppendLine($"namespace {entityClassNamespace}.Dtos");
codeBuilder.AppendLine("{");
// Start class
codeBuilder.AppendLine($"\tpublic class {generatedDtoClassName}");
codeBuilder.AppendLine("\t{");
// get all the properties defined in this class
var allProperties = typeNode.Members.OfType<PropertyDeclarationSyntax>();
// for each property in the domain entity, create a corresponding property
// in the DTO with the same type
foreach (var property in allProperties)
codeBuilder.AppendLine($"\t\t{property.BuildDtoProperty(context.Compilation)}");
// Add closing braces
codeBuilder.AppendLine("\t}");
codeBuilder.AppendLine("}");
// add the code for this DTO class to the context so it can be added to the build
context.AddSource(generatedDtoClassName,
SourceText.From(codeBuilder.ToString(), Encoding.UTF8));
codeBuilder.Clear();
}
}
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new TargetTypeTracker());
}
}
internal static class SourceGenExtns
{
internal static string BuildDtoProperty(
this PropertyDeclarationSyntax property, Compilation compilation)
{
// get the symbol for this property from the semantic model
var symbol = compilation
.GetSemanticModel(pds.SyntaxTree)
.GetDeclaredSymbol(pds);
var property = (symbol as IPropertySymbol);
// use the same type and name for the DTO properties as on the entity
return $"public {property.Type.Name()} {property.Name} {{get; set;}}";
}
// instead of returning "System.Collections.Generic.IList<>", just condense it to "IList<>"
// the namespace is already added in the usings block
internal static string Name(this ITypeSymbol typeSymbol) =>
typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
}
@thmsantonio
Copy link

thmsantonio commented Aug 17, 2021

I have one suggestion on the code. The parameter property of the BuildDtoProperty method should be named pds, otherwise it will conflict with the property variable (row nr. 70).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment