Обратите внимание на следующую тривиальную программу, использующую 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
с помощью внедрения зависимостей, поэтому поиск прямых и косвенных вызывающих ITest.DoSomething
дает мне следующее:
Итак, это дает мне полную картину - Program.BackToProgram
в конечном итоге вызывается из Program.Main
.
К сожалению, мне пришлось прибегнуть к проверке кода вручную, чтобы соединить точки. Внедрение зависимостей, похоже, нарушает способность NDepend отслеживать то, что вызывает, через границы DI.
Хотя это можно объяснить тем фактом, что DI в значительной степени полагается на отражение и что отражение на самом деле не поддается статическому анализу кода, тем не менее это создает большую проблему, потому что наш код довольно часто использует DI.
Итак, есть ли решение этой проблемы? Есть ли способ настроить NDepend для распознавания внедрения зависимостей, реализованного в MEF? В конце концов, когда меня спросят обо всех прямых и косвенных абонентах Program.BackToProgram
, я хочу видеть Program.Main
на графике без вмешательства человека.
Может, есть другой инструмент, который это делает?
ИЗМЕНИТЬ 1
Ответ, предоставленный Патриком из команды NDepend, интересен, но недостаточно хорош. Действительно, он возвращает задействованные методы, но граф вызывающих абонентов отключен:
Итак, для этого надуманного примера можно вывести отсутствующее соединение. Но эта роскошь недоступна в производственном коде, который широко использует DI. У нас будет много несвязных подграфов. Отслеживание звонков не поможет.