diff --git a/Biwen.AutoClassGen.Attributes/AutoDtoAttribute.cs b/Biwen.AutoClassGen.Attributes/AutoDtoAttribute.cs
index fae4f49..74346f2 100644
--- a/Biwen.AutoClassGen.Attributes/AutoDtoAttribute.cs
+++ b/Biwen.AutoClassGen.Attributes/AutoDtoAttribute.cs
@@ -21,4 +21,22 @@ public AutoDtoAttribute(Type fromType, params string[] excludeProps)
ExcludeProps = excludeProps;
}
}
+
+#if NET7_0_OR_GREATER
+
+ ///
+ /// 自动创建Dto
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+#pragma warning disable SA1402 // File may only contain a single type
+ public class AutoDtoAttribute : AutoDtoAttribute
+#pragma warning restore SA1402 // File may only contain a single type
+ {
+ public AutoDtoAttribute(params string[] excludeProps) : base(typeof(T), excludeProps)
+ {
+ }
+ }
+
+#endif
+
}
\ No newline at end of file
diff --git a/Biwen.AutoClassGen.Attributes/Biwen.AutoClassGen.Attributes.csproj b/Biwen.AutoClassGen.Attributes/Biwen.AutoClassGen.Attributes.csproj
index e5987e1..971d645 100644
--- a/Biwen.AutoClassGen.Attributes/Biwen.AutoClassGen.Attributes.csproj
+++ b/Biwen.AutoClassGen.Attributes/Biwen.AutoClassGen.Attributes.csproj
@@ -1,7 +1,7 @@
- netstandard2.0
+ netstandard2.0;net7.0
1.1.0.3
https://github.com/vipwan/Biwen.AutoClassGen
万雅虎
@@ -32,7 +32,7 @@
-
+
all
diff --git a/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs b/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs
index 7a66ef6..22c3e2f 100644
--- a/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs
+++ b/Biwen.AutoClassGen.Gen/SourceGenAnalyzer.cs
@@ -1,5 +1,6 @@
namespace Biwen.AutoClassGen
{
+ using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
@@ -18,6 +19,7 @@ public class SourceGenAnalyzer : DiagnosticAnalyzer
public const string GEN011 = "GEN011";
public const string GEN021 = "GEN021";
public const string GEN031 = "GEN031"; // 推荐生成
+ public const string GEN041 = "GEN041"; // 重复标注
///
/// 无法生成类的错误
@@ -53,7 +55,6 @@ public class SourceGenAnalyzer : DiagnosticAnalyzer
helpLinkUri: Helplink,
isEnabledByDefault: true);
-
///
/// 推荐使用自动生成
///
@@ -66,15 +67,35 @@ public class SourceGenAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true);
+ ///
+ /// Dto特性重复标注
+ ///
+ private static readonly DiagnosticDescriptor MutiMarkedAutoDtoError = new(id: GEN041,
+ title: "重复标注[AutoDto]",
+ messageFormat: "重复标注了[AutoDto],请删除多余的标注",
+ category: typeof(SourceGenerator).Assembly.GetName().Name,
+ DiagnosticSeverity.Error,
+ helpLinkUri: Helplink,
+ isEnabledByDefault: true);
+
+
+
+
#endregion
public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(
InvalidDeclareError,
InvalidDeclareNameError,
SuggestDeclareNameWarning,
- SuggestAutoGen);
+ SuggestAutoGen,
+ MutiMarkedAutoDtoError);
private const string AttributeValueMetadataName = "AutoGen";
+ ///
+ /// Dto特性名称,注意存在泛型的情况
+ ///
+ private const string AttributeValueMetadataNameDto = "AutoDto";
+
public override void Initialize(AnalysisContext context)
{
@@ -83,67 +104,92 @@ public override void Initialize(AnalysisContext context)
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(ctx =>
{
- // Find implicitly typed interface declarations.
- var declaration = (InterfaceDeclarationSyntax)ctx.Node;
- if (declaration == null) return;
-
- if (declaration.AttributeLists.Count > 0)
+ var kind = ctx.Node.Kind();
+ // InterfaceDeclarationSyntax
+ if (kind == SyntaxKind.InterfaceDeclaration)
{
- foreach (var attr in declaration.AttributeLists.AsEnumerable())
+ var declaration = (InterfaceDeclarationSyntax)ctx.Node;
+
+ if (declaration.AttributeLists.Count > 0)
{
- if (attr.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName))
+ foreach (var attr in declaration.AttributeLists.AsEnumerable())
{
- if (declaration.BaseList == null || !declaration.BaseList.Types.Any())
+ if (attr.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName))
{
- // issue error
- ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareError, attr.GetLocation()));
- }
-
- var arg0 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName)
- .ArgumentList!.Arguments[0];
-
- var arg1 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName)
- .ArgumentList!.Arguments[1];
-
- if (declaration.Identifier.Text == arg0.GetText().ToString().Replace("\"", ""))
- {
- var location = arg0?.GetLocation();
- // issue error
- ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareNameError, location));
- }
-
- // NamespaceDeclarationSyntax
- if (declaration.Parent is NamespaceDeclarationSyntax @namespace &&
- @namespace?.Name.ToString() != arg1?.GetText().ToString().Replace("\"", ""))
- {
- var location = arg1?.GetLocation();
- // issue warning
- ctx.ReportDiagnostic(Diagnostic.Create(SuggestDeclareNameWarning, location));
- }
- // FileScopedNamespaceDeclaration
- if (declaration.Parent is FileScopedNamespaceDeclarationSyntax @namespace2 &&
- @namespace2?.Name.ToString() != arg1?.GetText().ToString().Replace("\"", ""))
- {
- var location = arg1?.GetLocation();
- // issue warning
- ctx.ReportDiagnostic(Diagnostic.Create(SuggestDeclareNameWarning, location));
+ if (declaration.BaseList == null || !declaration.BaseList.Types.Any())
+ {
+ // issue error
+ ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareError, attr.GetLocation()));
+ }
+
+ var arg0 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName)
+ .ArgumentList!.Arguments[0];
+
+ var arg1 = attr.Attributes.First(x => x.Name.ToString() == AttributeValueMetadataName)
+ .ArgumentList!.Arguments[1];
+
+ if (declaration.Identifier.Text == arg0.GetText().ToString().Replace("\"", ""))
+ {
+ var location = arg0?.GetLocation();
+ // issue error
+ ctx.ReportDiagnostic(Diagnostic.Create(InvalidDeclareNameError, location));
+ }
+
+ // NamespaceDeclarationSyntax
+ if (declaration.Parent is NamespaceDeclarationSyntax @namespace &&
+ @namespace?.Name.ToString() != arg1?.GetText().ToString().Replace("\"", ""))
+ {
+ var location = arg1?.GetLocation();
+ // issue warning
+ ctx.ReportDiagnostic(Diagnostic.Create(SuggestDeclareNameWarning, location));
+ }
+ // FileScopedNamespaceDeclaration
+ if (declaration.Parent is FileScopedNamespaceDeclarationSyntax @namespace2 &&
+ @namespace2?.Name.ToString() != arg1?.GetText().ToString().Replace("\"", ""))
+ {
+ var location = arg1?.GetLocation();
+ // issue warning
+ ctx.ReportDiagnostic(Diagnostic.Create(SuggestDeclareNameWarning, location));
+ }
}
}
}
+ // suggest
+ if (declaration.BaseList != null && declaration.BaseList.Types.Any(x => x.IsKind(SyntaxKind.SimpleBaseType)))
+ {
+ var haveAttr = declaration.AttributeLists.Any(x => x.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName));
+ if (!haveAttr)
+ {
+ var location = declaration.GetLocation();
+ // issue suggest
+ ctx.ReportDiagnostic(Diagnostic.Create(SuggestAutoGen, location));
+ }
+ }
}
-
- // suggest
- if (declaration.BaseList != null && declaration.BaseList.Types.Any(x => x.IsKind(SyntaxKind.SimpleBaseType)))
+ // ClassDeclarationSyntax
+ if (kind == SyntaxKind.ClassDeclaration)
{
- var haveAttr = declaration.AttributeLists.Any(x => x.Attributes.Any(x => x.Name.ToString() == AttributeValueMetadataName));
- if (!haveAttr)
+ var declaration = (ClassDeclarationSyntax)ctx.Node;
+ if (declaration.AttributeLists.Count > 0)
{
- var location = declaration.GetLocation();
- // issue suggest
- ctx.ReportDiagnostic(Diagnostic.Create(SuggestAutoGen, location));
+ foreach (var attr in declaration.AttributeLists.AsEnumerable())
+ {
+ if (attr.Attributes.Where(x => x.Name.ToString().IndexOf(
+ AttributeValueMetadataNameDto, StringComparison.Ordinal) == 0).Count() > 1)
+ {
+ var location = declaration.GetLocation();
+
+ // issue error
+ ctx.ReportDiagnostic(Diagnostic.Create(MutiMarkedAutoDtoError, location));
+ }
+ }
}
}
- }, SyntaxKind.InterfaceDeclaration);
+
+ },
+ SyntaxKind.InterfaceDeclaration,
+ SyntaxKind.ClassDeclaration);
+
}
}
}
\ No newline at end of file
diff --git a/Biwen.AutoClassGen.Gen/SourceGenerator.cs b/Biwen.AutoClassGen.Gen/SourceGenerator.cs
index ddcc731..2c7fe64 100644
--- a/Biwen.AutoClassGen.Gen/SourceGenerator.cs
+++ b/Biwen.AutoClassGen.Gen/SourceGenerator.cs
@@ -8,6 +8,7 @@ namespace Biwen.AutoClassGen
using System.Collections.Immutable;
using System.Linq;
using System.Text;
+ using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -22,6 +23,13 @@ public class SourceGenerator : IIncrementalGenerator
private const string AttributeMetadataNameDto = "Biwen.AutoClassGen.Attributes.AutoDtoAttribute";
private const string AttributeValueMetadataNameDto = "AutoDto";
+
+ ///
+ /// 泛型AutoDtoAttribute
+ ///
+ private const string AttributeMetadataNameDtoG = "Biwen.AutoClassGen.Attributes.AutoDtoAttribute`1";
+
+
public void Initialize(IncrementalGeneratorInitializationContext context)
{
@@ -54,6 +62,22 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
context.RegisterSourceOutput(compilationAndTypesDto, static (spc, source) => HandleAnnotatedNodesDto(source.Item1, source.Item2, spc));
#endregion
+
+
+ #region AutoDtoAttributeG
+
+ var nodesDtoG = context.SyntaxProvider.ForAttributeWithMetadataName(
+ AttributeMetadataNameDtoG,
+ (context, attributeSyntax) => true,
+ (syntaxContext, _) => syntaxContext.TargetNode).Collect();
+
+ IncrementalValueProvider<(Compilation, ImmutableArray)> compilationAndTypesDtoG =
+ context.CompilationProvider.Combine(nodesDtoG);
+
+ context.RegisterSourceOutput(compilationAndTypesDtoG, static (spc, source) => HandleAnnotatedNodesDtoG(source.Item1, source.Item2, spc));
+
+ #endregion
+
}
@@ -65,9 +89,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
///
private static void HandleAnnotatedNodes(Compilation compilation, ImmutableArray nodes, SourceProductionContext context)
{
- StringBuilder envStringBuilder = new();
-
+ if (nodes.Length == 0) return;
+ StringBuilder envStringBuilder = new();
envStringBuilder.AppendLine("// ");
envStringBuilder.AppendLine("// author:vipwan@outlook.com 万雅虎");
@@ -217,6 +241,7 @@ void GenProperty(TypeSyntax @interfaceType)
///
private static void HandleAnnotatedNodesDto(Compilation compilation, ImmutableArray nodes, SourceProductionContext context)
{
+ if (nodes.Length == 0) return;
StringBuilder envStringBuilder = new();
@@ -385,6 +410,179 @@ void GenProperty(TypeSyntax @type)
context.AddSource($"Biwen.AutoClassGenDto.g.cs", SourceText.From(envSource, Encoding.UTF8));
}
+
+ ///
+ /// Gen AutoDtoAttribute G
+ ///
+ ///
+ ///
+ ///
+ private static void HandleAnnotatedNodesDtoG(Compilation compilation, ImmutableArray nodes, SourceProductionContext context)
+ {
+ if(nodes.Length == 0) return;
+
+ StringBuilder envStringBuilder = new();
+
+ envStringBuilder.AppendLine("// ");
+ envStringBuilder.AppendLine("// author:vipwan@outlook.com 万雅虎");
+ envStringBuilder.AppendLine("// issue:https://github.com/vipwan/Biwen.AutoClassGen/issues");
+ envStringBuilder.AppendLine("// 如果你在使用中遇到问题,请第一时间issue,谢谢!");
+ envStringBuilder.AppendLine("// This file is generated by Biwen.AutoClassGen.SourceGenerator");
+ envStringBuilder.AppendLine();
+ envStringBuilder.AppendLine("using System;");
+ envStringBuilder.AppendLine("using System.Collections.Generic;");
+ envStringBuilder.AppendLine("using System.Text;");
+ envStringBuilder.AppendLine("using System.Threading.Tasks;");
+ envStringBuilder.AppendLine("#pragma warning disable");
+
+ foreach (ClassDeclarationSyntax node in nodes.AsEnumerable().Cast())
+ {
+ AttributeSyntax? attributeSyntax = null;
+ foreach (var attr in node.AttributeLists.AsEnumerable())
+ {
+ var attrName = attr.Attributes.FirstOrDefault()?.Name.ToString();
+ if (attrName?.IndexOf(AttributeValueMetadataNameDto, System.StringComparison.Ordinal) == 0)
+ {
+ attributeSyntax = attr.Attributes.First(x => x.Name.ToString().IndexOf(AttributeValueMetadataNameDto, System.StringComparison.Ordinal) == 0);
+ break;
+ }
+ }
+ if (attributeSyntax == null)
+ {
+ continue;
+ }
+
+
+ //转译的Entity类名
+ var entityName = string.Empty;
+
+ string pattern = @"(?<=<)(?\w+)(?=>)";
+ var match = Regex.Match(attributeSyntax.ToString(), pattern);
+ if (match.Success)
+ {
+ entityName = match.Groups["type"].Value.Split(['.']).Last();
+ }
+ else
+ {
+ continue;
+ }
+
+ var sb = new StringBuilder();
+ sb.AppendLine();
+ sb.AppendLine($"//generate {entityName}-{node.Identifier.ValueText}");
+ sb.AppendLine();
+ sb.AppendLine("namespace $ni");
+ sb.AppendLine("{");
+ sb.AppendLine("$namespace");
+ sb.AppendLine("$classes");
+ sb.AppendLine("}");
+ // sb.AppendLine("#pragma warning restore");
+ string classTemp = $"partial class $className {{ $body }}";
+
+ {
+ // 排除的属性
+ List excapes = [];
+ for (var i = 0; i < attributeSyntax.ArgumentList!.Arguments.Count; i++)
+ {
+ var expressionSyntax = attributeSyntax.ArgumentList.Arguments[i].Expression;
+ if (expressionSyntax.IsKind(SyntaxKind.InvocationExpression))
+ {
+ var name = (expressionSyntax as InvocationExpressionSyntax)!.ArgumentList.DescendantNodes().First().ToString();
+ excapes.Add(name.Split(['.']).Last());
+ }
+ else if (expressionSyntax.IsKind(SyntaxKind.StringLiteralExpression))
+ {
+ var name = (expressionSyntax as LiteralExpressionSyntax)!.Token.ValueText;
+ excapes.Add(name);
+ }
+ }
+
+ var className = node.Identifier.ValueText;
+ var rootNamespace = node.AncestorsAndSelf().OfType().Single().Name.ToString();
+
+ StringBuilder bodyBuilder = new();
+ List namespaces = [];
+ StringBuilder bodyInnerBuilder = new();
+ StringBuilder mapperBodyBuilder = new();
+
+ bodyInnerBuilder.AppendLine();
+
+ // 生成属性
+ void GenProperty(TypeSyntax @type)
+ {
+ var symbols = compilation.GetSymbolsWithName(type.ToString());
+ foreach (ITypeSymbol symbol in symbols.Cast())
+ {
+ var fullNameSpace = symbol.ContainingNamespace.ToDisplayString();
+ // 命名空间
+ if (!namespaces.Contains(fullNameSpace))
+ {
+ namespaces.Add(fullNameSpace);
+ }
+ symbol.GetMembers().OfType().ToList().ForEach(prop =>
+ {
+ if (!excapes.Contains(prop.Name))
+ {
+ // prop:
+ var raw = $"public {prop.Type.ToDisplayString()} {prop.Name} {{get;set;}}";
+ // body:
+ bodyInnerBuilder.AppendLine($"/// ");
+ bodyInnerBuilder.AppendLine($"{raw}");
+
+ // mapper:
+ mapperBodyBuilder.AppendLine($"{prop.Name} = model.{prop.Name},");
+ }
+ });
+ }
+ }
+
+ // 生成属性:
+ var symbols = compilation.GetSymbolsWithName(entityName, SymbolFilter.Type);
+ var symbol = symbols.Cast().FirstOrDefault();
+ GenProperty(SyntaxFactory.ParseTypeName(symbol.MetadataName));
+
+ // 生成父类的属性:
+ INamedTypeSymbol? baseType = symbol.BaseType;
+ while (baseType != null)
+ {
+ GenProperty(SyntaxFactory.ParseTypeName(baseType.MetadataName));
+ baseType = baseType.BaseType;
+ }
+
+ var rawClass = classTemp.Replace("$className", className);
+ rawClass = rawClass.Replace("$body", bodyInnerBuilder.ToString());
+ // append:
+ bodyBuilder.AppendLine(rawClass);
+
+ string rawNamespace = string.Empty;
+ namespaces.ToList().ForEach(ns => rawNamespace += $"using {ns};\r\n");
+
+ var source = sb.ToString();
+ source = source.Replace("$namespace", rawNamespace);
+ source = source.Replace("$classes", bodyBuilder.ToString());
+ source = source.Replace("$ni", rootNamespace);
+
+ // 生成Mapper
+ var mapperSource = MapperTemplate.Replace("$namespace", namespaces.First());
+ mapperSource = mapperSource.Replace("$ns", rootNamespace);
+ mapperSource = mapperSource.Replace("$baseclass", entityName);
+ mapperSource = mapperSource.Replace("$dtoclass", className);
+ mapperSource = mapperSource.Replace("$body", mapperBodyBuilder.ToString());
+
+ // 合并
+ source = $"{source}\r\n{mapperSource}";
+ envStringBuilder.AppendLine(source);
+ }
+ }
+
+ envStringBuilder.AppendLine("#pragma warning restore");
+ var envSource = envStringBuilder.ToString();
+ // format:
+ envSource = FormatContent(envSource);
+ context.AddSource($"Biwen.AutoClassGenDtoG.g.cs", SourceText.From(envSource, Encoding.UTF8));
+ }
+
+
#region Template
public static readonly string MapperTemplate = $@"
diff --git a/Biwen.AutoClassGen.TestConsole/Biwen.AutoClassGen.TestConsole.csproj b/Biwen.AutoClassGen.TestConsole/Biwen.AutoClassGen.TestConsole.csproj
index 7f8d93a..3935225 100644
--- a/Biwen.AutoClassGen.TestConsole/Biwen.AutoClassGen.TestConsole.csproj
+++ b/Biwen.AutoClassGen.TestConsole/Biwen.AutoClassGen.TestConsole.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net7.0
enable
enable
diff --git a/Biwen.AutoClassGen.TestConsole/Dtos/UserDto.cs b/Biwen.AutoClassGen.TestConsole/Dtos/UserDto.cs
index 4ce79df..e19ee95 100644
--- a/Biwen.AutoClassGen.TestConsole/Dtos/UserDto.cs
+++ b/Biwen.AutoClassGen.TestConsole/Dtos/UserDto.cs
@@ -18,9 +18,19 @@ public partial class UserDto
/// to be generated more than one
///
[AutoDto(typeof(User), nameof(User.Email))]
+
public partial class User2Dto
{
}
+
+ ///
+ /// another way to be generated
+ ///
+ [AutoDto(nameof(User.Email), "TestCol")]
+ public partial class User3Dto
+ {
+ }
+
}
namespace Biwen.AutoClassGen.TestConsole.Entitys
diff --git a/Biwen.AutoClassGen.TestConsole/Program.cs b/Biwen.AutoClassGen.TestConsole/Program.cs
index 494a136..c0092d2 100644
--- a/Biwen.AutoClassGen.TestConsole/Program.cs
+++ b/Biwen.AutoClassGen.TestConsole/Program.cs
@@ -1,4 +1,5 @@
using Biwen.AutoClassGen;
+using Biwen.AutoClassGen.TestConsole.Dtos;
using Biwen.AutoClassGen.TestConsole.Entitys;
@@ -21,6 +22,7 @@
Age = 18,
};
+
var user = new User
{
Age = 18,
@@ -36,7 +38,15 @@
//mapper to User2Dto
var user2Dto = user.MapperToUser2Dto();
+//from [AutoDto(params string?[])]
+var user3Dto = user.MapperToUser3Dto();
+
+
Console.WriteLine($"{queryRequest.KeyWord}");
-Console.WriteLine($"I`m {userDto.FirstName} {userDto.LastName} I`m {userDto.Age} years old");
+Console.WriteLine($"I`m {nameof(userDto)} {userDto.FirstName} {userDto.LastName} I`m {userDto.Age} years old");
+Console.WriteLine($"I`m {nameof(user2Dto)} {user2Dto.FirstName} {user2Dto.LastName} I`m {user2Dto.Age} years old");
+Console.WriteLine($"I`m {nameof(user3Dto)} {user3Dto.FirstName} {user3Dto.LastName} I`m {user3Dto.Age} years old");
+
+
Console.ReadLine();
\ No newline at end of file