Skip to content

增量管道和缓存机制

本文档深入讲解增量管道操作、缓存机制和性能优化策略。

文档信息

  • 难度级别: 高级
  • 预计阅读时间: 20 分钟

缂撳瓨鏈哄埗璇﹁В

缂撳瓨宸ヤ綔鍘熺悊

IEquatable 瀹炵幇

澧為噺鐢熸垚鍣ㄤ娇鐢?IEquatable<T> 鏉ュ垽鏂暟鎹槸鍚﹀彉鍖栥€傛纭疄鐜?IEquatable 鏄紦瀛樻湁鏁堢殑鍏抽敭銆?

鎺ㄨ崘锛氫娇鐢?record 绫诲瀷

csharp
// 鉁?鎺ㄨ崘锛歳ecord 鑷姩瀹炵幇 IEquatable
public record ClassInfo(
    string Name,
    string Namespace,
    ImmutableArray<PropertyInfo> Properties
);

public record PropertyInfo(
    string Name,
    string Type
);

鎵嬪姩瀹炵幇 IEquatable

csharp
// 濡傛灉涓嶈兘浣跨敤 record锛屾墜鍔ㄥ疄鐜?IEquatable
public class ClassInfo : IEquatable<ClassInfo>
{
    public string Name { get; }
    public string Namespace { get; }
    public ImmutableArray<PropertyInfo> Properties { get; }
    
    public ClassInfo(string name, string ns, ImmutableArray<PropertyInfo> props)
    {
        Name = name;
        Namespace = ns;
        Properties = props;
    }
    
    public bool Equals(ClassInfo? other)
    {
        if (other is null) return false;
        if (ReferenceEquals(this, other)) return true;
        
        return Name == other.Name &&
               Namespace == other.Namespace &&
               Properties.SequenceEqual(other.Properties);
    }
    
    public override bool Equals(object? obj)
        => Equals(obj as ClassInfo);
    
    public override int GetHashCode()
    {
        var hash = new HashCode();
        hash.Add(Name);
        hash.Add(Namespace);
        
        foreach (var prop in Properties)
        {
            hash.Add(prop);
        }
        
        return hash.ToHashCode();
    }
}

缂撳瓨绛栫暐瀵规瘮

绛栫暐閫傜敤鍦烘櫙浼樼偣缂虹偣
*鍊肩浉绛?绠€鍗曟暟鎹被鍨?绮剧‘銆佸彲闈?闇€瑕佸疄鐜?IEquatable
寮曠敤鐩哥瓑涓嶅彲鍙樺璞?鎬ц兘鏈€濂?鍙兘瀵艰嚧涓嶅繀瑕佺殑閲嶆柊璁$畻
*鑷畾涔夋瘮杈?澶嶆潅瀵硅薄鐏垫椿瀹炵幇澶嶆潅

缂撳瓨澶辨晥鍦烘櫙

csharp
// 鍦烘櫙 1锛氬睘鎬у€煎彉鍖?
public record PersonInfo(string Name, int Age);

var person1 = new PersonInfo("John", 30);
var person2 = new PersonInfo("John", 31);  // Age 鍙樺寲锛岀紦瀛樺け鏁?

// 鍦烘櫙 2锛氶泦鍚堝厓绱犲彉鍖?
public record ClassInfo(
    string Name,
    ImmutableArray<string> Properties
);

var class1 = new ClassInfo("Person", ImmutableArray.Create("Name", "Age"));
var class2 = new ClassInfo("Person", ImmutableArray.Create("Name", "Age", "Email"));
// Properties 鍙樺寲锛岀紦瀛樺け鏁?

// 鍦烘櫙 3锛氶泦鍚堥『搴忓彉鍖?
var props1 = ImmutableArray.Create("Name", "Age");
var props2 = ImmutableArray.Create("Age", "Name");
// 椤哄簭涓嶅悓锛岀紦瀛樺け鏁堬紙濡傛灉浣跨敤 SequenceEqual锛?

浼樺寲缂撳瓨鏁堢巼

csharp
// 鉂?閬垮厤锛氬寘鍚笉蹇呰鐨勬暟鎹?
public record ClassInfo(
    string Name,
    string Namespace,
    SyntaxTree SyntaxTree,  // 涓嶈鍖呭惈鏁翠釜璇硶鏍戯紒
    ImmutableArray<PropertyInfo> Properties
);

