4

Please, observe the following trivial program using MEF as the Dependency Injection framework:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace ConsoleApplication2
{
    [InheritedExport]
    public interface ITest
    {
        void DoSomething();
    }

    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class Test : ITest
    {
        #region Implementation of ITest

        public void DoSomething()
        {
            Program.BackToProgram();
        }

        #endregion
    }

    [Export]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class TestClient
    {
        private readonly ITest m_test;

        [ImportingConstructor]
        public TestClient(ITest test)
        {
            m_test = test;
        }

        public void DoSomethingFromTestClient()
        {
            m_test.DoSomething();
        }
    }

    class Program
    {
        private static CompositionContainer m_container;

        static void Main()
        {
            m_container = new CompositionContainer(new TypeCatalog(typeof(Test), typeof(TestClient)), true);
            var testClient = m_container.GetExportedValue<TestClient>();
            testClient.DoSomethingFromTestClient();
        }

        public static void BackToProgram()
        {
        }
    }
}

Now let us analyse it with NDepend 6.3. Suppose I wish to know all the direct and indirect callers of Program.BackToProgram: enter image description here

However, the class TestClient consumes a Test instance through the ITest interface using Dependency Injection, so looking for the direct and indirect callers of ITest.DoSomething gives me this: enter image description here

So, this gives me the complete picture - Program.BackToProgram is ultimately called from Program.Main.

Unfortunately, I had to resort to manual code inspection in order to connect the dots. Dependency Injection seems to break NDepend's ability to track what calls what across the DI boundaries.

While this can be explained by the fact that DI heavily relies on reflection and that reflection does not really lend itself to static code analysis, this poses, nevertheless, a big problem, because our code uses DI quite a lot.

So, is there any solution to this problem? Is there a way to configure NDepend to recognise the Dependency Injection as implemented by MEF? At the end of the day, when asked for all the direct and indirect callers of Program.BackToProgram I wish to see Program.Main on the graph without human intervention.

Maybe there is another tool that does it?

EDIT 1

The answer provided by Patrick from NDepend team is interesting, but it is not good enough. Indeed, it returns the involved methods, but the caller graph is disconnected: enter image description here

So for this contrived example one can deduce the missing connection. But this luxury is unavailable in a production code that uses DI extensively. We will end up with many disconnected subgraphs. This would not be any helpful to trace the callers.

Community
  • 1
  • 1
mark
  • 59,016
  • 79
  • 296
  • 580
  • Closing advocates - any reasons? If you have a suggestion for another forum where to post my question - speak freely. – mark Jul 18 '16 at 11:23

1 Answers1

2

You can apply the answer of this question as-is.

// Retrieve the target method by name
let methodTarget = Methods.WithFullName("ConsoleApplication2.Program.BackToProgram()").Single()

// Build a ICodeMetric<IMethod,ushort> representing the depth of indirect
// call of the target method.
let indirectCallDepth = 
   methodTarget.ToEnumerable()
   .FillIterative(
       methods => methods.SelectMany(
          m => m.MethodsCallingMe.Union(m.OverriddensBase)))

from m in indirectCallDepth.DefinitionDomain
select new { m, callDepth = indirectCallDepth[m]  }

et voilà :)

NDepend indirect method interface call

Community
  • 1
  • 1
Patrick from NDepend team
  • 13,237
  • 6
  • 61
  • 92