我一直在尝试在asp.net MVC5应用程序中实现松散耦合的应用程序。我有一个控制器:
public class HeaderController : Controller
{
private IMenuService _menuService;
public HeaderController(IMenuService menuService)
{
this._menuService = menuService;
}
//
// GET: /Header/
public ActionResult Index()
{
return View();
}
public ActionResult GetMenu()
{
MenuItem menu = this._menuService.GetMenu();
return View("Menu", menu);
}
}
此控制器中使用的服务是:
public class MenuService : IMenuService
{
private IMenuRespository _menuRepository;
public MenuService(IMenuRespository menuRepository)
{
this._menuRepository = menuRepository;
}
public MenuItem GetMenu()
{
return this._menuRepository.GetMenu();
}
}
服务类中使用的存储库是:
public class MenuRepository : IMenuRespository
{
public MenuItem GetMenu()
{
//return the menu items
}
}
用于服务和存储库的接口是这样的:
public interface IMenuService
{
MenuItem GetMenu();
}
public interface IMenuRespository
{
MenuItem GetMenu();
}
HeaderController
的构造函数使用构造函数注入接收MenuService
,并且我将ninject作为DI容器来处理它。
一切都很好 - 除了在我的控制器中,我仍然可以这样做:
MenuItem menu = new MenuService(new MenuRepository());
......打破了架构。如何防止以这种方式使用“新”?
答案 0 :(得分:7)
这样做的一种方法是将您的接口和实现移动到单独的Visual Studio项目/程序集中,并仅引用实际需要它的项目中的实现项目 - 其他所有内容都可以引用您的{的接口项目{1}} - 此时代码可以使用接口,但实际上并不是新实现的任何实现。
然后,您可以在依赖项中的DI处引用实施项目。
WebApp解决方案:
WebApp Proj(控制器等) - >服务接口项目
服务Impl项目 - >服务接口项目
即便如此,这是一个很好的方法,它并非一丝不苟 - 另一个组件是教育和代码审查,以便为您的团队提供最佳实践,例如可测试性和依赖注入。 / p>
答案 1 :(得分:4)
我假设手动实例化对象的部分问题可能与大型团队一起工作,因此一些成员正在以错误的方式使用构造函数注入技术。如果是这种情况,我几乎通过教育他们在框架上解决了大部分问题。偶尔,你会发现某人以错误的方式做事,但并不常见。另一种方法是在控制器构造函数上添加[EditorBrowsable(EditorBrowsableState.Never)]
属性。构造函数将从intellisense中消失;好吧,它似乎已经消失了。但是,它仍然可以使用。
您可以将实现分解为另一个DLL而不是MVC项目直接引用(隐式引用),因此没有直接引用,您不能直接使用这些类型。通过每个项目引用的一个项目中的接口和间接引用的实现的项目,因此仅包括接口。如果您正在进行单元测试,我建议在单元测试项目中包含直接参考,以提高测试覆盖率。
答案 2 :(得分:3)
一些潜在的选择(我从未尝试过,但可能会有一些支持):
如果在代码中使用了构造函数,你可能会写一个FXCop规则会出错。
您可以将构造函数标记为过时,如果在代码中使用过时的方法,则构建服务器会失败。
如果DI容器通过反射使用它,这应该都可以(尽管在FXCop的情况下,如果它在NInject命名空间中的方法中,你可能不会抛出)
答案 3 :(得分:2)
作为一般设计原则,接口(合同)应该在一个组件中,并且实现应该在另一个组件中。 Contracts程序集应该在MVC项目中引用,并且实现的程序集应该复制在" bin"夹。比使用" Dynamic Module Loading"加载类型。通过这种方式,您将避免上述问题,这是更广泛的解决方案。因为您可以在不构建UI和联系程序集的情况下替换实现。