A fluent builder-pattern syntax for orchestration of domain-specific code generators.
๐ Find it on nuget.org
var sourceFileContents = SourceFileBuilder
.Create($"MyDomain.Infrastructure.Queries.MyEntities")
.Using(
"System.Threading",
"System.Threading.Tasks",
"MediatR",
"MyDomain.API.SDK.Clients.QueryResponses")
.AddClass(handlerName, @class => @class
.WithBase($"IRequestHandler<GetMyEntity, GetMyEntityResponse>")
.AddField(
"IMyEntityQueryRepository",
field => field
.WithAccessibility(Access.Private)
.AsReadOnly())
.WithConstructor(ctor => ctor
.WithParameters(parameters => parameters
.AddParameter("IMyEntityQueryRepository", "myEntityQueryRepository")
)
.WithBody(body => body
.AddStatement("_myEntityQueryRepository = myEntityQueryRepository;")
)
)
.WithMethod(
"Handle",
method => method
.WithAccessibility(Access.Public)
.WithParameters(parameters => parameters
.AddParameter("GetMyEntity", "query")
.AddParameter("CancellationToken"))
.WithBody(body => body
.AddStatement("var entity = await _myEntityQueryRepository.GetAsync(query.Id);")
.AddStatement("return new(entity.Identity, entity.Name, entity.Description);")
)
.Returns("GetMyEntityResponse")
.AsAsync()
)
)
.Build();
โ ๏ธ WARNINGExamples last updated at version
0.1.0-alpha
Create a console project in your solution, i.e. <target-project>.CodeGenerator
#!/usr/bin/env bash
# change these
repoDir="${HOME}/src/MyRepo"
targetProject="MyDomain"
# create project in solution
cd "$repoDir/project"
generatorProject = "${targetProject}.CodeGenerator"
dotnet new console --name "$generatorProject"
dotnet sln add "$generatorProject"
example for generating query:
using StarBridge.FluentRoslyn.CSharp.Model;
namespace MyDomain.CodeGenerator;
internal class ApiGenerator
{
private readonly IFileRepository _fileRepository;
public ApiGenerator(IFileRepository fileRepository)
{
_fileRepository = fileRepository;
}
// example method that generates a set of files for a "query" in your api project
internal void GenerateQuery(ProjectInfo project, Entity entity)
{
// response
var response = entity.GenerateQueryResponse(project);
_fileRepository.Save(response);
// query
var query = entity.GenerateQuery(project);
_fileRepository.Save(query);
// query handler
var handler = entity.GenerateQueryHandler(project);
_fileRepository.Save(handler);
}
}
Call it from your entry point
using StarBridge.FluentRoslyn.CSharp.Model;
using MyDomain.CodeGenerator;
var fileRepository = new FileRepository(); // out of scope for example
var apiGenerator = new ApiGenerator(fileRepository);
var projectName = new ProjectName("MyDomain");
var projectDir = "/home/username/src/MyDomain/project/MyDomain.API";
var project = new ProjectInfo(projectName, projectDir);
var entity = new Entity(
new EntityName(
Singular: "MyEntity",
Plural: "TheEntities"));
apiGenerator.GenerateQuery(project, entity);
Example - Write each file as an extension of Entity
:
using FluentRoslyn.CSharp;
using FluentRoslyn.CSharp.Model;
using Microsoft.CodeAnalysis.CSharp;
namespace MyDomain.CodeGenerator;
public static class ApiQueryFactory
{
/// <summary>
/// Generates <b>MyDomain.API.SDK/Clients/QueryTheEntities/GetMyEntityResponse.cs</b>
/// </summary>
internal static GeneratedFile GenerateQueryResponse(this Entity entity, ProjectInfo project)
{
var responsePath = $"{project.SdkName}\\Clients\\QueryResponses\\Get{entity.Name.Singular}Response.cs";
var responseFileInfo = project.GetInfo(responsePath);
var recordName = $"Get{entity.Name.Singular}Response";
var fileName = $"{recordName}.cs";
var responseFileContents = SourceFileBuilder
.Create("MyDomain.API.SDK.Clients.QueryResponses")
.Using("MyDomain.Core.Identity")
.AddRecord(recordName, rec => rec
.WithParameters(param => param
.AddParameter(entity.Name.Identifier, "Identity")
.AddParameter(SyntaxKind.StringKeyword, "Name")
.AddParameter(SyntaxKind.StringKeyword, "Description")))
.Build();
var responseFile = new GeneratedFile(responseFileInfo, responseFileContents);
return responseFile;
}
/// <summary>
/// Generates <b>MyDomain.Infrastructure.Queries/TheEntities/GetMyEntity.cs</b>
/// </summary>
internal static GeneratedFile GenerateQuery(this Entity entity, ProjectInfo project) { /**/ }
/// <summary>
/// Generates <b>MyDomain.Infrastructure.Queries/MyEntities/GetMyEntityHandler.cs</b>
/// </summary>
internal static GeneratedFile GenerateQueryHandler(this Entity entity, ProjectInfo project) { /**/ }
}
The fluent syntax should promote discovery ๐ค Feel free to create an issue if something isn't intuitive!
- ๐ Contributing Guidelines
- ๐งช Tests which will provide examples, and document builder permutations
- ๐จ More coverage of Roslyn (Dogfooding this package for a personal project)
- ๐ช Build Pipeline
- ๐ป Command-line Interface