Skip to content

Instantly share code, notes, and snippets.

@explorer14
Last active February 21, 2023 22:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save explorer14/c4e75cd2d3dcd7f24092b74164504554 to your computer and use it in GitHub Desktop.
Save explorer14/c4e75cd2d3dcd7f24092b74164504554 to your computer and use it in GitHub Desktop.
[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 pds, 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

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).

@OnSive
Copy link

OnSive commented Feb 20, 2023

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).

// bump @explorer14

@explorer14
Copy link
Author

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).

// bump @explorer14

Done! I didn't realise Thomas had commented on a gist! Thanks for the bump!🙂

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