// 鉁?鎺ㄨ崘锛氬彧鍖呭惈蹇呰鐨勬暟鎹?
public record ClassInfo(
    string Name,
    string Namespace,
    ImmutableArray<PropertyInfo> Properties
);

// 鉂?閬垮厤锛氫娇鐢ㄥ彲鍙橀泦鍚?
public record ClassInfo(
    string Name,
    List<PropertyInfo> Properties  // List 鏄彲鍙樼殑
);

// 鉁?鎺ㄨ崘锛氫娇鐢ㄤ笉鍙彉闆嗗悎
public record ClassInfo(
    string Name,
    ImmutableArray<PropertyInfo> Properties
);

鎬ц兘浼樺寲绛栫暐

浼樺寲鍘熷垯

绛栫暐 1锛氬揩閫熻娉曡繃婊?

csharp
// 鉂?鎱細鍦?predicate 涓娇鐢ㄥ鏉傞€昏緫
var provider = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: (node, ct) =>
    {
        if (node is not ClassDeclarationSyntax cls)
            return false;
        
        // 鎱細瀛楃涓叉搷浣?
        var text = cls.ToString();
        if (text.Contains("abstract"))
            return false;
        
        // 鎱細閬嶅巻鎵€鏈夋垚鍛?
        foreach (var member in cls.Members)
        {
            // ...
        }
        
        return true;
    },
    transform: (ctx, ct) => ctx.Node
);

// 鉁?蹇細浣跨敤绠€鍗曠殑璇硶妫€鏌?
var provider = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: (node, ct) =>
    {
        // 蹇細绫诲瀷妫€鏌?
        if (node is not ClassDeclarationSyntax cls)
            return false;
        
        // 蹇細妫€鏌ヤ慨楗扮
        if (cls.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)))
            return false;
        
        // 蹇細妫€鏌ョ壒鎬у垪琛ㄦ暟閲?
        if (cls.AttributeLists.Count == 0)
            return false;
        
        return true;
    },
    transform: (ctx, ct) => ctx.Node
);

绛栫暐 2锛氬欢杩熻涔夊垎鏋?

csharp
// 鉂?閬垮厤锛氬湪 predicate 涓娇鐢ㄨ涔夋ā鍨?
var provider = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: (node, ct) =>
    {
        var model = /* 鑾峰彇璇箟妯″瀷 */;  // 闈炲父鎱紒
        var symbol = model.GetDeclaredSymbol(node);
        return symbol != null;
    },
    transform: (ctx, ct) => ctx.Node
);

// 鉁?鎺ㄨ崘锛氬湪 transform 涓娇鐢ㄨ涔夋ā鍨?
var provider = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: (node, ct) =>
        node is ClassDeclarationSyntax,  // 鍙仛璇硶妫€鏌?
    transform: (ctx, ct) =>
    {
        // 鍦ㄨ繖閲屼娇鐢ㄨ涔夋ā鍨?
        var symbol = ctx.SemanticModel.GetDeclaredSymbol(ctx.Node);
        return symbol;
    }
);

绛栫暐 3锛氭渶灏忓寲鏁版嵁浼犺緭

csharp
// 鉂?閬垮厤锛氫紶杈撳ぇ閲忔暟鎹?
public record ClassInfo(
    string Name,
    string Namespace,
    string FullSourceCode,  // 鏁翠釜婧愪唬鐮侊紒
    SyntaxTree Tree,        // 鏁翠釜璇硶鏍戯紒
    ImmutableArray<MemberInfo> AllMembers  // 鎵€鏈夋垚鍛橈紒
);

// 鉁?鎺ㄨ崘锛氬彧浼犺緭蹇呰鏁版嵁
public record ClassInfo(
    string Name,
    string Namespace,
    ImmutableArray<PropertyInfo> PublicProperties  // 鍙鍏叡灞炴€?
);

public record PropertyInfo(
    string Name,
    string Type
);

绛栫暐 4锛氫娇鐢?Collect 鎵归噺澶勭悊

csharp
// 鉂?浣庢晥锛氫负姣忎釜绫荤敓鎴愬崟鐙殑鏂囦欢
context.RegisterSourceOutput(classes, (spc, classInfo) =>
{
    var source = GenerateCode(classInfo);
    spc.AddSource($"{classInfo.Name}.g.cs", source);
});

