Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save SaahilClaypool/b1aca690f154ec0fe9ed49751988e701 to your computer and use it in GitHub Desktop.
Save SaahilClaypool/b1aca690f154ec0fe9ed49751988e701 to your computer and use it in GitHub Desktop.
Dapper constructor source generator
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Diagnostics;
namespace Srcgen;
/// <summary>
/// Author: Saahil Claypool
/// https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md
/// </summary>
[Generator]
public class DapperConstructorGenerator : IIncrementalGenerator
{
public const string AttributeNameSpace = "Srcgen";
public const string AttributeName = "DapperConstructorAttribute";
public const string AttributeFullName = "Srcgen.DapperConstructorAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
AttributeFullName,
(_, _) => true,
(syntaxContext, _) => syntaxContext
).Combine(context.CompilationProvider);
context.RegisterSourceOutput(generatorAttributes, (sourceProductionContext, tuple) =>
{
var (generatorActivationContext, compilation) = tuple;
if (generatorActivationContext.TargetSymbol is not INamedTypeSymbol symbol)
return;
var constructor = symbol.Constructors.First();
var parameters = constructor.Parameters.Select(p => (Type: $"{p.Type.ContainingNamespace}.{p.Type.Name}", p.Name));
var defaultsString = string.Join(", ", parameters.Select(p => "default!"));
var source = $$"""
namespace {{symbol.ContainingNamespace}}
{
public partial record {{symbol.Name}}
{
private {{symbol.Name}}() : this({{defaultsString}}) { }
}
}
""";
// Avoid duplicate names
sourceProductionContext.AddSource(
$"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))}_{AttributeFullName}.g.cs",
source);
});
context.RegisterPostInitializationOutput(callback: ctx =>
{
ctx.AddSource(
$"{AttributeFullName}.g.cs",
$$"""
namespace {{AttributeNameSpace}}
{
/// <summary>
/// generate a constructor with all parameters set to default
/// this is only to be used by dapper or other serializers that require
/// a no parameter constructor.
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
sealed class {{AttributeName}} : System.Attribute
{
public {{AttributeName}}() { }
}
}
"""
);
});
}
}
[Srcgen.DapperConstructor]
public partial record TestMe(string A, string B)
{
public int C { get; set; }
}
/* generated
namespace Tasks
{
public partial record TestResult
{
private TestResult() : this(default!, default!) { }
}
}
*/
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
</ItemGroup>
</Project>
@x0rld
Copy link

x0rld commented Jun 11, 2023

Hi,
I tried to use it but I have the error "DapperConstructor" and "DapperConstructorAttribute" doesn't exist
I tried with only your generator + sample to test

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