|
6 | 6 | using System.Collections.Immutable; |
7 | 7 | using System.Diagnostics; |
8 | 8 | using System.Linq; |
| 9 | +using System.Text; |
9 | 10 | using System.Threading; |
10 | 11 |
|
11 | 12 | namespace ProjectR |
@@ -134,6 +135,103 @@ public partial class {mapperName} : global::ProjectR.Mapper<{entityFullName}, {d |
134 | 135 | context.ReportDiagnostic(Diagnostic.Create(Diagnostics.UnexpectedError, mapperClass.GetLocation(), ex.Message, ex.StackTrace)); |
135 | 136 | } |
136 | 137 | } |
| 138 | + |
| 139 | + GenerateDiRegistration(compilation, allMappers, context); |
| 140 | + } |
| 141 | + |
| 142 | + private static void GenerateDiRegistration(Compilation compilation, List<INamedTypeSymbol> mappers, SourceProductionContext context) |
| 143 | + { |
| 144 | + var sb = new StringBuilder(); |
| 145 | + var assemblyName = compilation.AssemblyName!; |
| 146 | + var safeAssemblyName = assemblyName.Replace(".", "_"); |
| 147 | + var generatedNamespace = $"{assemblyName}.Generated"; |
| 148 | + var extensionsClassName = $"GeneratedMapperRegistrations_{safeAssemblyName}"; |
| 149 | + |
| 150 | + var discoverMappersAttributeSymbol = compilation.GetTypeByMetadataName("ProjectR.Attributes.DiscoverMappersAttribute"); |
| 151 | + bool shouldDiscover = compilation.Assembly.GetAttributes().Any(ad => |
| 152 | + discoverMappersAttributeSymbol != null && |
| 153 | + ad.AttributeClass?.Equals(discoverMappersAttributeSymbol, SymbolEqualityComparer.Default) == true); |
| 154 | + |
| 155 | + sb.AppendLine("// <auto-generated/>"); |
| 156 | + sb.AppendLine("#nullable enable"); |
| 157 | + sb.AppendLine(); |
| 158 | + sb.AppendLine("using Microsoft.Extensions.DependencyInjection;"); |
| 159 | + sb.AppendLine("using Microsoft.Extensions.DependencyInjection.Extensions;"); |
| 160 | + sb.AppendLine("using ProjectR.Services;"); |
| 161 | + sb.AppendLine(); |
| 162 | + |
| 163 | + var namespaces = mappers |
| 164 | + .Select(m => m.ContainingNamespace.ToDisplayString()) |
| 165 | + .Distinct() |
| 166 | + .OrderBy(ns => ns); |
| 167 | + |
| 168 | + foreach (var ns in namespaces) |
| 169 | + { |
| 170 | + sb.AppendLine($"using {ns};"); |
| 171 | + } |
| 172 | + |
| 173 | + sb.AppendLine(); |
| 174 | + sb.AppendLine($"namespace {generatedNamespace};"); |
| 175 | + sb.AppendLine(); |
| 176 | + sb.AppendLine("/// <summary>"); |
| 177 | + sb.AppendLine($"/// Generated mapper registration for {assemblyName}."); |
| 178 | + sb.AppendLine("/// Generated by MapperGenerator."); |
| 179 | + sb.AppendLine("/// </summary>"); |
| 180 | + sb.AppendLine($"public static class {extensionsClassName}"); |
| 181 | + sb.AppendLine("{"); |
| 182 | + sb.AppendLine(" /// <summary>"); |
| 183 | + sb.AppendLine(" /// Registers all generated mappers in the DI container."); |
| 184 | + sb.AppendLine(" /// </summary>"); |
| 185 | + sb.AppendLine(" public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddGeneratedMappers("); |
| 186 | + sb.AppendLine(" this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)"); |
| 187 | + sb.AppendLine(" {"); |
| 188 | + |
| 189 | + // Register individual mappers |
| 190 | + sb.AppendLine(" // Register individual mappers from this assembly"); |
| 191 | + foreach (var mapper in mappers.OrderBy(m => m.Name)) |
| 192 | + { |
| 193 | + var fullyQualifiedName = mapper.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); |
| 194 | + var baseType = mapper.BaseType; |
| 195 | + if (baseType != null && baseType.TypeArguments.Length == 2) |
| 196 | + { |
| 197 | + var sourceType = baseType.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); |
| 198 | + var destinationType = baseType.TypeArguments[1].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); |
| 199 | + |
| 200 | + sb.AppendLine($" services.TryAddSingleton<{fullyQualifiedName}>();"); |
| 201 | + sb.AppendLine($" services.TryAddSingleton<global::ProjectR.Mapper<{sourceType}, {destinationType}>>(sp => sp.GetRequiredService<{fullyQualifiedName}>());"); |
| 202 | + } |
| 203 | + } |
| 204 | + sb.AppendLine(); |
| 205 | + |
| 206 | + if (shouldDiscover) |
| 207 | + { |
| 208 | + sb.AppendLine(" // Discover and register mappers from referenced assemblies"); |
| 209 | + foreach (var referencedAssembly in compilation.SourceModule.ReferencedAssemblySymbols) |
| 210 | + { |
| 211 | + var refSafeName = referencedAssembly.Name.Replace(".", "_"); |
| 212 | + var refNamespace = $"{referencedAssembly.Name}.Generated"; |
| 213 | + var refClassName = $"GeneratedMapperRegistrations_{refSafeName}"; |
| 214 | + |
| 215 | + // Check if the class exists in the referenced assembly |
| 216 | + var refType = compilation.GetTypeByMetadataName($"{refNamespace}.{refClassName}"); |
| 217 | + if (refType != null) |
| 218 | + { |
| 219 | + sb.AppendLine($" global::{refNamespace}.{refClassName}.AddGeneratedMappers(services);"); |
| 220 | + } |
| 221 | + } |
| 222 | + sb.AppendLine(); |
| 223 | + } |
| 224 | + |
| 225 | + // Register global services (resolver) |
| 226 | + sb.AppendLine(" // Register global services (only once)"); |
| 227 | + sb.AppendLine(" services.TryAddSingleton<global::ProjectR.Services.IMapperResolver, global::ProjectR.Services.MapperResolver>();"); |
| 228 | + sb.AppendLine(); |
| 229 | + |
| 230 | + sb.AppendLine(" return services;"); |
| 231 | + sb.AppendLine(" }"); |
| 232 | + sb.AppendLine("}"); |
| 233 | + |
| 234 | + context.AddSource("MapperRegistration.g.cs", sb.ToString()); |
137 | 235 | } |
138 | 236 |
|
139 | 237 | private static void CollectUsings(ITypeSymbol typeSymbol, HashSet<string> usings) |
|
0 commit comments