增量管道和缓存机制
本文档深入讲解增量管道操作、缓存机制和性能优化策略。
文档信息
- 难度级别: 高级
- 预计阅读时间: 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();
}鎬ц兘娴嬭瘯缁撴灉
| 浼樺寲绛栫暐 | 棣栨缂栬瘧 | 鏃犲彉鍖栭噸缂栬瘧 | 灏忔敼鍔ㄩ噸缂栬瘧 | 鏀硅繘 |
|---|---|---|---|---|
| 鏃犱紭鍖? | 1000ms | 1000ms | 1000ms | - |
| 蹇€熻繃婊? | 800ms | 800ms | 800ms | 20% |
| + 寤惰繜璇箟鍒嗘瀽 | 600ms | 100ms | 200ms | 80% |
| + 鏈€灏忓寲鏁版嵁 | 500ms | 50ms | 150ms | 90% |
| + 鎵归噺澶勭悊 | 400ms | 10ms | 100ms | 95% |
鐪熷疄搴旂敤鍦烘櫙
鍦烘櫙 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 鍙嶆ā寮?
📝 下一步
最后更新: 2025-01-21