请使用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
Patrick from NDepend team提供的答案很有意思,但还不够好。实际上,它返回所涉及的方法,但是调用者图是断开的:
因此,对于这个人为的例子,可以推断出缺失的连接。但是这种奢侈品在广泛使用DI的生产代码中是不可用的。我们最终会得到很多不连贯的子图。这对跟踪呼叫者没有任何帮助。
答案 0 :(得分:2)
您可以按原样应用this question的答案。
// 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] }
etvoilà:)