// 鉁?楂樻晥锛氭敹闆嗘墍鏈夌被锛岀敓鎴愬崟涓枃浠?
var allClasses = classes.Collect();
context.RegisterSourceOutput(allClasses, (spc, classList) =>
{
    var sb = new StringBuilder();
    
    // 鐢熸垚鍗曚釜鏂囦欢鍖呭惈鎵€鏈夌被
    sb.AppendLine("namespace Generated {");
    
    foreach (var classInfo in classList)
    {
        sb.AppendLine(GenerateCode(classInfo));
    }
    
    sb.AppendLine("}");
    
    spc.AddSource("AllGenerated.g.cs", sb.ToString());
});

绛栫暐 5锛氶伩鍏嶉噸澶嶈绠?

csharp
// 鉂?閬垮厤锛氶噸澶嶈绠?
var provider = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: (node, ct) => node is ClassDeclarationSyntax,
    transform: (ctx, ct) =>
    {
        var cls = (ClassDeclarationSyntax)ctx.Node;
        var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls);
        
        // 姣忔閮介噸鏂拌绠楀睘鎬у垪琛?
        var properties = symbol.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => p.DeclaredAccessibility == Accessibility.Public)
            .ToImmutableArray();
        
        return new ClassInfo(symbol.Name, properties);
    }
);

// 鉁?鎺ㄨ崘锛氫娇鐢ㄧ紦瀛?
var provider = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: (node, ct) => node is ClassDeclarationSyntax,
    transform: (ctx, ct) =>
    {
        var cls = (ClassDeclarationSyntax)ctx.Node;
        var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls);
        
        // 杩斿洖 record锛岃嚜鍔ㄥ疄鐜扮紦瀛?
        return new ClassInfo(
            Name: symbol.Name,
            Properties: GetPublicProperties(symbol)
        );
    }
);

// 杈呭姪鏂规硶
static ImmutableArray<PropertyInfo> GetPublicProperties(INamedTypeSymbol symbol)
{
    return symbol.GetMembers()
        .OfType<IPropertySymbol>()
        .Where(p => p.DeclaredAccessibility == Accessibility.Public)
        .Select(p => new PropertyInfo(p.Name, p.Type.ToDisplayString()))
        .ToImmutableArray();
}

鎬ц兘娴嬭瘯缁撴灉

浼樺寲绛栫暐棣栨缂栬瘧鏃犲彉鍖栭噸缂栬瘧灏忔敼鍔ㄩ噸缂栬瘧鏀硅繘
鏃犱紭鍖?1000ms1000ms1000ms-
蹇€熻繃婊?800ms800ms800ms20%
+ 寤惰繜璇箟鍒嗘瀽600ms100ms200ms80%
+ 鏈€灏忓寲鏁版嵁500ms50ms150ms90%
+ 鎵归噺澶勭悊400ms10ms100ms95%

鐪熷疄搴旂敤鍦烘櫙

鍦烘櫙 1锛氳嚜鍔ㄧ敓鎴?DTO 鏄犲皠

**闇€姹?*锛氫负鎵€鏈夊疄浣撶被鑷姩鐢熸垚鍒?DTO 鐨勬槧灏勪唬鐮併€?

瀹炵幇锛?

csharp
[Generator]
public class DtoMapperGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 鏌ユ壘鎵€鏈夊甫 [GenerateMapper] 鐗规€х殑绫?
        var entities = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) =>
                    node is ClassDeclarationSyntax cls &&
                    cls.AttributeLists.Count > 0,
                transform: (ctx, _) =>
                {
                    var cls = (ClassDeclarationSyntax)ctx.Node;
                    var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls);
                    
                    if (symbol == null)
                        return null;
                    
                    var hasAttribute = symbol.GetAttributes()
                        .Any(a => a.AttributeClass?.Name == "GenerateMapperAttribute");
                    
                    if (!hasAttribute)
                        return null;
                    
                    return new EntityInfo(
                        Name: symbol.Name,
                        Namespace: symbol.ContainingNamespace.ToDisplayString(),
                        Properties: symbol.GetMembers()
                            .OfType<IPropertySymbol>()
                            .Where(p => p.DeclaredAccessibility == Accessibility.Public)
                            .Select(p => new PropertyInfo(
                                p.Name,
                                p.Type.ToDisplayString()
                            ))
                            .ToImmutableArray()
                    );
                }
            )
            .Where(info => info != null);
        
        // 鐢熸垚鏄犲皠浠g爜
        context.RegisterSourceOutput(entities, (spc, entity) =>
        {
            var source = $@"
namespace {entity!.Namespace}
{{
    public static class {entity.Name}Mapper
    {{
        public static {entity.Name}Dto ToDto(this {entity.Name} entity)
        {{
            return new {entity.Name}Dto
            {{
{string.Join(",\n", entity.Properties.Select(p => $"                {p.Name} = entity.{p.Name}"))}
            }};
        }}
        
        public static {entity.Name} ToEntity(this {entity.Name}Dto dto)
        {{
            return new {entity.Name}
            {{
{string.Join(",\n", entity.Properties.Select(p => $"                {p.Name} = dto.{p.Name}"))}
            }};
        }}
    }}
}}";
            spc.AddSource($"{entity.Name}Mapper.g.cs", source);
        });
    }
}

