├── .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 | }
--------------------------------------------------------------------------------