generate source based on other assembly classes (c# source generator)
Asked Answered
J

1

13

I want to generate a static class that should have a method depending on other classes in specific reference assembly.

a simplified example:

// Generator.csproj
[Generator]
   public class MyGenerator : ISourceGenerator
   {
      public void Initialize(GeneratorInitializationContext context)
      {
          // Register a factory that can create our custom syntax receiver
          context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
      }

      public void Execute(GeneratorExecutionContext context)
      {
          // var syntaxReceiver = (MySyntaxReceiver)context.SyntaxReceiver;
      }
   }

    private class MySyntaxReceiver : ISyntaxReceiver
    {
       ....
    }
// Core.csproj
// namespace Core.Entities
class Entity1 : IAccessControl {}
class Entity2  {}
class Entity3 : IAccessControl {}
// Persistence.csproj => has a reference to Core project and the Generator
// this class should be generated ...
static class GeneratedClass
{
   public static void DoSomethingEntity1()
   public static void DoSomethingEntity3()
}

I want to find the Entity classes in the Core project and generate a class in the Persistence project, The problem is my Core project is not accessible and it is already compiled before Persistence. should I use reflection or manually read the Core Entities ? or is there a better way to access SyntaxTree in the Core project?

Judicature answered 20/6, 2021 at 11:9 Comment(0)
J
23

Because the Core project is already compiled we can not access SyntaxTree but we can go through the compilation to get the referenced assemblies, then look through those assemblies and find the symbols.

public void Execute(GeneratorExecutionContext context)
{
// finding Core reference assembly Symbols
 IAssemblySymbol assemblySymbol = 
context.Compilation.SourceModule.ReferencedAssemblySymbols.First(q => q.Name == "Core");

// use assembly symbol to get namespace and type symbols
// all members in namespace Core.Entities
var members = assemblySymbol.GlobalNamespace.
                             GetNamespaceMembers().First(q => q.Name == "Core")                                       
                            .GetNamespaceMembers().First(q => q.Name == "Entities")
                            .GetTypeMembers().ToList();

var targets = new HashSet<INamedTypeSymbol>();

// find classes that implemented IAccessControl
foreach (var member in members.Where(m => m.AllInterfaces.Any(i => i.Name == "IAccessControl")))
{
   targets.Add(member); // Entity1 Entity3
}


// generate source using targets ...
// context.AddSource("GeneratedClass", source);
}

hope this example help others.

Judicature answered 21/6, 2021 at 8:16 Comment(2)
How about directly geting GlobalNamespace by Compilation.GlobalNamespace ?Iroquoian
@MartinHan I'm not sure might be other ways to do the same thing. at that time this worked for me perfectly, It would be great if you post the other ways you think is better to achieve the same results.Judicature

© 2022 - 2024 — McMap. All rights reserved.