Skip to content

Commit

Permalink
Add source generator for repository-pattern code
Browse files Browse the repository at this point in the history
Related to #48
  • Loading branch information
WeihanLi committed Dec 8, 2024
1 parent e01d6ce commit e09c11e
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace WeihanLi.EntityFramework.SourceGenerator;

[Generator]
public class EFRepositorySourceGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var additionalFiles = context.AdditionalTextsProvider
.Where(file => file.Path.EndsWith(".template"))
.Select((file, cancellationToken) => file.GetText(cancellationToken)?.ToString())
.Where(content => !string.IsNullOrEmpty(content));

var dbContextDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsDbContextDeclaration(s),
transform: static (ctx, _) => GetDbContextDeclaration(ctx))
.Where(static m => m is not null);

var combined = dbContextDeclarations.Combine(additionalFiles.Collect());

context.RegisterSourceOutput(combined, (spc, source) =>
{
var (dbContextDeclaration, templates) = source;
var dbContextName = dbContextDeclaration!.Identifier.Text;
var repositoryNamespace = dbContextDeclaration!.Parent!.GetNamespace();
var generatedCode = GenerateRepositoryCode(dbContextName, repositoryNamespace, templates);
spc.AddSource($"{dbContextName}Repository.g.cs", SourceText.From(generatedCode, Encoding.UTF8));

Check failure on line 33 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The name 'Encoding' does not exist in the current context

Check failure on line 33 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The name 'Encoding' does not exist in the current context

Check failure on line 33 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The name 'Encoding' does not exist in the current context

Check failure on line 33 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The name 'Encoding' does not exist in the current context
});
}

private static bool IsDbContextDeclaration(SyntaxNode node)
{
return node is ClassDeclarationSyntax classDeclaration &&
classDeclaration.BaseList?.Types.Any(t => t.ToString().Contains("DbContext")) == true;
}

private static ClassDeclarationSyntax? GetDbContextDeclaration(GeneratorSyntaxContext context)
{
var classDeclaration = (ClassDeclarationSyntax)context.Node;
return classDeclaration;
}

private static string GenerateRepositoryCode(string dbContextName, string repositoryNamespace, ImmutableArray<string> templates)
{
var builder = new StringBuilder();

Check failure on line 51 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 51 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 51 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 51 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)
builder.AppendLine("using WeihanLi.EntityFramework;");
builder.AppendLine($"namespace {repositoryNamespace}");
builder.AppendLine("{");

foreach (var template in templates)
{
var code = template.Replace("{{DbContextName}}", dbContextName);
builder.AppendLine(code);
}

builder.AppendLine("}");
return builder.ToString();
}
}

internal static class SyntaxNodeExtensions
{
public static string GetNamespace(this SyntaxNode node)
{
while (node != null)
{
if (node is NamespaceDeclarationSyntax namespaceDeclaration)
{
return namespaceDeclaration.Name.ToString();
}

node = node.Parent;

Check warning on line 78 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 78 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 78 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 78 in src/WeihanLi.EntityFramework.SourceGenerator/EFRepositorySourceGenerator.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
}

return string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<IsPackable>true</IsPackable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>WeihanLi.EntityFramework.SourceGenerator</PackageId>
<Title>WeihanLi.EntityFramework.SourceGenerator</Title>
<Description>Source generator for WeihanLi.EntityFramework</Description>
<Authors>WeihanLi</Authors>
<Company>WeihanLi</Company>
<Product>WeihanLi.EntityFramework</Product>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<RepositoryUrl>https://github.com/WeihanLi/WeihanLi.EntityFramework</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Copyright>Copyright 2019-2024 (c) WeihanLi</Copyright>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>

</Project>
5 changes: 4 additions & 1 deletion src/WeihanLi.EntityFramework/WeihanLi.EntityFramework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(EFVersion)" />
<PackageReference Include="WeihanLi.Common" Version="$(CommonVersion)" />
</ItemGroup>
</Project>
<ItemGroup>
<ProjectReference Include="..\WeihanLi.EntityFramework.SourceGenerator\WeihanLi.EntityFramework.SourceGenerator.csproj" />
</ItemGroup>
</Project>

0 comments on commit e09c11e

Please sign in to comment.