Skip to content

Commit

Permalink
#7 Support Auto Inject
Browse files Browse the repository at this point in the history
  • Loading branch information
vipwan committed Mar 27, 2024
1 parent cc92518 commit 63a3452
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 15 deletions.
23 changes: 21 additions & 2 deletions Biwen.AutoClassGen.Attributes/AutoInjectAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
{
using System;

#if NET7_0_OR_GREATER

/// <summary>
/// 服务生命周期
/// </summary>
Expand All @@ -14,6 +12,27 @@ public enum ServiceLifetime
Scoped = 4,
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class AutoInjectAttribute : Attribute
{
public ServiceLifetime ServiceLifetime { get; set; }

public Type BaseType { get; set; }

/// <summary>
///
/// </summary>
/// <param name="baseType">NULL表示服务自身</param>
/// <param name="serviceLifetime">服务生命周期</param>
public AutoInjectAttribute(Type baseType = null, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
{
ServiceLifetime = serviceLifetime;
BaseType = baseType;
}
}


#if NET7_0_OR_GREATER

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class AutoInjectAttribute<T> : Attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net7.0</TargetFrameworks>
<PackageVersion>1.3.0.0</PackageVersion>
<PackageVersion>1.3.1.0</PackageVersion>
<PackageProjectUrl>https://github.com/vipwan/Biwen.AutoClassGen</PackageProjectUrl>
<Authors>万雅虎</Authors>
<RepositoryUrl>https://github.com/vipwan/Biwen.AutoClassGen</RepositoryUrl>
Expand All @@ -26,7 +26,7 @@
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'=='Release'">
<PackageReference Include="Biwen.AutoClassGen" Version="1.3.0" PrivateAssets="contentfiles;analyzers" />
<PackageReference Include="Biwen.AutoClassGen" Version="1.3.1" PrivateAssets="contentfiles;analyzers" />
</ItemGroup>


Expand Down
181 changes: 174 additions & 7 deletions Biwen.AutoClassGen.Gen/AutoInjectSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,31 @@ public class AutoInjectSourceGenerator : IIncrementalGenerator
/// </summary>
private const string GenericAutoInjectAttributeName = "Biwen.AutoClassGen.Attributes.AutoInjectAttribute`1";

/// <summary>
/// 非泛型AutoInjectAttribute
/// </summary>
private const string AutoInjectAttributeName = "Biwen.AutoClassGen.Attributes.AutoInjectAttribute";

public void Initialize(IncrementalGeneratorInitializationContext context)
{
#region 非泛型

var nodesAutoInject = context.SyntaxProvider.ForAttributeWithMetadataName(
AutoInjectAttributeName,
(context, attributeSyntax) => true,
(syntaxContext, _) => syntaxContext.TargetNode).Collect();

IncrementalValueProvider<(Compilation, ImmutableArray<SyntaxNode>)> compilationAndTypesInject =
context.CompilationProvider.Combine(nodesAutoInject);

lock (_lock)
{
context.RegisterSourceOutput(compilationAndTypesInject, static (spc, source) => GetAnnotatedNodesInject(source.Item1, source.Item2));
}
#endregion

#region 泛型

var nodesAutoInjectG = context.SyntaxProvider.ForAttributeWithMetadataName(
GenericAutoInjectAttributeName,
(context, attributeSyntax) => true,
Expand All @@ -31,16 +53,23 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
IncrementalValueProvider<(Compilation, ImmutableArray<SyntaxNode>)> compilationAndTypesInjectG =
context.CompilationProvider.Combine(nodesAutoInjectG);

context.RegisterSourceOutput(compilationAndTypesInjectG, static (spc, source) => HandleGenericAnnotatedNodesInject(source.Item1, source.Item2, spc));
lock (_lock)
{
context.RegisterSourceOutput(compilationAndTypesInjectG, static (spc, source) => GetGenericAnnotatedNodesInject(source.Item1, source.Item2));
}
#endregion

lock (_lock)

context.RegisterSourceOutput(compilationAndTypesInjectG, static (spc, source) => GenSource(spc));
}

/// <summary>
/// Gen AutoInjectAttribute G
/// Get AutoInjectAttribute G
/// </summary>
/// <param name="compilation"></param>
/// <param name="nodes"></param>
/// <param name="context"></param>
private static void HandleGenericAnnotatedNodesInject(Compilation compilation, ImmutableArray<SyntaxNode> nodes, SourceProductionContext context)
private static void GetGenericAnnotatedNodesInject(Compilation compilation, ImmutableArray<SyntaxNode> nodes)
{
if (nodes.Length == 0) return;
// 注册的服务
Expand Down Expand Up @@ -130,9 +159,146 @@ private static void HandleGenericAnnotatedNodesInject(Compilation compilation, I
}
}

_injectDefines.AddRange(autoInjects);
_namespaces.AddRange(namespaces);
}

/// <summary>
/// Get AutoInjectAttribute
/// </summary>
/// <param name="compilation"></param>
/// <param name="nodes"></param>
private static void GetAnnotatedNodesInject(Compilation compilation, ImmutableArray<SyntaxNode> nodes)
{
if (nodes.Length == 0) return;
// 注册的服务
List<AutoInjectDefine> autoInjects = [];
List<string> namespaces = [];

foreach (ClassDeclarationSyntax node in nodes.AsEnumerable().Cast<ClassDeclarationSyntax>())
{
AttributeSyntax? attributeSyntax = null;
foreach (var attr in node.AttributeLists.AsEnumerable())
{
var attrName = attr.Attributes.FirstOrDefault()?.Name.ToString();
attributeSyntax = attr.Attributes.First(x => x.Name.ToString().IndexOf(AttributeValueMetadataNameInject, System.StringComparison.Ordinal) == 0);

if (attrName?.IndexOf(AttributeValueMetadataNameInject, System.StringComparison.Ordinal) == 0)
{
var implTypeName = node.Identifier.ValueText;
var rootNamespace = node.AncestorsAndSelf().OfType<NamespaceDeclarationSyntax>().Single().Name.ToString();

var symbols = compilation.GetSymbolsWithName(implTypeName);
foreach (ITypeSymbol symbol in symbols.Cast<ITypeSymbol>())
{
var fullNameSpace = symbol.ContainingNamespace.ToDisplayString();
// 命名空间
if (!namespaces.Contains(fullNameSpace))
{
namespaces.Add(fullNameSpace);
}
}

//转译的Entity类名
var baseTypeName = string.Empty;

if (attributeSyntax.ArgumentList == null || attributeSyntax.ArgumentList!.Arguments.Count == 0)
{
baseTypeName = implTypeName;
}
else
{
if (attributeSyntax.ArgumentList!.Arguments[0].Expression is TypeOfExpressionSyntax)
{
var eType = (attributeSyntax.ArgumentList!.Arguments[0].Expression as TypeOfExpressionSyntax)!.Type;
if (eType.IsKind(SyntaxKind.IdentifierName))
{
baseTypeName = (eType as IdentifierNameSyntax)!.Identifier.ValueText;
}
else if (eType.IsKind(SyntaxKind.QualifiedName))
{
baseTypeName = (eType as QualifiedNameSyntax)!.ToString().Split(['.']).Last();
}
else if (eType.IsKind(SyntaxKind.AliasQualifiedName))
{
baseTypeName = (eType as AliasQualifiedNameSyntax)!.ToString().Split(['.']).Last();
}
if (string.IsNullOrEmpty(baseTypeName))
{
baseTypeName = implTypeName;
}
}
else
{
baseTypeName = implTypeName;
}
}

string lifeTime = "AddScoped"; //default
{
if (attributeSyntax.ArgumentList != null)
{
for (var i = 0; i < attributeSyntax.ArgumentList!.Arguments.Count; i++)
{
var expressionSyntax = attributeSyntax.ArgumentList.Arguments[i].Expression;
if (expressionSyntax.IsKind(SyntaxKind.SimpleMemberAccessExpression))
{
var name = (expressionSyntax as MemberAccessExpressionSyntax)!.Name.Identifier.ValueText;
lifeTime = name switch
{
"Singleton" => "AddSingleton",
"Transient" => "AddTransient",
"Scoped" => "AddScoped",
_ => "AddScoped",
};
break;
}
}
}

autoInjects.Add(new AutoInjectDefine
{
ImplType = implTypeName,
BaseType = baseTypeName,
LifeTime = lifeTime,
});

//命名空间
symbols = compilation.GetSymbolsWithName(baseTypeName);
foreach (ITypeSymbol symbol in symbols.Cast<ITypeSymbol>())
{
var fullNameSpace = symbol.ContainingNamespace.ToDisplayString();
// 命名空间
if (!namespaces.Contains(fullNameSpace))
{
namespaces.Add(fullNameSpace);
}
}
}
}
}
}

_injectDefines.AddRange(autoInjects);
_namespaces.AddRange(namespaces);

}


private static readonly object _lock = new();

/// <summary>
/// 所有的注入定义
/// </summary>
private static List<AutoInjectDefine> _injectDefines = [];
private static List<string> _namespaces = [];


private static void GenSource(SourceProductionContext context)
{
// 生成代码
StringBuilder classes = new();
foreach (var define in autoInjects)
foreach (var define in _injectDefines)
{
if (define.ImplType != define.BaseType)
{
Expand All @@ -145,13 +311,14 @@ private static void HandleGenericAnnotatedNodesInject(Compilation compilation, I
}

string rawNamespace = string.Empty;
namespaces.ForEach(ns => rawNamespace += $"using {ns};\r\n");
_namespaces.Distinct().ToList().ForEach(ns => rawNamespace += $"using {ns};\r\n");

var envSource = Template.Replace("$services", classes.ToString());
envSource = envSource.Replace("$namespaces", rawNamespace);
// format:
envSource = FormatContent(envSource);
context.AddSource($"Biwen.AutoClassGenInjectG.g.cs", SourceText.From(envSource, Encoding.UTF8));
context.AddSource($"Biwen.AutoClassGenInject.g.cs", SourceText.From(envSource, Encoding.UTF8));

}

private class AutoInjectDefine
Expand Down
4 changes: 2 additions & 2 deletions Biwen.AutoClassGen.Gen/Biwen.AutoClassGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<AnalysisLevel>6.0-all</AnalysisLevel>
<Authors>万雅虎</Authors>
<PackageVersion>1.3.0.0</PackageVersion>
<PackageVersion>1.3.1.0</PackageVersion>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -27,7 +27,7 @@
<PackageReleaseNotes>AutoInject</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/vipwan/Biwen.AutoClassGen</PackageProjectUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1573,1591,1712</NoWarn>
<NoWarn>1573,1591,1712,SA1309</NoWarn>
</PropertyGroup>

<!--开发调试阶段-->
Expand Down
7 changes: 5 additions & 2 deletions Biwen.AutoClassGen.TestConsole/Services/TestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ public interface ITest2Service
string Say2(string message);
}


[AutoInject<TestService>]
[AutoInject<ITestService>(ServiceLifetime.Singleton)]
//[AutoInject<ITest2Service>(ServiceLifetime.Scoped)]
[AutoInject<ITest2Service>(ServiceLifetime.Scoped)]
public class TestService : ITestService, ITest2Service
{
public string Say(string message)
Expand All @@ -30,7 +31,9 @@ public string Say2(string message)
}


[AutoInject<ITest2Service>]
[AutoInject]
[AutoInject(serviceLifetime: ServiceLifetime.Transient)]
[AutoInject(typeof(ITest2Service), ServiceLifetime.Scoped)]
public class TestService2 : ITest2Service
{
public string Say2(string message)
Expand Down

0 comments on commit 63a3452

Please sign in to comment.