我最近在我的代码中遇到了以下错误,这使我无法进行调试。我想基于它的接口注入一个实例,如下所示:
MovementController(IMotorController motorController)
但是我不小心使用了这样的具体类型:
MovementController(MotorController motorController)
项目仍然构建并运行正常,直到我尝试从motorController
实例访问MovementController
。由于IMotorController
的底层实现访问硬件,因此它必须是单例或我的锁定代码。但是,由于我有其他类注入IMotorController
,我现在在对象图中有两个实例MotorController
,它们都通过串行连接访问硬件。这导致了一个错误,在运行时处于更低的水平,这让我永远调试并找到了真正的原因。
如何避免这种类型的错误或为我的StructureMap注册表编写单元测试以捕获这个微妙的错误?
答案 0 :(得分:1)
您可以使用NDepend等静态分析工具轻松检查。有了它,您只需查找控制器类型,然后检查它们的构造函数,并警告您是否发现任何非接口类型的构造函数参数。
为了完善史蒂夫的答案,您可以编写一个看起来像的代码规则:(使用NDepend代码规则为C# LINQ query,前缀为warnif count > 0
)
// <Name>Don't use MotorController, use IMotorController instead</Name>
warnif count > 0
from m in Application.Methods
where m.IsUsing ("NamespaceA.MotorController ") &&
m.ParentType.FullName != "NamespaceB.ClassThatCanUseMotorController "
select m
如果ClassThatCanUseMotorController
为零或很多,则可以轻松改进规则。
答案 1 :(得分:0)
确定。因此,我为单元测试提出的解决方案是获取实现IMotorController
的所有实例,并声明其计数等于1
:
var motorControllerInstances = container.GetAllInstances<IMotorController>().Select(x => x); // cast enumerable to List using Linq
Assert.True(motorControllerInstances.Count == 1);
不确定这是最优雅的方式,但似乎有效。
更新1: 这段代码不抓住了我的错误。我仍在寻找问题的正确答案。
更新2:我越来越近了。如果您不小心注册具体类型的相应界面,这至少会捕获。但是,它似乎没有检查它的实例是否实际构建。
var allInterfaceInstances = dicFixture.result.Model.GetAllPossible<IMotorController>();
Assert.True(allInterfaceInstance.Count() == 1);
答案 2 :(得分:0)
最安全的解决方案是在运行时检查只创建了MotorController
的一个实例。例如,您可以使用静态计数器变量计算MotorController
的实例数:
public class MotorController : IMotorController
{
private static bool instantiated;
public MotorController(...)
{
if (instantiated)
throw new InvalidOperationException(
"MotorController can only be instantiated once.")
...
instantiated = true;
}
...
}
我通常会考虑这个糟糕的设计,因为一个类是否被用作单例是只有依赖注入框架应该关注的东西。另请注意,这不是线程安全的。
答案 3 :(得分:0)
在 SOLID
中尝试遵守 D依赖倒置原则在哪里 一个应该“取决于抽象。不要依赖于结核
一个项目。在这种情况下,Asp.Net-MVC5,我想要一种方法来确保所有控制器(MVC和WebAPI2)遵循这种模式,它们不依赖于具体结构。
最初的想法来自我读过的一篇文章,其中创建了一个单元测试来扫描所有控制器,以确保它们已经定义了明确的授权。我在检查所有控制器都有依赖于抽象的构造函数时应用了类似的思想。
[TestClass]
public class ControllerDependencyTests : ControllerUnitTests {
[TestMethod]
public void All_Controllers_Should_Depend_Upon_Abstractions() {
var controllers = UnitTestHelper.GetAssemblySources() //note this is custom code to get the assemblies to reflect.
.SelectMany(assembly => assembly.GetTypes())
.Where(t => typeof(IController).IsAssignableFrom(t) || typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(t));
var constructors = controllers
.SelectMany(type => type.GetConstructors())
.Where(constructor => {
var parameters = constructor.GetParameters();
var result = constructor.IsPublic
&& parameters.Length > 0
&& parameters.Any(arg => arg.ParameterType.IsClass && !arg.ParameterType.IsAbstract);
return result;
});
// produce a test failure error mssage if any controllers are uncovered
if (constructors.Any()) {
var errorStrings = constructors
.Select(c => {
var parameters = string.Join(", ", c.GetParameters().Select(p => string.Format("{0} {1}", p.ParameterType.Name, p.Name)));
var ctor = string.Format("{0}({1})", c.DeclaringType.Name, parameters);
return ctor;
}).Distinct();
Assert.Fail(String.Format("\nType depends on concretion instead of its abstraction.\n{0} Found :\n{1}",
errorStrings.Count(),
String.Join(Environment.NewLine, errorStrings)));
}
}
}
所以给出以下例子。 (注意我将其改编为MVC)
public class MovementController : Controller {
public MovementController(MotorController motorController) {
//...
}
}
public interface IMotorController {
//...
}
public class MotorController : IMotorController {
//...
}
单元测试将失败...
Result Message: Assert.Fail failed.
Type depends on concretion instead of its abstraction.
1 Found :
MovementController(MotorController motorController)
这对我有用,因为我有IController
和ApiController
寻找的常见类型。
测试还有改进的余地,但应该是一个很好的起点。