NDepend и Dependency Injection - как соединить точки?
Обратите внимание на следующую тривиальную программу, использующую MEF в качестве основы для внедрения зависимостей:
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()
{
}
}
}
Теперь давайте проанализируем это с помощью NDepend 6.3. Предположим, я хочу знать всех прямых и косвенных абонентов Program.BackToProgram
:
Тем не менее, класс TestClient
потребляет Test
экземпляр через ITest
интерфейс с использованием Dependency Injection, поэтому ищем прямых и косвенных абонентов ITest.DoSomething
дает мне это:
Итак, это дает мне полную картину - Program.BackToProgram
в конечном итоге вызывается из Program.Main
,
К сожалению, мне пришлось прибегнуть к ручной проверке кода, чтобы соединить точки. Внедрение зависимостей, кажется, нарушает способность NDepend отслеживать то, что вызывает то, что выходит за границы DI.
Хотя это можно объяснить тем фактом, что DI в значительной степени зависит от рефлексии и что рефлексия на самом деле не поддается статическому анализу кода, тем не менее это создает большую проблему, поскольку наш код использует DI довольно часто.
Итак, есть ли решение этой проблемы? Есть ли способ настроить NDepend для распознавания внедрения зависимостей, реализованного MEF? В конце дня, когда попросят всех прямых и косвенных абонентов Program.BackToProgram
Я хочу увидеть Program.Main
на графике без вмешательства человека.
Может быть, есть другой инструмент, который это делает?
РЕДАКТИРОВАТЬ 1
Ответ Patrick from NDepend team интересен, но он недостаточно хорош. Действительно, он возвращает задействованные методы, но граф вызывающих абонентов отключен:
Таким образом, для этого надуманного примера можно вывести недостающее соединение. Но эта роскошь недоступна в производственном коде, который широко использует DI. В итоге у нас будет много отключенных подграфов. Это не поможет отследить звонящих.
1 ответ
Вы можете применить ответ на этот вопрос как есть.
// 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] }
и вуаля:)