傳統做法:定義 XSD → 撰寫 XML 模型 → 實作 IVsSingleFileGenerator 讀取 XML 產生 C# 實體類別。缺點:
核心概念:開發者撰寫介面定義,產生器讀取介面語法樹,輸出實作類別。優勢:
[Table]
interface IUser {
[ID]
string UserName { get; set; }
string BirthDate { get; set; }
string Region { get; set; }
string Remark { get; set; }
}
[ComVisible(true)]
[Guid("...")]
[CodeGeneratorRegistration(typeof(InterfaceToClassGenerator), "Interface To Class Generator", vsContextGuids.vsContextGuidVCSProject)]
public class InterfaceToClassGenerator : BaseCodeGeneratorWithSite {
protected override byte[] GenerateCode(string inputFileName, string inputFileContent) {
// 1. 解析輸入檔案 (應為 .cs 含介面定義)
var syntaxTree = CSharpSyntaxTree.ParseText(inputFileContent);
var root = syntaxTree.GetCompilationUnitRoot();
var interfaceDecl = root.DescendantNodes().OfType().FirstOrDefault();
if (interfaceDecl == null) return Encoding.UTF8.GetBytes("// No interface found");
// 2. 建構輸出類別語法樹
var className = interfaceDecl.Identifier.Text.TrimStart('I'); // IUser → User
var members = new List();
foreach (var prop in interfaceDecl.Members.OfType()) {
// 複製 Attribute 清單
var attrs = prop.AttributeLists;
// 產生私有欄位
var fieldName = "_" + char.ToLower(prop.Identifier.Text[0]) + prop.Identifier.Text.Substring(1);
var field = SyntaxFactory.FieldDeclaration(
SyntaxFactory.VariableDeclaration(prop.Type)
.WithVariables(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.VariableDeclarator(fieldName))))
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)));
// 產生屬性實作 (含 EntityState.FieldChange 通知)
var property = SyntaxFactory.PropertyDeclaration(prop.Type, prop.Identifier)
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
.WithAttributeLists(attrs)
.WithAccessorList(SyntaxFactory.AccessorList(
SyntaxFactory.List(new[] {
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(fieldName)))),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithBody(SyntaxFactory.Block(
SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName(fieldName), SyntaxFactory.IdentifierName("value"))),
SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName("EntityState"),
SyntaxFactory.IdentifierName("FieldChange")))
.WithArgumentList(SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(prop.Identifier.Text)))))))))
})));
// 靜態 FieldInfo 欄位 (供 ORM 使用)
var staticField = SyntaxFactory.FieldDeclaration(
SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("FieldInfo"))
.WithVariables(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.VariableDeclarator(prop.Identifier.Text + "Field")
.WithInitializer(SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName("FieldInfo"))
.WithArgumentList(SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList(new[] {
SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(className))),
SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(prop.Identifier.Text))))
})))))))
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)));
members.Add(field);
members.Add(property);
members.Add(staticField);
}
var classDecl = SyntaxFactory.ClassDeclaration(className)
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
.WithBaseList(SyntaxFactory.BaseList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("DataObject")))))
.WithAttributeLists(interfaceDecl.AttributeLists) // 複製介面上的 [Table] 等
.WithMembers(SyntaxFactory.List(members));
var namespaceDecl = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("MyApp.Entities"))
.WithMembers(SyntaxFactory.SingletonSeparatedList(classDecl));
var outputTree = SyntaxFactory.CompilationUnit().AddMembers(namespaceDecl).NormalizeWhitespace();
return Encoding.UTF8.GetBytes(outputTree.ToFullString());
}
}
.vsix 安裝至 Visual Studio。Custom Tool = "InterfaceToClassGenerator",Custom Tool Namespace 可選填。IUser.cs (介面定義) → 儲存 → 產生 IUser.Generated.cs (實作類別) → 專案引用產生的類別。virtual、override、[NotMapped] 等。