Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
849 views
in Technique[技术] by (71.8m points)

visual studio - C# Recursively Find References in All Projects In Solution

We have a very large solution (140ish projects) we deploy different projects to different servers. To do a complete deploy is costly (time-wise), so we will try to track our changes to determine what projects are affected by the change and then only deploy those projects.

For example:

Let's say that we have a project "Integral Part 1" (IP1) and another "Intergral Part 2" (IP2). Inside IP2 we have a class, Advert, with a method that generates an html link, GenerateLink. GenerateLink is called by another method in Advert called, GenerateAd. I modify GenerateLink.

IP1 calls into and uses the services available in IP2. Therefore IP1 will need to be redeployed in order for the changes to be visible in IP1.

This is a simplistic view, but should relate the issue. Currently, I need to go into the GenerateLink method and find all references, then follow each reference and find all references on them. I repeat this process until I have found all references across all projects within the solution that in some way are affected by my change.

Is there some way to automate this process, and simply ask for all references, recursively, for a method?

The closest answer I've found in my searches is here: Programmatically find all references to a function recursively, but I don't think it's quite what I'm looking for. This sounds more like the find all references tool already in Visual Studio.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can use Roslyn aka Microsoft.CodeAnalysis to achieve this. you need to setup the machine to work it out.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RoslynCompiler
{
    class ReferenceFinder
    {
        public void Find(string methodName)
        {

            string solutionPath = @"C:Users...ConsoleForEverything.sln";
            var msWorkspace = MSBuildWorkspace.Create();

            List<ReferencedSymbol> referencesToMethod = new List<ReferencedSymbol>();
            Console.WriteLine("Searching for method "{0}" reference in solution {1} ", methodName, Path.GetFileName(solutionPath));
            ISymbol methodSymbol = null;
            bool found = false;

            //You must install the MSBuild Tools or this line will throw an exception.

            var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;
            foreach (var project in solution.Projects)
            {
                foreach (var document in project.Documents)
                {
                    var model = document.GetSemanticModelAsync().Result;

                    var methodInvocation = document.GetSyntaxRootAsync().Result;
                    InvocationExpressionSyntax node = null;
                    try
                    {
                        node = methodInvocation.DescendantNodes().OfType<InvocationExpressionSyntax>()
                         .Where(x => ((MemberAccessExpressionSyntax)x.Expression).Name.ToString() == methodName).FirstOrDefault();

                        if (node == null)
                            continue;
                    }
                    catch(Exception exception)
                    {
                        // Swallow the exception of type cast. 
                        // Could be avoided by a better filtering on above linq.
                        continue;
                    }

                    methodSymbol = model.GetSymbolInfo(node).Symbol;
                    found = true;
                    break;
                }

                if (found) break;
            }

            foreach (var item in SymbolFinder.FindReferencesAsync(methodSymbol, solution).Result)
            {
                foreach (var location in item.Locations)
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine("Project Assembly -> {0}", location.Document.Project.AssemblyName);
                    Console.ResetColor();
                }

            }

            Console.WriteLine("Finished searching. Press any key to continue....");
        }
    }
}

Download and install following items before you run the sample:

.Net 4.6 runtime

.Net 4.6 targeting pack

MSBuildTools 2015

The following setup is required to avoid a runtime exception that MSBuildWorkspace Throws exception The type or namespace name 'MSBuild' does not exist in the namespace 'Microsoft.CodeAnalysis' (are you missing an assembly reference?) because the assembly in nuget package is built on 4.5.2.

Create a console application targeting .Net 4.6 and Install the nuget package: Microsoft.CodeAnalysis 1.0.0

Test run of above code:

        ReferenceFinder finder = new ReferenceFinder();
        finder.Find("Read");

Output:

enter image description here

This program might require more enhancment as Roslyn is much more powerful. But this should give you a head start. You can explore more about Roslyn and you can completely control your project solution code etc. from C# code.

TODO: I will create a github project for this console app and I will update this post soon.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...