record EntityInfo(string Name, string Namespace, ImmutableArray<PropertyInfo> Properties);
record PropertyInfo(string Name, string Type);

浣跨敤锛?

csharp
[GenerateMapper]
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

// 鐢熸垚鐨勪唬鐮侊細
public static class UserMapper
{
    public static UserDto ToDto(this User entity)
    {
        return new UserDto
        {
            Id = entity.Id,
            Name = entity.Name,
            Email = entity.Email
        };
    }
    
    public static User ToEntity(this UserDto dto)
    {
        return new User
        {
            Id = dto.Id,
            Name = dto.Name,
            Email = dto.Email
        };
    }
}

// 浣跨敤鏄犲皠
var user = new User { Id = 1, Name = "John", Email = "john@example.com" };
var dto = user.ToDto();

鍦烘櫙 2锛氳嚜鍔ㄧ敓鎴愰獙璇佷唬鐮?

**闇€姹?*锛氫负甯﹂獙璇佺壒鎬х殑灞炴€ц嚜鍔ㄧ敓鎴愰獙璇佹柟娉曘€?

瀹炵幇锛?

csharp
[Generator]
public class ValidationGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var classesWithValidation = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) =>
                {
                    var cls = (ClassDeclarationSyntax)ctx.Node;
                    var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls);
                    
                    if (symbol == null)
                        return null;
                    
                    var validatedProperties = symbol.GetMembers()
                        .OfType<IPropertySymbol>()
                        .Select(p => new
                        {
                            Property = p,
                            Attributes = p.GetAttributes()
                                .Where(a => a.AttributeClass?.Name.EndsWith("Attribute") == true)
                                .ToList()
                        })
                        .Where(x => x.Attributes.Any())
                        .ToList();
                    
                    if (!validatedProperties.Any())
                        return null;
                    
                    return new ValidationInfo(
                        ClassName: symbol.Name,
                        Namespace: symbol.ContainingNamespace.ToDisplayString(),
                        Properties: validatedProperties
                            .Select(x => new ValidatedProperty(
                                Name: x.Property.Name,
                                Type: x.Property.Type.ToDisplayString(),
                                Validations: x.Attributes
                                    .Select(a => a.AttributeClass!.Name)
                                    .ToImmutableArray()
                            ))
                            .ToImmutableArray()
                    );
                }
            )
            .Where(info => info != null);
        
        context.RegisterSourceOutput(classesWithValidation, (spc, info) =>
        {
            var sb = new StringBuilder();
            sb.AppendLine($"namespace {info!.Namespace}");
            sb.AppendLine("{");
            sb.AppendLine($"    public partial class {info.ClassName}");
            sb.AppendLine("    {");
            sb.AppendLine("        public bool Validate(out List<string> errors)");
            sb.AppendLine("        {");
            sb.AppendLine("            errors = new List<string>();");
            
            foreach (var prop in info.Properties)
            {
                foreach (var validation in prop.Validations)
                {
                    if (validation == "RequiredAttribute")
                    {
                        sb.AppendLine($"            if ({prop.Name} == null)");
                        sb.AppendLine($"                errors.Add(\"{prop.Name} is required\");");
                    }
                    else if (validation == "MaxLengthAttribute")
                    {
                        sb.AppendLine($"            if ({prop.Name}?.Length > 100)");
                        sb.AppendLine($"                errors.Add(\"{prop.Name} is too long\");");
                    }
                }
            }
            
            sb.AppendLine("            return errors.Count == 0;");
            sb.AppendLine("        }");
            sb.AppendLine("    }");
            sb.AppendLine("}");
            
            spc.AddSource($"{info.ClassName}.Validation.g.cs", sb.ToString());
        });
    }
}

record ValidationInfo(
    string ClassName,
    string Namespace,
    ImmutableArray<ValidatedProperty> Properties
);

record ValidatedProperty(
    string Name,
    string Type,
    ImmutableArray<string> Validations
);

