我们正在使用Roslyn来完成代码库并替换数据库调用,并将新的会话参数添加到之前创建它们的任何方法中。例如:
public bool Save(string someData) { // implementation here }
// Becomes
public bool Save(IDriveSession session, string someData) { }
// And Invocations Go From
Save("MyData");
// to
Save(session, "MyData");
除了Linq调用之外,这是有效的,特别是那些已经减少的调用。例如:
public IEnumerable<Person> GetPeople(IEnumerable<Guid> ids)
{
return ids.Select(GetPerson);
}
// Should transform to
public IEnumerable<Person> GetPeople(IDriveSession session, IEnumerable<Guid> ids)
{
return ids.Select(x => GetPerson(session, x));
}
如果它在foreach循环中,它将正确地看到并转换它。我不确定如何正确看到Linq。
以下是我正在使用的CSharpSyntaxRewriter。
public class DriveSessionMethodReferencesRewriter : CSharpSyntaxRewriter
{
private readonly SemanticModel semanticModel;
public DriveSessionMethodReferencesRewriter(SemanticModel semanticModel)
{
this.semanticModel = semanticModel ?? throw new ArgumentNullException(nameof(semanticModel));
}
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) => VisitDeclaration(node);
public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node) => VisitDeclaration(node);
private SyntaxNode VisitDeclaration(BaseMethodDeclarationSyntax node)
{
var methodInvocations = RewriterUtility
.GetMethodInvocationsMissingDriveSessionParameter(semanticModel, node)
.ToList();
var objectCreations = RewriterUtility
.GetObjectCreationsMissingDriveSessionParameter(semanticModel, node)
.ToList();
if (!methodInvocations.Any() && !objectCreations.Any()) return node;
node = node.ReplaceNodes(methodInvocations, (n, ne) => ne.PrependArgument(RewriterUtility.CreateDriveSessionArgument()));
node = node.ReplaceNodes(objectCreations, (n, ne) => ne.PrependArgument(RewriterUtility.CreateDriveSessionArgument()));
if (!RewriterUtility.HasDriveSessionParameter(node))
{
if (node is ConstructorDeclarationSyntax)
node = (node as ConstructorDeclarationSyntax).PrependParameter(RewriterUtility.CreateDriveSessionParameter());
else if (node is MethodDeclarationSyntax)
node = (node as MethodDeclarationSyntax).PrependParameter(RewriterUtility.CreateDriveSessionParameter());
}
return node;
}
}
适用的静态方法
public static IEnumerable<InvocationExpressionSyntax> GetMethodInvocationsMissingDriveSessionParameter(
SemanticModel semanticModel, SyntaxNode syntax) =>
syntax.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Where(invocationExpressionSyntax => invocationExpressionSyntax.ArgumentList == null ||
!invocationExpressionSyntax.ArgumentList.Arguments.Any(a => a.ToString() == DriveSessionParameterName))
.Where(invocationExpressionSyntax => CallsMethodWithDriveSessionParameter(semanticModel, invocationExpressionSyntax));
// Child Nodes that have method calls with 0 arguments OR without session argument AND the method called takes a new Session Parm.
// * This may need to be recursive to find nested calls.
public static IEnumerable<ObjectCreationExpressionSyntax> GetObjectCreationsMissingDriveSessionParameter(
SemanticModel semanticModel, SyntaxNode syntax) =>
syntax.DescendantNodes()
.OfType<ObjectCreationExpressionSyntax>()
.Where(invocationExpressionSyntax => invocationExpressionSyntax.ArgumentList == null || !invocationExpressionSyntax.ArgumentList.Arguments.Any(a => a.ToString() == DriveSessionParameterName))
.Where(invocationExpressionSyntax => CallsMethodWithDriveSessionParameter(semanticModel, invocationExpressionSyntax));
//Determine if methods need session parm.
private static bool CallsMethodWithDriveSessionParameter(SemanticModel semanticModel, ExpressionSyntax syntax)
{
SymbolInfo symbolInfo;
symbolInfo = semanticModel.GetSymbolInfo(syntax);
return symbolInfo.CandidateSymbols
.OfType<IMethodSymbol>()
.Any(methodSymbol => methodSymbol.Parameters.Any(p => p.Type.Name == DriveSessionTypeName));
}