├── .dockerignore ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── Directory.Build.props ├── Gnarly.sln ├── Gnarly.sln.DotSettings.user ├── LICENSE ├── README.md ├── docker-compose.yml └── src ├── Gnarly.Application ├── Gnarly.Application.csproj ├── ITestService.cs └── TestService.cs ├── Gnarly.Data ├── Gnarly.Data.csproj ├── IScopeDependency.cs ├── ISingletonDependency.cs ├── ITransientDependency.cs └── RegistrationAttribute.cs ├── Gnarly.Test ├── Gnarly.Test.csproj ├── ITest2Service.cs └── Test2Service.cs ├── Gnarly.WebApi ├── Dockerfile ├── Gnarly.WebApi.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── appsettings.Development.json └── appsettings.json └── Gnarly ├── Gnarly.csproj ├── ScanService.cs └── ServiceRegistrationGenerator.cs /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.project 6 | **/.settings 7 | **/.toolstarget 8 | **/.vs 9 | **/.vscode 10 | **/.idea 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - 'Directory.Build.props' 8 | 9 | jobs: 10 | deploy-nuget: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Build 15 | run: dotnet build --configuration Release 16 | - name: Pack 17 | run: dotnet pack --no-build --configuration Release 18 | - name: Push NuGet package 19 | run: | 20 | dotnet nuget push **/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.Gnarly }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ 6 | /.vs 7 | /.idea 8 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.1.0 5 | AIDotNet 6 | Token 7 | 自动依赖注入,利用源生成器实现自动依赖注入,并且不使用反射进行实现功能。 8 | https://github.com/AIDotNet/Gnarly 9 | MIT 10 | https://github.com/AIDotNet/Gnarly 11 | git 12 | 9.0.0 13 | 8.0.0 14 | 7.0.0 15 | 6.0.0 16 | 17 | true 18 | MIT 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Gnarly.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35312.102 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EAEBB030-5D94-4D76-98DC-C947ECE0F269}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gnarly", "src\Gnarly\Gnarly.csproj", "{ACE2309E-9DA5-41B3-9ADE-784D6B5F91F1}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gnarly.WebApi", "src\Gnarly.WebApi\Gnarly.WebApi.csproj", "{28EE6E90-E647-4EAA-98EB-6599EBC5CE69}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gnarly.Data", "src\Gnarly.Data\Gnarly.Data.csproj", "{30E211CA-1BED-425B-8035-BEBBD47F4EE7}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7F9F8F9C-8B0A-4DED-9E79-7B97C829F6B2}" 15 | ProjectSection(SolutionItems) = preProject 16 | Directory.Build.props = Directory.Build.props 17 | docker-compose.yml = docker-compose.yml 18 | EndProjectSection 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gnarly.Application", "src\Gnarly.Application\Gnarly.Application.csproj", "{B241F5DD-1A66-4E36-9B98-C7D4A5D5A1A8}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gnarly.Test", "src\Gnarly.Test\Gnarly.Test.csproj", "{6D10270A-6309-430B-8902-582DBA9BF8BF}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {ACE2309E-9DA5-41B3-9ADE-784D6B5F91F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {ACE2309E-9DA5-41B3-9ADE-784D6B5F91F1}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {ACE2309E-9DA5-41B3-9ADE-784D6B5F91F1}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {ACE2309E-9DA5-41B3-9ADE-784D6B5F91F1}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {28EE6E90-E647-4EAA-98EB-6599EBC5CE69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {28EE6E90-E647-4EAA-98EB-6599EBC5CE69}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {28EE6E90-E647-4EAA-98EB-6599EBC5CE69}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {28EE6E90-E647-4EAA-98EB-6599EBC5CE69}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {30E211CA-1BED-425B-8035-BEBBD47F4EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {30E211CA-1BED-425B-8035-BEBBD47F4EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {30E211CA-1BED-425B-8035-BEBBD47F4EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {30E211CA-1BED-425B-8035-BEBBD47F4EE7}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {B241F5DD-1A66-4E36-9B98-C7D4A5D5A1A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {B241F5DD-1A66-4E36-9B98-C7D4A5D5A1A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {B241F5DD-1A66-4E36-9B98-C7D4A5D5A1A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {B241F5DD-1A66-4E36-9B98-C7D4A5D5A1A8}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {6D10270A-6309-430B-8902-582DBA9BF8BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {6D10270A-6309-430B-8902-582DBA9BF8BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {6D10270A-6309-430B-8902-582DBA9BF8BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {6D10270A-6309-430B-8902-582DBA9BF8BF}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(NestedProjects) = preSolution 55 | {ACE2309E-9DA5-41B3-9ADE-784D6B5F91F1} = {EAEBB030-5D94-4D76-98DC-C947ECE0F269} 56 | {28EE6E90-E647-4EAA-98EB-6599EBC5CE69} = {EAEBB030-5D94-4D76-98DC-C947ECE0F269} 57 | {30E211CA-1BED-425B-8035-BEBBD47F4EE7} = {EAEBB030-5D94-4D76-98DC-C947ECE0F269} 58 | {B241F5DD-1A66-4E36-9B98-C7D4A5D5A1A8} = {EAEBB030-5D94-4D76-98DC-C947ECE0F269} 59 | {6D10270A-6309-430B-8902-582DBA9BF8BF} = {EAEBB030-5D94-4D76-98DC-C947ECE0F269} 60 | EndGlobalSection 61 | GlobalSection(ExtensibilityGlobals) = postSolution 62 | SolutionGuid = {271DCA1A-7B6B-431E-A491-8C1A7B8971D9} 63 | EndGlobalSection 64 | EndGlobal 65 | -------------------------------------------------------------------------------- /Gnarly.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | ForceIncluded -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 AIDotNet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gnarly 2 | 3 | 基于源生成器实现的自动依赖注入,只需要继承接口即可实现自动依赖注入,并且不需要反射,自动生成代码,只需要先引用项目,然后添加以下代码 4 | 5 | ## 简单入门 6 | 7 | 安装NuGet包在我们的Api层 8 | 9 | 安装NuGet包 10 | 11 | ```shell 12 | dotnet add package Gnarly 13 | ``` 14 | 15 | 然后我们只需要在需要注入的项目中在安装NuGet包 16 | 17 | ```shell 18 | dotnet add package Gnarly.Data 19 | ``` 20 | 21 | 然后根据需要注册的生命周期继承不同的接口,比如我们需要注册一个单例,我们只需要继承ISingletonDependency接口 22 | 23 | ```csharp 24 | public class FileService: ISingletonDependency 25 | { 26 | } 27 | ``` 28 | 29 | 然后我们打开`Program.cs`文件,添加以下代码 30 | 31 | ```csharp 32 | var builder = WebApplication.CreateBuilder(args); 33 | builder.Services.AddEndpointsApiExplorer(); 34 | builder.Services.AddSwaggerGen(); 35 | 36 | builder.Services.AddAutoGnarly(); // 添加自动依赖注入,那么SG就会扫描所有依赖的项目然后添加道`AddAutoGnarly`方法中,然后我们就可以直接使用依赖注入了 37 | 38 | var app = builder.Build(); 39 | // Configure the HTTP request pipeline. 40 | if (app.Environment.IsDevelopment()) 41 | { 42 | app.UseSwagger(); 43 | app.UseSwaggerUI(); 44 | } 45 | app.MapGet("/", async (ITestService service) => 46 | { 47 | await service.SendMessageAsync(); 48 | return Results.Ok("Hello, Gnarly!"); 49 | }); 50 | app.Run(); 51 | 52 | ``` 53 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | gnarly.webapi: 3 | image: gnarly.webapi 4 | build: 5 | context: . 6 | dockerfile: src/Gnarly.WebApi/Dockerfile 7 | -------------------------------------------------------------------------------- /src/Gnarly.Application/Gnarly.Application.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | false 6 | Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Gnarly.Application/ITestService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Test 4 | { 5 | public interface ITestService 6 | { 7 | Task SendMessageAsync(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Gnarly.Application/TestService.cs: -------------------------------------------------------------------------------- 1 | using Gnarly.Data; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace Test 6 | { 7 | public class TestService : ITestService, ISingletonDependency 8 | { 9 | public async Task SendMessageAsync() 10 | { 11 | Console.WriteLine("Hello, Gnarly!"); 12 | 13 | await Task.CompletedTask; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Gnarly.Data/Gnarly.Data.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | true 6 | latest 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Gnarly.Data/IScopeDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Gnarly.Data 2 | { 3 | /// 4 | /// 注册为作用域服务 5 | /// 6 | public interface IScopeDependency 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/Gnarly.Data/ISingletonDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Gnarly.Data 2 | { 3 | /// 4 | /// 注册为单例服务 5 | /// 6 | public interface ISingletonDependency 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Gnarly.Data/ITransientDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Gnarly.Data 2 | { 3 | /// 4 | /// 注册为瞬态服务 5 | /// 6 | public interface ITransientDependency 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Gnarly.Data/RegistrationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Gnarly.Data 4 | { 5 | /// 6 | /// 指定注册接口 7 | /// 8 | [AttributeUsage(AttributeTargets.Class)] 9 | public class RegistrationAttribute : Attribute 10 | { 11 | /// 12 | /// 注册类型 13 | /// 14 | public Type Type { get; set; } 15 | 16 | /// 17 | /// 构造函数 18 | /// 19 | /// 20 | public RegistrationAttribute(Type type) 21 | { 22 | Type = type; 23 | } 24 | 25 | public RegistrationAttribute() 26 | { 27 | } 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/Gnarly.Test/Gnarly.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Gnarly.Test/ITest2Service.cs: -------------------------------------------------------------------------------- 1 | namespace Gnarly.Test; 2 | 3 | public interface ITest2Service 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/Gnarly.Test/Test2Service.cs: -------------------------------------------------------------------------------- 1 | using Gnarly.Data; 2 | 3 | namespace Gnarly.Test; 4 | 5 | public class Test2Service : ITest2Service, IScopeDependency 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/Gnarly.WebApi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base 2 | USER $APP_UID 3 | WORKDIR /app 4 | EXPOSE 8080 5 | EXPOSE 8081 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 8 | ARG BUILD_CONFIGURATION=Release 9 | WORKDIR /src 10 | COPY ["src/Gnarly.WebApi/Gnarly.WebApi.csproj", "src/Gnarly.WebApi/"] 11 | COPY ["src/Gnarly.Data/Gnarly.Data.csproj", "src/Gnarly.Data/"] 12 | COPY ["src/Gnarly.Application/Gnarly.Application.csproj", "src/Gnarly.Application/"] 13 | COPY ["src/Gnarly/Gnarly.csproj", "src/Gnarly/"] 14 | RUN dotnet restore "src/Gnarly.WebApi/Gnarly.WebApi.csproj" 15 | COPY . . 16 | WORKDIR "/src/src/Gnarly.WebApi" 17 | RUN dotnet build "Gnarly.WebApi.csproj" -c $BUILD_CONFIGURATION -o /app/build 18 | 19 | FROM build AS publish 20 | ARG BUILD_CONFIGURATION=Release 21 | RUN dotnet publish "Gnarly.WebApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false 22 | 23 | FROM base AS final 24 | WORKDIR /app 25 | COPY --from=publish /app/publish . 26 | ENTRYPOINT ["dotnet", "Gnarly.WebApi.dll"] 27 | -------------------------------------------------------------------------------- /src/Gnarly.WebApi/Gnarly.WebApi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Linux 8 | true 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | Analyzer 23 | 24 | 25 | 26 | 27 | 28 | .dockerignore 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Gnarly.WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Test; 2 | 3 | internal class Program 4 | { 5 | private static void Main(string[] args) 6 | { 7 | var builder = WebApplication.CreateBuilder(args); 8 | 9 | builder.Services.AddEndpointsApiExplorer(); 10 | builder.Services.AddSwaggerGen(); 11 | 12 | var app = builder.Build(); 13 | 14 | // Configure the HTTP request pipeline. 15 | if (app.Environment.IsDevelopment()) 16 | { 17 | app.UseSwagger(); 18 | app.UseSwaggerUI(); 19 | } 20 | 21 | app.MapGet("/", async (ITestService service) => 22 | { 23 | await service.SendMessageAsync(); 24 | return Results.Ok("Hello, Gnarly!"); 25 | }); 26 | 27 | app.Run(); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/Gnarly.WebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:9136", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5048", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Gnarly.WebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Gnarly.WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/Gnarly/Gnarly.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 12 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | true 15 | false 16 | true 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Gnarly/ScanService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Gnarly; 6 | 7 | /// 8 | /// 扫描 9 | /// 10 | public static class ScanService 11 | { 12 | private static INamedTypeSymbol? iScopeDependencySymbol; 13 | private static INamedTypeSymbol? iSingletonDependencySymbol; 14 | private static INamedTypeSymbol? iTransientDependencySymbol; 15 | 16 | public static void ScanAndCollect(Compilation compilationContext, List scopeMethods, 17 | List singletonMethods, List transientMethods) 18 | { 19 | var visitedAssemblies = new HashSet(SymbolEqualityComparer.Default); 20 | 21 | iScopeDependencySymbol ??= compilationContext.GetTypeByMetadataName("Gnarly.Data.IScopeDependency"); 22 | iSingletonDependencySymbol ??= compilationContext.GetTypeByMetadataName("Gnarly.Data.ISingletonDependency"); 23 | iTransientDependencySymbol ??= compilationContext.GetTypeByMetadataName("Gnarly.Data.ITransientDependency"); 24 | 25 | void ScanAssembly(IAssemblySymbol assemblySymbol) 26 | { 27 | if (visitedAssemblies.Contains(assemblySymbol) || assemblySymbol.Name.StartsWith("Microsoft") || 28 | assemblySymbol.Name.StartsWith("System")) 29 | { 30 | return; 31 | } 32 | 33 | visitedAssemblies.Add(assemblySymbol); 34 | 35 | foreach (var referencedAssembly in assemblySymbol.Modules.SelectMany(m => m.ReferencedAssemblySymbols)) 36 | { 37 | ScanAssembly(referencedAssembly); 38 | } 39 | } 40 | 41 | void ScanType(INamedTypeSymbol namedTypeSymbol) 42 | { 43 | var registrationAttribute = namedTypeSymbol.GetAttributes() 44 | .FirstOrDefault(a => a.AttributeClass?.Name == "RegistrationAttribute"); 45 | 46 | int type = 0; 47 | if (namedTypeSymbol.AllInterfaces.Contains(iScopeDependencySymbol)) 48 | { 49 | type = 1; 50 | } 51 | else if (namedTypeSymbol.AllInterfaces.Contains(iSingletonDependencySymbol)) 52 | { 53 | type = 2; 54 | } 55 | else if (namedTypeSymbol.AllInterfaces.Contains(iTransientDependencySymbol)) 56 | { 57 | type = 3; 58 | } 59 | 60 | if ((registrationAttribute?.ConstructorArguments.FirstOrDefault().Value as INamedTypeSymbol) is 61 | { 62 | TypeKind: TypeKind.Interface 63 | } registrationType) 64 | { 65 | switch (type) 66 | { 67 | case 1: 68 | scopeMethods.Add($"{registrationType},{namedTypeSymbol}"); 69 | break; 70 | case 2: 71 | singletonMethods.Add($"{registrationType},{namedTypeSymbol}"); 72 | break; 73 | case 3: 74 | transientMethods.Add($"{registrationType},{namedTypeSymbol}"); 75 | break; 76 | } 77 | } 78 | else if (namedTypeSymbol.AllInterfaces.Any(i => i.Name == $"I{namedTypeSymbol.Name}")) 79 | { 80 | var namedTypeSymbolInterface = 81 | namedTypeSymbol.AllInterfaces.First(i => 82 | i.Name.ToLower() == $"I{namedTypeSymbol.Name}".ToLower()); 83 | 84 | switch (type) 85 | { 86 | case 1: 87 | scopeMethods.Add($"{namedTypeSymbolInterface},{namedTypeSymbol}"); 88 | break; 89 | case 2: 90 | singletonMethods.Add($"{namedTypeSymbolInterface},{namedTypeSymbol}"); 91 | break; 92 | case 3: 93 | transientMethods.Add($"{namedTypeSymbolInterface},{namedTypeSymbol}"); 94 | break; 95 | } 96 | } 97 | else 98 | { 99 | switch (type) 100 | { 101 | case 1: 102 | scopeMethods.Add(namedTypeSymbol.ToString()); 103 | break; 104 | case 2: 105 | singletonMethods.Add(namedTypeSymbol.ToString()); 106 | break; 107 | case 3: 108 | transientMethods.Add(namedTypeSymbol.ToString()); 109 | break; 110 | } 111 | } 112 | 113 | foreach (var nestedType in namedTypeSymbol.GetTypeMembers()) 114 | { 115 | ScanType(nestedType); 116 | } 117 | } 118 | 119 | foreach (var assemblySymbol in compilationContext.SourceModule.ReferencedAssemblySymbols) 120 | { 121 | ScanAssembly(assemblySymbol); 122 | } 123 | 124 | ScanAssembly(compilationContext.Assembly); 125 | 126 | void ScanNamespace(INamespaceSymbol namespaceSymbol) 127 | { 128 | foreach (var typeSymbol in namespaceSymbol.GetTypeMembers()) 129 | { 130 | ScanType(typeSymbol); 131 | } 132 | 133 | foreach (var nestedNamespace in namespaceSymbol.GetNamespaceMembers()) 134 | { 135 | ScanNamespace(nestedNamespace); 136 | } 137 | } 138 | 139 | foreach (var item in visitedAssemblies) 140 | { 141 | ScanNamespace(item.GlobalNamespace); 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /src/Gnarly/ServiceRegistrationGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.Text; 7 | 8 | namespace Gnarly 9 | { 10 | [Generator(LanguageNames.CSharp)] 11 | public class ServiceRegistrationGenerator : IIncrementalGenerator 12 | { 13 | public void Initialize(IncrementalGeneratorInitializationContext context) 14 | { 15 | // 注册一个增量生成器 16 | var provider = context.CompilationProvider.Combine(context.AnalyzerConfigOptionsProvider); 17 | context.RegisterSourceOutput(provider, (spc, source) => Execute(source.Left, spc)); 18 | } 19 | 20 | private void Execute(Compilation compilation, SourceProductionContext context) 21 | { 22 | var scopeMethods = new List(); 23 | var singletonMethods = new List(); 24 | var transientMethods = new List(); 25 | 26 | ScanService.ScanAndCollect(compilation, scopeMethods, singletonMethods, transientMethods); 27 | 28 | var scopeRegistrations = string.Join("\n ", scopeMethods.Select(m => $"services.AddScoped<{m}>();")); 29 | var singletonRegistrations = 30 | string.Join("\n ", singletonMethods.Select(m => $"services.AddSingleton<{m}>();")); 31 | var transientRegistrations = 32 | string.Join("\n ", transientMethods.Select(m => $"services.AddTransient<{m}>();")); 33 | 34 | // 生成源代码 35 | string source = $@"// Gnarly自动生成的源代码,请勿手动修改 36 | using System; 37 | using Microsoft.Extensions.DependencyInjection; 38 | 39 | namespace {compilation.AssemblyName} 40 | {{ 41 | public static class {compilation.AssemblyName?.Replace(".","")}Extensions 42 | {{ 43 | /// 44 | /// SourceGenerator自动注入服务 45 | /// 46 | public static IServiceCollection AddAutoGnarly(this IServiceCollection services) 47 | {{ 48 | {scopeRegistrations} 49 | 50 | {singletonRegistrations} 51 | 52 | {transientRegistrations} 53 | return services; 54 | }} 55 | }} 56 | }} 57 | "; 58 | 59 | // 添加生成的源代码到编译单元 60 | context.AddSource($"{compilation.AssemblyName?.Replace(".", "")}Extensions.g.cs", SourceText.From(source, Encoding.UTF8)); 61 | } 62 | 63 | private string GetDebuggerDisplay() 64 | { 65 | return ToString(); 66 | } 67 | } 68 | } --------------------------------------------------------------------------------