浣跨敤锛?

csharp
public partial class CreateUserRequest
{
    [Required]
    [MaxLength(100)]
    public string Name { get; set; }
    
    [Required]
    [EmailAddress]
    public string Email { get; set; }
}

// 浣跨敤楠岃瘉
var request = new CreateUserRequest { Name = "", Email = "invalid" };
if (!request.Validate(out var errors))
{
    foreach (var error in errors)
    {
        Console.WriteLine(error);
    }
}

鍦烘櫙 3锛氳嚜鍔ㄧ敓鎴愪緷璧栨敞鍏ユ敞鍐屼唬鐮?

**闇€姹?*锛氳嚜鍔ㄤ负甯?[Injectable] 鐗规€х殑绫荤敓鎴愪緷璧栨敞鍏ユ敞鍐屼唬鐮併€?

瀹炵幇锛?

csharp
[Generator]
public class DependencyInjectionGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var injectableClasses = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) =>
                    node is ClassDeclarationSyntax cls &&
                    cls.AttributeLists.Count > 0,
                transform: (ctx, _) =>
                {
                    var cls = (ClassDeclarationSyntax)ctx.Node;
                    var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls);
                    
                    if (symbol == null)
                        return null;
                    
                    var injectableAttr = symbol.GetAttributes()
                        .FirstOrDefault(a => a.AttributeClass?.Name == "InjectableAttribute");
                    
                    if (injectableAttr == null)
                        return null;
                    
                    // 鑾峰彇鐢熷懡鍛ㄦ湡锛圱ransient, Scoped, Singleton锛?
                    var lifetime = injectableAttr.ConstructorArguments.FirstOrDefault().Value?.ToString() 
                        ?? "Transient";
                    
                    // 鑾峰彇鎺ュ彛
                    var interfaceType = symbol.Interfaces.FirstOrDefault();
                    
                    return new InjectableInfo(
                        ClassName: symbol.Name,
                        Namespace: symbol.ContainingNamespace.ToDisplayString(),
                        InterfaceName: interfaceType?.ToDisplayString(),
                        Lifetime: lifetime
                    );
                }
            )
            .Where(info => info != null)
            .Collect();
        
        context.RegisterSourceOutput(injectableClasses, (spc, classes) =>
        {
            var sb = new StringBuilder();
            sb.AppendLine("using Microsoft.Extensions.DependencyInjection;");
            sb.AppendLine();
            sb.AppendLine("namespace Generated");
            sb.AppendLine("{");
            sb.AppendLine("    public static class ServiceCollectionExtensions");
            sb.AppendLine("    {");
            sb.AppendLine("        public static IServiceCollection AddGeneratedServices(");
            sb.AppendLine("            this IServiceCollection services)");
            sb.AppendLine("        {");
            
            foreach (var info in classes)
            {
                if (info == null) continue;
                
                var serviceType = info.InterfaceName ?? info.ClassName;
                var implementationType = info.ClassName;
                var method = info.Lifetime switch
                {
                    "Singleton" => "AddSingleton",
                    "Scoped" => "AddScoped",
                    _ => "AddTransient"
                };
                
                if (info.InterfaceName != null)
                {
                    sb.AppendLine($"            services.{method}<{serviceType}, {implementationType}>();");
                }
                else
                {
                    sb.AppendLine($"            services.{method}<{implementationType}>();");
                }
            }
            
            sb.AppendLine("            return services;");
            sb.AppendLine("        }");
            sb.AppendLine("    }");
            sb.AppendLine("}");
            
            spc.AddSource("ServiceCollectionExtensions.g.cs", sb.ToString());
        });
    }
}

record InjectableInfo(
    string ClassName,
    string Namespace,
    string? InterfaceName,
    string Lifetime
);

浣跨敤锛?

csharp
public interface IUserService
{
    User GetUser(int id);
}

[Injectable("Scoped")]
public class UserService : IUserService
{
    public User GetUser(int id)
    {
        // ...
    }
}

[Injectable("Singleton")]
public class CacheService
{
    // ...
}

// 鍦?Startup.cs 涓娇鐢?
public void ConfigureServices(IServiceCollection services)
{
    services.AddGeneratedServices();  // 鑷姩娉ㄥ唽鎵€鏈夋湇鍔?
}

鏈€浣冲疄璺?vs 鍙嶆ā寮?


📝 下一步

  1. 探索 最佳实践和FAQ
  2. 复习 基础概念

最后更新: 2025-01-21

基于 MIT 许可发布