Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 211
AliReza Sabouri Avatar asked Jun 20 '21 11:06

AliReza Sabouri


Video Answer


1 Answers

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.

like image 143
AliReza Sabouri Avatar answered Oct 17 '22 08:10

AliReza Sabouri