Skip to content

Instantly share code, notes, and snippets.

@ihorbond
Created January 29, 2021 18:48
Show Gist options
  • Save ihorbond/38b0e9537ffd65b7542db7a31348aa41 to your computer and use it in GitHub Desktop.
Save ihorbond/38b0e9537ffd65b7542db7a31348aa41 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace iProQDesignerAPI.CodeGeneration
{
/// <summary>
/// Generates 4 files: I{Entity}ServiceCore, {Entity}ServiceCore, I{Entity}Repository, {Entity}Repository
/// TODO:
/// 1. register created services and repos with DI
/// 2. add new line characters between fields and constructor
/// 3. ability to generate code from db entity directly
/// 4. optional controller generation
/// </summary>
public static class Program
{
private static string CurrentProject { get => Assembly.GetCallingAssembly().GetName().Name; }
public static void Main(string[] args)
{
string entityName = args.Length > 0 ? args[0] : "YourEntityName";
const string comment = @"
/// <summary>
/// This code was autogenerated by iProQDesingerAPI.CodeGeneration project
/// </summary>
";
var dict = new Dictionary<string, CompilationUnitSyntax>
{
{
Path.GetRelativePath(CurrentProject, $@"iProQDesigner.Core\Interfaces\Services\I{entityName}ServiceCore.cs"),
CreateCoreServiceInterface(entityName, namespaceName: "iProQDesigner.Core.Interfaces.Services", comment)
},
{
Path.GetRelativePath(CurrentProject, $@"iProQDesigner.Core\Services\{entityName}ServiceCore.cs"),
CreateCoreService(entityName, namespaceName: "iProQDesigner.Core.Services", comment)
},
{
Path.GetRelativePath(CurrentProject, $@"iProQDesigner.Core\Interfaces\Repositories\I{entityName}Repository.cs"),
CreateRepoInterface(entityName, namespaceName: "iProQDesigner.Core.Interfaces.Repositories", comment)
},
{
Path.GetRelativePath(CurrentProject, $@"iProQDesigner.Infrastructure\Data\Repositories\{entityName}Repository.cs"),
CreateRepoImplementation(entityName, namespaceName: "iProQDesigner.Infrastructure.Data.Repositories", comment)
},
};
var sb = new StringBuilder();
// go thru dictionary keys and write files
dict.Keys.ToList().ForEach(path =>
{
var code = dict[path];
//var namespaceMember = code.Members.FirstOrDefault(m => m.IsKind(SyntaxKind.NamespaceDeclaration));
sb.AppendLine(code.ToFullString());
using var writer = new StreamWriter(File.Create(path));
code.WriteTo(writer);
});
Console.Write(sb.ToString());
Console.Read();
}
static CompilationUnitSyntax CreateCoreServiceInterface(string entityName, string namespaceName, string comment)
{
var @using = new[]
{
UsingDirective(ParseName("iProQDesigner.Core.Models")),
UsingDirective(ParseName("System.Threading.Tasks"))
};
var interfaceDeclaraton = InterfaceDeclaration($"I{entityName}ServiceCore")
.AddModifiers(Token(SyntaxKind.PublicKeyword));
var @namespace = NamespaceDeclaration(IdentifierName(namespaceName))
.AddMembers(interfaceDeclaraton)
.WithLeadingTrivia(ParseLeadingTrivia(comment));
var compilationUnit = CompilationUnit()
.AddUsings(@using)
.AddMembers(@namespace)
.NormalizeWhitespace();
return compilationUnit;
}
static CompilationUnitSyntax CreateCoreService(string entityName, string namespaceName, string comment)
{
var @using = new[]
{
UsingDirective(ParseName("System")),
UsingDirective(ParseName("iProQDesigner.Core.Guards")),
UsingDirective(ParseName("iProQDesigner.Core.Models")),
UsingDirective(ParseName("iProQDesigner.Core.Interfaces.Repositories")),
UsingDirective(ParseName("iProQDesigner.Core.Interfaces.Services"))
};
string repoName = Char.ToLowerInvariant(entityName[0]) + entityName[1..];
var fieldDeclaration = FieldDeclaration(VariableDeclaration(ParseTypeName($"I{entityName}Repository"))
.AddVariables(VariableDeclarator($"_{repoName}Repository")))
.AddModifiers(
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.ReadOnlyKeyword));
var constructorDeclaration = ConstructorDeclaration($"{entityName}ServiceCore")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier($"{repoName}Repository")).WithType(ParseTypeName($"I{entityName}Repository")))
.WithBody(Block(
SingletonList<StatementSyntax>(
ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName($"_{repoName}Repository"),
IdentifierName($"{repoName}Repository"))))));
var classDeclaration = ClassDeclaration($"{entityName}ServiceCore")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddBaseListTypes(SimpleBaseType(IdentifierName($"I{entityName}ServiceCore")))
.AddMembers(fieldDeclaration, constructorDeclaration);
var @namespace = NamespaceDeclaration(IdentifierName(namespaceName))
.AddMembers(classDeclaration)
.WithLeadingTrivia(ParseLeadingTrivia(comment));
var compilationUnit = CompilationUnit()
.AddUsings(@using)
.AddMembers(@namespace)
.NormalizeWhitespace();
return compilationUnit;
}
static CompilationUnitSyntax CreateRepoInterface(string entityName, string namespaceName, string comment)
{
var @using = new[]
{
UsingDirective(ParseName("iProQDesigner.Core.Models")),
UsingDirective(ParseName("System.Threading.Tasks"))
};
var interfaceDeclaraton = InterfaceDeclaration($"I{entityName}Repository")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddBaseListTypes(SimpleBaseType(IdentifierName($"IGenericRepository<{entityName}>")));
var @namespace = NamespaceDeclaration(IdentifierName(namespaceName))
.AddMembers(interfaceDeclaraton)
.WithLeadingTrivia(ParseLeadingTrivia(comment));
var compilationUnit = CompilationUnit()
.AddUsings(@using)
.AddMembers(@namespace)
.NormalizeWhitespace();
return compilationUnit;
}
static CompilationUnitSyntax CreateRepoImplementation(string entityName, string namespaceName, string comment)
{
var fieldsDeclaration = FieldDeclaration(VariableDeclaration(PredefinedType(Token(SyntaxKind.StringKeyword)))
.AddVariables(
VariableDeclarator(Identifier("_tableName"), null, EqualsValueClause(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal($"{entityName}")))),
VariableDeclarator(Identifier("_pkName"), null, EqualsValueClause(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal($"{entityName}Id"))))))
.AddModifiers(
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.ConstKeyword));
var constructorDeclaration = ConstructorDeclaration($"{entityName}Repository")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.WithInitializer(
ConstructorInitializer(SyntaxKind.BaseConstructorInitializer)
.AddArgumentListArguments(
Argument(IdentifierName("_tableName")),
Argument(IdentifierName("_pkName"))))
.WithBody(Block());
var classDeclaration = ClassDeclaration($"{entityName}Repository")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddBaseListTypes(
SimpleBaseType(IdentifierName($"GenericRepository<{entityName}>")),
SimpleBaseType(IdentifierName(Identifier($"I{entityName}Repository"))))
.AddMembers(fieldsDeclaration, constructorDeclaration);
var @using = new[]
{
UsingDirective(ParseName("iProQDesigner.Core.Models")),
UsingDirective(ParseName("iProQDesigner.Core.Interfaces.Repositories")),
UsingDirective(ParseName("System")),
UsingDirective(ParseName("System.Data")),
UsingDirective(ParseName("System.Threading.Tasks")),
UsingDirective(ParseName("Dapper"))
};
var @namespace = NamespaceDeclaration(IdentifierName(namespaceName))
.AddMembers(classDeclaration)
.WithLeadingTrivia(ParseLeadingTrivia(comment));
var compilationUnit = CompilationUnit()
.AddUsings(@using)
.AddMembers(@namespace)
.NormalizeWhitespace();
return compilationUnit;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment