1

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.

Community
  • 1
  • 1
Saiori
  • 41
  • 1
  • 3
  • Did you see [this SO question](http://stackoverflow.com/questions/8750018/net-dll-references-dependency-checking-utility)? I think it might give you what you're looking for. – DeadZone Oct 06 '15 at 15:53
  • I would fix this problem instead: _"To do a complete deploy is costly (time-wise)"_. You seem to be asking to limit updates to deployed assemblies to those which you believe code changes exist which would actually be executed in that environment. Trying to do this on a method-by-method basis seems overly tricky and likely to lead to diagnostic confusion. Better would be to only deploy to an environment those assemblies actually needed, and then use some kind of versioning to decide which assemblies to update on deployment. – Peter Duniho Oct 06 '15 at 15:54
  • How are you deploying these applications? If you are looking for incremental deployment then the solution you are proposing could be over kill. There are ways to do the incremental deployment even you can identify the assemblies which are changed from last full build if you have a build server setup for full build. – vendettamit Oct 06 '15 at 18:09
  • @PeterDuniho It is very tricky, and ultimately not what we want to do, but we're stuck in a bit of a crunch as we transition teams. As of Monday we decided to go with a more simplified, best guess and check approach. – Saiori Oct 07 '15 at 17:09
  • @vendettamit I am not very well versed in how we deploy. We have a team that handles it and I'm fairly sure that it is at least partially automated, but we are in a post flux state and this project had been shelved for a long while, so many of us have never worked in it before. We're trying to get up to speed, but in the mean time I was trying to find a way to get a definitive list of the assemblies that use the specific functions changed. – Saiori Oct 07 '15 at 17:12

1 Answers1

8

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.

vendettamit
  • 14,315
  • 2
  • 32
  • 54
  • Thank you for your response. Things have gotten crazy, but I will test it out soon and respond. – Saiori Oct 09 '15 at 15:37