cat on a spaceship
This commit is contained in:
@@ -0,0 +1,852 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Svrnty.CQRS.Grpc.Generators.Helpers;
|
||||
using Svrnty.CQRS.Grpc.Generators.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Svrnty.CQRS.Grpc.Generators
|
||||
{
|
||||
[Generator]
|
||||
public class GrpcGenerator : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// Find all types that might be commands or queries
|
||||
var typeDeclarations = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => node is TypeDeclarationSyntax,
|
||||
transform: static (ctx, _) => GetTypeSymbol(ctx))
|
||||
.Where(static symbol => symbol is not null);
|
||||
|
||||
// Combine with compilation
|
||||
var compilationAndTypes = context.CompilationProvider.Combine(typeDeclarations.Collect());
|
||||
|
||||
// Register source output
|
||||
context.RegisterSourceOutput(compilationAndTypes, static (spc, source) => Execute(source.Left, source.Right!, spc));
|
||||
}
|
||||
|
||||
private static INamedTypeSymbol? GetTypeSymbol(GeneratorSyntaxContext context)
|
||||
{
|
||||
var typeDeclaration = (TypeDeclarationSyntax)context.Node;
|
||||
var symbol = context.SemanticModel.GetDeclaredSymbol(typeDeclaration);
|
||||
return symbol as INamedTypeSymbol;
|
||||
}
|
||||
|
||||
private static void Execute(Compilation compilation, IEnumerable<INamedTypeSymbol?> types, SourceProductionContext context)
|
||||
{
|
||||
var grpcIgnoreAttribute = compilation.GetTypeByMetadataName("Svrnty.CQRS.Grpc.Abstractions.Attributes.GrpcIgnoreAttribute");
|
||||
var commandHandlerInterface = compilation.GetTypeByMetadataName("Svrnty.CQRS.Abstractions.ICommandHandler`1");
|
||||
var commandHandlerWithResultInterface = compilation.GetTypeByMetadataName("Svrnty.CQRS.Abstractions.ICommandHandler`2");
|
||||
var queryHandlerInterface = compilation.GetTypeByMetadataName("Svrnty.CQRS.Abstractions.IQueryHandler`2");
|
||||
|
||||
if (commandHandlerInterface == null || queryHandlerInterface == null)
|
||||
{
|
||||
return; // Handler interfaces not found
|
||||
}
|
||||
|
||||
var commandMap = new Dictionary<INamedTypeSymbol, INamedTypeSymbol?>(SymbolEqualityComparer.Default); // Command -> Result type (null if no result)
|
||||
var queryMap = new Dictionary<INamedTypeSymbol, INamedTypeSymbol>(SymbolEqualityComparer.Default); // Query -> Result type
|
||||
|
||||
// Find all command and query types by looking at handler implementations
|
||||
foreach (var typeSymbol in types)
|
||||
{
|
||||
if (typeSymbol == null || typeSymbol.IsAbstract || typeSymbol.IsStatic)
|
||||
continue;
|
||||
|
||||
// Check if this type implements ICommandHandler<T> or ICommandHandler<T, TResult>
|
||||
foreach (var iface in typeSymbol.AllInterfaces)
|
||||
{
|
||||
if (iface.IsGenericType)
|
||||
{
|
||||
// Check for ICommandHandler<TCommand>
|
||||
if (SymbolEqualityComparer.Default.Equals(iface.OriginalDefinition, commandHandlerInterface) && iface.TypeArguments.Length == 1)
|
||||
{
|
||||
var commandType = iface.TypeArguments[0] as INamedTypeSymbol;
|
||||
if (commandType != null && !commandMap.ContainsKey(commandType))
|
||||
commandMap[commandType] = null; // No result type
|
||||
}
|
||||
// Check for ICommandHandler<TCommand, TResult>
|
||||
else if (SymbolEqualityComparer.Default.Equals(iface.OriginalDefinition, commandHandlerWithResultInterface) && iface.TypeArguments.Length == 2)
|
||||
{
|
||||
var commandType = iface.TypeArguments[0] as INamedTypeSymbol;
|
||||
var resultType = iface.TypeArguments[1] as INamedTypeSymbol;
|
||||
if (commandType != null && resultType != null)
|
||||
commandMap[commandType] = resultType;
|
||||
}
|
||||
// Check for IQueryHandler<TQuery, TResult>
|
||||
else if (SymbolEqualityComparer.Default.Equals(iface.OriginalDefinition, queryHandlerInterface) && iface.TypeArguments.Length == 2)
|
||||
{
|
||||
var queryType = iface.TypeArguments[0] as INamedTypeSymbol;
|
||||
var resultType = iface.TypeArguments[1] as INamedTypeSymbol;
|
||||
if (queryType != null && resultType != null)
|
||||
queryMap[queryType] = resultType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var commands = new List<CommandInfo>();
|
||||
var queries = new List<QueryInfo>();
|
||||
|
||||
// Process discovered command types
|
||||
foreach (var kvp in commandMap)
|
||||
{
|
||||
var commandType = kvp.Key;
|
||||
var resultType = kvp.Value;
|
||||
|
||||
// Skip if marked with [GrpcIgnore]
|
||||
if (grpcIgnoreAttribute != null && HasAttribute(commandType, grpcIgnoreAttribute))
|
||||
continue;
|
||||
|
||||
var commandInfo = ExtractCommandInfo(commandType, resultType);
|
||||
if (commandInfo != null)
|
||||
commands.Add(commandInfo);
|
||||
}
|
||||
|
||||
// Process discovered query types
|
||||
foreach (var kvp in queryMap)
|
||||
{
|
||||
var queryType = kvp.Key;
|
||||
var resultType = kvp.Value;
|
||||
|
||||
// Skip if marked with [GrpcIgnore]
|
||||
if (grpcIgnoreAttribute != null && HasAttribute(queryType, grpcIgnoreAttribute))
|
||||
continue;
|
||||
|
||||
var queryInfo = ExtractQueryInfo(queryType, resultType);
|
||||
if (queryInfo != null)
|
||||
queries.Add(queryInfo);
|
||||
}
|
||||
|
||||
// Generate services if we found any commands or queries
|
||||
if (commands.Any() || queries.Any())
|
||||
{
|
||||
GenerateProtoAndServices(context, commands, queries, compilation);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasAttribute(INamedTypeSymbol typeSymbol, INamedTypeSymbol attributeSymbol)
|
||||
{
|
||||
return typeSymbol.GetAttributes().Any(attr =>
|
||||
SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeSymbol));
|
||||
}
|
||||
|
||||
private static bool ImplementsInterface(INamedTypeSymbol typeSymbol, INamedTypeSymbol? interfaceSymbol)
|
||||
{
|
||||
if (interfaceSymbol == null)
|
||||
return false;
|
||||
|
||||
return typeSymbol.AllInterfaces.Any(i =>
|
||||
SymbolEqualityComparer.Default.Equals(i, interfaceSymbol));
|
||||
}
|
||||
|
||||
private static bool ImplementsGenericInterface(INamedTypeSymbol typeSymbol, INamedTypeSymbol? genericInterfaceSymbol)
|
||||
{
|
||||
if (genericInterfaceSymbol == null)
|
||||
return false;
|
||||
|
||||
return typeSymbol.AllInterfaces.Any(i =>
|
||||
i.IsGenericType && SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, genericInterfaceSymbol));
|
||||
}
|
||||
|
||||
private static CommandInfo? ExtractCommandInfo(INamedTypeSymbol commandType, INamedTypeSymbol? resultType)
|
||||
{
|
||||
var commandInfo = new CommandInfo
|
||||
{
|
||||
Name = commandType.Name,
|
||||
FullyQualifiedName = commandType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
Namespace = commandType.ContainingNamespace.ToDisplayString(),
|
||||
Properties = new List<PropertyInfo>()
|
||||
};
|
||||
|
||||
// Set result type if provided
|
||||
if (resultType != null)
|
||||
{
|
||||
commandInfo.ResultType = resultType.Name;
|
||||
commandInfo.ResultFullyQualifiedName = resultType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
commandInfo.HandlerInterfaceName = $"ICommandHandler<{commandType.Name}, {resultType.Name}>";
|
||||
}
|
||||
else
|
||||
{
|
||||
commandInfo.HandlerInterfaceName = $"ICommandHandler<{commandType.Name}>";
|
||||
}
|
||||
|
||||
// Extract properties
|
||||
var properties = commandType.GetMembers().OfType<IPropertySymbol>()
|
||||
.Where(p => p.DeclaredAccessibility == Accessibility.Public && !p.IsStatic)
|
||||
.ToList();
|
||||
|
||||
int fieldNumber = 1;
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var propertyType = property.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
var protoType = ProtoTypeMapper.MapToProtoType(propertyType, out bool isRepeated, out bool isOptional);
|
||||
|
||||
commandInfo.Properties.Add(new PropertyInfo
|
||||
{
|
||||
Name = property.Name,
|
||||
Type = propertyType,
|
||||
ProtoType = protoType,
|
||||
FieldNumber = fieldNumber++
|
||||
});
|
||||
}
|
||||
|
||||
return commandInfo;
|
||||
}
|
||||
|
||||
private static QueryInfo? ExtractQueryInfo(INamedTypeSymbol queryType, INamedTypeSymbol resultType)
|
||||
{
|
||||
var queryInfo = new QueryInfo
|
||||
{
|
||||
Name = queryType.Name,
|
||||
FullyQualifiedName = queryType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
Namespace = queryType.ContainingNamespace.ToDisplayString(),
|
||||
Properties = new List<PropertyInfo>()
|
||||
};
|
||||
|
||||
// Set result type
|
||||
queryInfo.ResultType = resultType.Name;
|
||||
queryInfo.ResultFullyQualifiedName = resultType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
queryInfo.HandlerInterfaceName = $"IQueryHandler<{queryType.Name}, {resultType.Name}>";
|
||||
|
||||
// Extract properties
|
||||
var properties = queryType.GetMembers().OfType<IPropertySymbol>()
|
||||
.Where(p => p.DeclaredAccessibility == Accessibility.Public && !p.IsStatic)
|
||||
.ToList();
|
||||
|
||||
int fieldNumber = 1;
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var propertyType = property.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
var protoType = ProtoTypeMapper.MapToProtoType(propertyType, out bool isRepeated, out bool isOptional);
|
||||
|
||||
queryInfo.Properties.Add(new PropertyInfo
|
||||
{
|
||||
Name = property.Name,
|
||||
Type = propertyType,
|
||||
ProtoType = protoType,
|
||||
FieldNumber = fieldNumber++
|
||||
});
|
||||
}
|
||||
|
||||
return queryInfo;
|
||||
}
|
||||
|
||||
private static void GenerateProtoAndServices(SourceProductionContext context, List<CommandInfo> commands, List<QueryInfo> queries, Compilation compilation)
|
||||
{
|
||||
// Get root namespace from compilation
|
||||
var rootNamespace = compilation.AssemblyName ?? "Application";
|
||||
|
||||
// Generate service implementations for commands
|
||||
if (commands.Any())
|
||||
{
|
||||
var commandService = GenerateCommandServiceImpl(commands, rootNamespace);
|
||||
context.AddSource("CommandServiceImpl.g.cs", commandService);
|
||||
}
|
||||
|
||||
// Generate service implementations for queries
|
||||
if (queries.Any())
|
||||
{
|
||||
var queryService = GenerateQueryServiceImpl(queries, rootNamespace);
|
||||
context.AddSource("QueryServiceImpl.g.cs", queryService);
|
||||
}
|
||||
|
||||
// Generate registration extensions
|
||||
var registrationExtensions = GenerateRegistrationExtensions(commands.Any(), queries.Any(), rootNamespace);
|
||||
context.AddSource("GrpcServiceRegistration.g.cs", registrationExtensions);
|
||||
}
|
||||
|
||||
private static string GenerateCommandMessages(List<CommandInfo> commands, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("#nullable enable");
|
||||
sb.AppendLine("using System.Runtime.Serialization;");
|
||||
sb.AppendLine("using ProtoBuf;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Messages");
|
||||
sb.AppendLine("{");
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
// Generate command DTO
|
||||
sb.AppendLine(" [ProtoContract]");
|
||||
sb.AppendLine(" [DataContract]");
|
||||
sb.AppendLine($" public sealed class {command.Name}Dto");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
foreach (var prop in command.Properties)
|
||||
{
|
||||
sb.AppendLine($" [ProtoMember({prop.FieldNumber})]");
|
||||
sb.AppendLine(" [DataMember(Order = " + prop.FieldNumber + ")]");
|
||||
sb.AppendLine($" public {prop.Type} {prop.Name} {{ get; set; }}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate result DTO if command has a result
|
||||
if (command.HasResult)
|
||||
{
|
||||
sb.AppendLine(" [ProtoContract]");
|
||||
sb.AppendLine(" [DataContract]");
|
||||
sb.AppendLine($" public sealed class {command.Name}ResultDto");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" [ProtoMember(1)]");
|
||||
sb.AppendLine(" [DataMember(Order = 1)]");
|
||||
sb.AppendLine($" public {command.ResultFullyQualifiedName} Result {{ get; set; }}");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("}");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateQueryMessages(List<QueryInfo> queries, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("#nullable enable");
|
||||
sb.AppendLine("using System.Runtime.Serialization;");
|
||||
sb.AppendLine("using ProtoBuf;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Messages");
|
||||
sb.AppendLine("{");
|
||||
|
||||
foreach (var query in queries)
|
||||
{
|
||||
// Generate query DTO
|
||||
sb.AppendLine(" [ProtoContract]");
|
||||
sb.AppendLine(" [DataContract]");
|
||||
sb.AppendLine($" public sealed class {query.Name}Dto");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
foreach (var prop in query.Properties)
|
||||
{
|
||||
sb.AppendLine($" [ProtoMember({prop.FieldNumber})]");
|
||||
sb.AppendLine(" [DataMember(Order = " + prop.FieldNumber + ")]");
|
||||
sb.AppendLine($" public {prop.Type} {prop.Name} {{ get; set; }}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate result DTO
|
||||
sb.AppendLine(" [ProtoContract]");
|
||||
sb.AppendLine(" [DataContract]");
|
||||
sb.AppendLine($" public sealed class {query.Name}ResultDto");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" [ProtoMember(1)]");
|
||||
sb.AppendLine(" [DataMember(Order = 1)]");
|
||||
sb.AppendLine($" public {query.ResultFullyQualifiedName} Result {{ get; set; }}");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine("}");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateCommandService(List<CommandInfo> commands, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("#nullable enable");
|
||||
sb.AppendLine("using System.ServiceModel;");
|
||||
sb.AppendLine("using System.Threading;");
|
||||
sb.AppendLine("using System.Threading.Tasks;");
|
||||
sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
|
||||
sb.AppendLine($"using {rootNamespace}.Grpc.Messages;");
|
||||
sb.AppendLine("using Svrnty.CQRS.Abstractions;");
|
||||
sb.AppendLine("using ProtoBuf.Grpc;");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate service interface
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Services");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" [ServiceContract]");
|
||||
sb.AppendLine(" public interface ICommandService");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (command.HasResult)
|
||||
{
|
||||
sb.AppendLine($" [OperationContract]");
|
||||
sb.AppendLine($" Task<{command.Name}ResultDto> Execute{command.Name}Async({command.Name}Dto request, CallContext context = default);");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($" [OperationContract]");
|
||||
sb.AppendLine($" Task Execute{command.Name}Async({command.Name}Dto request, CallContext context = default);");
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate service implementation
|
||||
sb.AppendLine(" public sealed class CommandService : ICommandService");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" private readonly IServiceProvider _serviceProvider;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" public CommandService(IServiceProvider serviceProvider)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" _serviceProvider = serviceProvider;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (command.HasResult)
|
||||
{
|
||||
sb.AppendLine($" public async Task<{command.Name}ResultDto> Execute{command.Name}Async({command.Name}Dto request, CallContext context = default)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" var handler = _serviceProvider.GetRequiredService<{command.HandlerInterfaceName}>();");
|
||||
sb.AppendLine($" var command = new {command.FullyQualifiedName}");
|
||||
sb.AppendLine(" {");
|
||||
foreach (var prop in command.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.Name} = request.{prop.Name}!,");
|
||||
}
|
||||
sb.AppendLine(" };");
|
||||
sb.AppendLine(" var result = await handler.HandleAsync(command, context.CancellationToken);");
|
||||
sb.AppendLine($" return new {command.Name}ResultDto {{ Result = result }};");
|
||||
sb.AppendLine(" }");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($" public async Task Execute{command.Name}Async({command.Name}Dto request, CallContext context = default)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" var handler = _serviceProvider.GetRequiredService<{command.HandlerInterfaceName}>();");
|
||||
sb.AppendLine($" var command = new {command.FullyQualifiedName}");
|
||||
sb.AppendLine(" {");
|
||||
foreach (var prop in command.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.Name} = request.{prop.Name}!,");
|
||||
}
|
||||
sb.AppendLine(" };");
|
||||
sb.AppendLine(" await handler.HandleAsync(command, context.CancellationToken);");
|
||||
sb.AppendLine(" }");
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateQueryService(List<QueryInfo> queries, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("#nullable enable");
|
||||
sb.AppendLine("using System.ServiceModel;");
|
||||
sb.AppendLine("using System.Threading;");
|
||||
sb.AppendLine("using System.Threading.Tasks;");
|
||||
sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
|
||||
sb.AppendLine($"using {rootNamespace}.Grpc.Messages;");
|
||||
sb.AppendLine("using Svrnty.CQRS.Abstractions;");
|
||||
sb.AppendLine("using ProtoBuf.Grpc;");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate service interface
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Services");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" [ServiceContract]");
|
||||
sb.AppendLine(" public interface IQueryService");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
foreach (var query in queries)
|
||||
{
|
||||
sb.AppendLine($" [OperationContract]");
|
||||
sb.AppendLine($" Task<{query.Name}ResultDto> Execute{query.Name}Async({query.Name}Dto request, CallContext context = default);");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate service implementation
|
||||
sb.AppendLine(" public sealed class QueryService : IQueryService");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" private readonly IServiceProvider _serviceProvider;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" public QueryService(IServiceProvider serviceProvider)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" _serviceProvider = serviceProvider;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var query in queries)
|
||||
{
|
||||
sb.AppendLine($" public async Task<{query.Name}ResultDto> Execute{query.Name}Async({query.Name}Dto request, CallContext context = default)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" var handler = _serviceProvider.GetRequiredService<{query.HandlerInterfaceName}>();");
|
||||
sb.AppendLine($" var query = new {query.FullyQualifiedName}");
|
||||
sb.AppendLine(" {");
|
||||
foreach (var prop in query.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.Name} = request.{prop.Name}!,");
|
||||
}
|
||||
sb.AppendLine(" };");
|
||||
sb.AppendLine(" var result = await handler.HandleAsync(query, context.CancellationToken);");
|
||||
sb.AppendLine($" return new {query.Name}ResultDto {{ Result = result }};");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateRegistrationExtensions(bool hasCommands, bool hasQueries, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("using Microsoft.AspNetCore.Builder;");
|
||||
sb.AppendLine("using Microsoft.AspNetCore.Routing;");
|
||||
sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
|
||||
sb.AppendLine($"using {rootNamespace}.Grpc.Services;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Extensions");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Auto-generated extension methods for registering and mapping gRPC services");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static class GrpcServiceRegistrationExtensions");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
if (hasCommands)
|
||||
{
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Registers the auto-generated Command gRPC service");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static IServiceCollection AddGrpcCommandService(this IServiceCollection services)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" services.AddGrpc();");
|
||||
sb.AppendLine(" services.AddSingleton<CommandServiceImpl>();");
|
||||
sb.AppendLine(" return services;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Maps the auto-generated Command gRPC service endpoints");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static IEndpointRouteBuilder MapGrpcCommands(this IEndpointRouteBuilder endpoints)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" endpoints.MapGrpcService<CommandServiceImpl>();");
|
||||
sb.AppendLine(" return endpoints;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (hasQueries)
|
||||
{
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Registers the auto-generated Query gRPC service");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static IServiceCollection AddGrpcQueryService(this IServiceCollection services)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" services.AddGrpc();");
|
||||
sb.AppendLine(" services.AddSingleton<QueryServiceImpl>();");
|
||||
sb.AppendLine(" return services;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Maps the auto-generated Query gRPC service endpoints");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static IEndpointRouteBuilder MapGrpcQueries(this IEndpointRouteBuilder endpoints)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" endpoints.MapGrpcService<QueryServiceImpl>();");
|
||||
sb.AppendLine(" return endpoints;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (hasCommands && hasQueries)
|
||||
{
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Registers both Command and Query gRPC services");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static IServiceCollection AddGrpcCommandsAndQueries(this IServiceCollection services)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" services.AddGrpc();");
|
||||
sb.AppendLine(" services.AddGrpcReflection();");
|
||||
if (hasCommands)
|
||||
sb.AppendLine(" services.AddSingleton<CommandServiceImpl>();");
|
||||
if (hasQueries)
|
||||
sb.AppendLine(" services.AddSingleton<QueryServiceImpl>();");
|
||||
sb.AppendLine(" return services;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Maps both Command and Query gRPC service endpoints");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public static IEndpointRouteBuilder MapGrpcCommandsAndQueries(this IEndpointRouteBuilder endpoints)");
|
||||
sb.AppendLine(" {");
|
||||
if (hasCommands)
|
||||
sb.AppendLine(" endpoints.MapGrpcService<CommandServiceImpl>();");
|
||||
if (hasQueries)
|
||||
sb.AppendLine(" endpoints.MapGrpcService<QueryServiceImpl>();");
|
||||
sb.AppendLine(" return endpoints;");
|
||||
sb.AppendLine(" }");
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string ToCamelCase(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str) || char.IsLower(str[0]))
|
||||
return str;
|
||||
|
||||
return char.ToLowerInvariant(str[0]) + str.Substring(1);
|
||||
}
|
||||
|
||||
// New methods for standard gRPC generation
|
||||
|
||||
private static string GenerateCommandsProto(List<CommandInfo> commands, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("syntax = \"proto3\";");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"option csharp_namespace = \"{rootNamespace}.Grpc\";");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("package cqrs;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("// Command service for CQRS operations");
|
||||
sb.AppendLine("service CommandService {");
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
var methodName = command.Name.Replace("Command", "");
|
||||
sb.AppendLine($" // {command.Name}");
|
||||
sb.AppendLine($" rpc {methodName} ({command.Name}Request) returns ({command.Name}Response);");
|
||||
}
|
||||
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate message types
|
||||
foreach (var command in commands)
|
||||
{
|
||||
// Request message
|
||||
sb.AppendLine($"message {command.Name}Request {{");
|
||||
foreach (var prop in command.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.ProtoType} {ToCamelCase(prop.Name)} = {prop.FieldNumber};");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
// Response message
|
||||
sb.AppendLine($"message {command.Name}Response {{");
|
||||
if (command.HasResult)
|
||||
{
|
||||
sb.AppendLine($" {ProtoTypeMapper.MapToProtoType(command.ResultFullyQualifiedName!, out _, out _)} result = 1;");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateQueriesProto(List<QueryInfo> queries, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("syntax = \"proto3\";");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"option csharp_namespace = \"{rootNamespace}.Grpc\";");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("package cqrs;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("// Query service for CQRS operations");
|
||||
sb.AppendLine("service QueryService {");
|
||||
|
||||
foreach (var query in queries)
|
||||
{
|
||||
var methodName = query.Name.Replace("Query", "");
|
||||
sb.AppendLine($" // {query.Name}");
|
||||
sb.AppendLine($" rpc {methodName} ({query.Name}Request) returns ({query.Name}Response);");
|
||||
}
|
||||
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
// Generate message types
|
||||
foreach (var query in queries)
|
||||
{
|
||||
// Request message
|
||||
sb.AppendLine($"message {query.Name}Request {{");
|
||||
foreach (var prop in query.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.ProtoType} {ToCamelCase(prop.Name)} = {prop.FieldNumber};");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
// Response message
|
||||
sb.AppendLine($"message {query.Name}Response {{");
|
||||
sb.AppendLine($" {ProtoTypeMapper.MapToProtoType(query.ResultFullyQualifiedName, out _, out _)} result = 1;");
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateCommandServiceImpl(List<CommandInfo> commands, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("#nullable enable");
|
||||
sb.AppendLine("using Grpc.Core;");
|
||||
sb.AppendLine("using System.Threading.Tasks;");
|
||||
sb.AppendLine("using System.Linq;");
|
||||
sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
|
||||
sb.AppendLine("using FluentValidation;");
|
||||
sb.AppendLine($"using {rootNamespace}.Grpc;");
|
||||
sb.AppendLine("using Svrnty.CQRS.Abstractions;");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Services");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Auto-generated gRPC service implementation for Commands");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public sealed class CommandServiceImpl : CommandService.CommandServiceBase");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" private readonly IServiceProvider _serviceProvider;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" public CommandServiceImpl(IServiceProvider serviceProvider)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" _serviceProvider = serviceProvider;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
var methodName = command.Name.Replace("Command", "");
|
||||
var requestType = $"{command.Name}Request";
|
||||
var responseType = $"{command.Name}Response";
|
||||
|
||||
sb.AppendLine($" public override async Task<{responseType}> {methodName}(");
|
||||
sb.AppendLine($" {requestType} request,");
|
||||
sb.AppendLine(" ServerCallContext context)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" var command = new {command.FullyQualifiedName}");
|
||||
sb.AppendLine(" {");
|
||||
foreach (var prop in command.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.Name} = request.{char.ToUpper(prop.Name[0]) + prop.Name.Substring(1)},");
|
||||
}
|
||||
sb.AppendLine(" };");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" // Validate command if validator is registered");
|
||||
sb.AppendLine($" var validator = _serviceProvider.GetService<IValidator<{command.FullyQualifiedName}>>();");
|
||||
sb.AppendLine(" if (validator != null)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" var validationResult = await validator.ValidateAsync(command, context.CancellationToken);");
|
||||
sb.AppendLine(" if (!validationResult.IsValid)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" var errors = string.Join(\", \", validationResult.Errors.Select(e => e.ErrorMessage));");
|
||||
sb.AppendLine(" throw new RpcException(new Status(StatusCode.InvalidArgument, $\"Validation failed: {errors}\"));");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($" var handler = _serviceProvider.GetRequiredService<{command.HandlerInterfaceName}>();");
|
||||
|
||||
if (command.HasResult)
|
||||
{
|
||||
sb.AppendLine(" var result = await handler.HandleAsync(command, context.CancellationToken);");
|
||||
sb.AppendLine($" return new {responseType} {{ Result = result }};");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(" await handler.HandleAsync(command, context.CancellationToken);");
|
||||
sb.AppendLine($" return new {responseType}();");
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateQueryServiceImpl(List<QueryInfo> queries, string rootNamespace)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("#nullable enable");
|
||||
sb.AppendLine("using Grpc.Core;");
|
||||
sb.AppendLine("using System.Threading.Tasks;");
|
||||
sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
|
||||
sb.AppendLine($"using {rootNamespace}.Grpc;");
|
||||
sb.AppendLine("using Svrnty.CQRS.Abstractions;");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine($"namespace {rootNamespace}.Grpc.Services");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// Auto-generated gRPC service implementation for Queries");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" public sealed class QueryServiceImpl : QueryService.QueryServiceBase");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" private readonly IServiceProvider _serviceProvider;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" public QueryServiceImpl(IServiceProvider serviceProvider)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" _serviceProvider = serviceProvider;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var query in queries)
|
||||
{
|
||||
var methodName = query.Name.Replace("Query", "");
|
||||
var requestType = $"{query.Name}Request";
|
||||
var responseType = $"{query.Name}Response";
|
||||
|
||||
sb.AppendLine($" public override async Task<{responseType}> {methodName}(");
|
||||
sb.AppendLine($" {requestType} request,");
|
||||
sb.AppendLine(" ServerCallContext context)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" var handler = _serviceProvider.GetRequiredService<{query.HandlerInterfaceName}>();");
|
||||
sb.AppendLine($" var query = new {query.FullyQualifiedName}");
|
||||
sb.AppendLine(" {");
|
||||
foreach (var prop in query.Properties)
|
||||
{
|
||||
sb.AppendLine($" {prop.Name} = request.{char.ToUpper(prop.Name[0]) + prop.Name.Substring(1)},");
|
||||
}
|
||||
sb.AppendLine(" };");
|
||||
sb.AppendLine(" var result = await handler.HandleAsync(query, context.CancellationToken);");
|
||||
sb.AppendLine($" return new {responseType} {{ Result = result }};");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Svrnty.CQRS.Grpc.Generators.Helpers
|
||||
{
|
||||
internal static class ProtoTypeMapper
|
||||
{
|
||||
private static readonly Dictionary<string, string> TypeMap = new Dictionary<string, string>
|
||||
{
|
||||
// Primitives
|
||||
{ "System.String", "string" },
|
||||
{ "System.Boolean", "bool" },
|
||||
{ "System.Int32", "int32" },
|
||||
{ "System.Int64", "int64" },
|
||||
{ "System.UInt32", "uint32" },
|
||||
{ "System.UInt64", "uint64" },
|
||||
{ "System.Single", "float" },
|
||||
{ "System.Double", "double" },
|
||||
{ "System.Byte", "uint32" },
|
||||
{ "System.SByte", "int32" },
|
||||
{ "System.Int16", "int32" },
|
||||
{ "System.UInt16", "uint32" },
|
||||
{ "System.Decimal", "string" }, // Decimal as string to preserve precision
|
||||
{ "System.DateTime", "int64" }, // Unix timestamp
|
||||
{ "System.DateTimeOffset", "int64" }, // Unix timestamp
|
||||
{ "System.Guid", "string" },
|
||||
{ "System.TimeSpan", "int64" }, // Ticks
|
||||
|
||||
// Nullable variants
|
||||
{ "System.Boolean?", "bool" },
|
||||
{ "System.Int32?", "int32" },
|
||||
{ "System.Int64?", "int64" },
|
||||
{ "System.UInt32?", "uint32" },
|
||||
{ "System.UInt64?", "uint64" },
|
||||
{ "System.Single?", "float" },
|
||||
{ "System.Double?", "double" },
|
||||
{ "System.Byte?", "uint32" },
|
||||
{ "System.SByte?", "int32" },
|
||||
{ "System.Int16?", "int32" },
|
||||
{ "System.UInt16?", "uint32" },
|
||||
{ "System.Decimal?", "string" },
|
||||
{ "System.DateTime?", "int64" },
|
||||
{ "System.DateTimeOffset?", "int64" },
|
||||
{ "System.Guid?", "string" },
|
||||
{ "System.TimeSpan?", "int64" },
|
||||
};
|
||||
|
||||
public static string MapToProtoType(string csharpType, out bool isRepeated, out bool isOptional)
|
||||
{
|
||||
isRepeated = false;
|
||||
isOptional = false;
|
||||
|
||||
// Handle arrays
|
||||
if (csharpType.EndsWith("[]"))
|
||||
{
|
||||
isRepeated = true;
|
||||
var elementType = csharpType.Substring(0, csharpType.Length - 2);
|
||||
return MapToProtoType(elementType, out _, out _);
|
||||
}
|
||||
|
||||
// Handle generic collections
|
||||
if (csharpType.StartsWith("System.Collections.Generic.List<") ||
|
||||
csharpType.StartsWith("System.Collections.Generic.IList<") ||
|
||||
csharpType.StartsWith("System.Collections.Generic.IEnumerable<") ||
|
||||
csharpType.StartsWith("System.Collections.Generic.ICollection<"))
|
||||
{
|
||||
isRepeated = true;
|
||||
var startIndex = csharpType.IndexOf('<') + 1;
|
||||
var endIndex = csharpType.LastIndexOf('>');
|
||||
var elementType = csharpType.Substring(startIndex, endIndex - startIndex);
|
||||
return MapToProtoType(elementType, out _, out _);
|
||||
}
|
||||
|
||||
// Handle nullable value types
|
||||
if (csharpType.EndsWith("?"))
|
||||
{
|
||||
isOptional = true;
|
||||
}
|
||||
|
||||
// Check if it's a known primitive type
|
||||
if (TypeMap.TryGetValue(csharpType, out var protoType))
|
||||
{
|
||||
return protoType;
|
||||
}
|
||||
|
||||
// For unknown types, assume it's a custom message type
|
||||
// Extract just the type name without namespace
|
||||
var lastDot = csharpType.LastIndexOf('.');
|
||||
if (lastDot >= 0)
|
||||
{
|
||||
return csharpType.Substring(lastDot + 1).Replace("?", "");
|
||||
}
|
||||
|
||||
return csharpType.Replace("?", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Svrnty.CQRS.Grpc.Generators.Models
|
||||
{
|
||||
public class CommandInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string FullyQualifiedName { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
public List<PropertyInfo> Properties { get; set; }
|
||||
public string? ResultType { get; set; }
|
||||
public string? ResultFullyQualifiedName { get; set; }
|
||||
public bool HasResult => ResultType != null;
|
||||
public string HandlerInterfaceName { get; set; }
|
||||
|
||||
public CommandInfo()
|
||||
{
|
||||
Name = string.Empty;
|
||||
FullyQualifiedName = string.Empty;
|
||||
Namespace = string.Empty;
|
||||
Properties = new List<PropertyInfo>();
|
||||
HandlerInterfaceName = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public class PropertyInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string ProtoType { get; set; }
|
||||
public int FieldNumber { get; set; }
|
||||
|
||||
public PropertyInfo()
|
||||
{
|
||||
Name = string.Empty;
|
||||
Type = string.Empty;
|
||||
ProtoType = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Svrnty.CQRS.Grpc.Generators.Models
|
||||
{
|
||||
public class QueryInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string FullyQualifiedName { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
public List<PropertyInfo> Properties { get; set; }
|
||||
public string ResultType { get; set; }
|
||||
public string ResultFullyQualifiedName { get; set; }
|
||||
public string HandlerInterfaceName { get; set; }
|
||||
|
||||
public QueryInfo()
|
||||
{
|
||||
Name = string.Empty;
|
||||
FullyQualifiedName = string.Empty;
|
||||
Namespace = string.Empty;
|
||||
Properties = new List<PropertyInfo>();
|
||||
ResultType = string.Empty;
|
||||
ResultFullyQualifiedName = string.Empty;
|
||||
HandlerInterfaceName = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<IsPackable>true</IsPackable>
|
||||
<DevelopmentDependency>true</DevelopmentDependency>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||
<Company>Svrnty</Company>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Description>Source Generator for Svrnty.CQRS.Grpc - generates .proto files and gRPC service implementations from commands and queries</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\icon.png" Pack="true" PackagePath="" />
|
||||
<None Include="..\README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user