如何防止在 C# 中滥用构造函数 class
How to prevent constructor misuse in c# class
我一直在尝试在 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();
}
}
服务 class 中使用的存储库是:
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());
...这破坏了体系结构。如何防止以这种方式使用 'new'?
我假设手动实例化对象的部分问题可能来自与大型团队合作,其中一些成员以错误的方式使用构造函数注入技术。如果是这样的话,我发现通过对他们进行框架方面的教育几乎可以解决大部分问题。偶尔,您会发现有人以错误的方式做事,但并不常见。另一种选择是在控制器构造函数上添加一个 [EditorBrowsable(EditorBrowsableState.Never)]
属性。构造函数将从智能感知中消失;好吧,它似乎已经消失了。但是,它仍然可以使用。
您可以将实现分解为 MVC 项目不直接引用(隐式引用)的另一个 DLL,因此由于没有直接引用,您不能直接使用这些类型。对于每个项目引用的一个项目中的接口,以及间接引用实现的项目,因此将仅包含接口。如果您正在进行单元测试,我建议在单元测试项目中包含一个直接引用,以提高测试覆盖率。
一种方法是将您的接口和实现移动到单独的 Visual Studio 项目/程序集中,并且只在实际需要它的项目中引用实现项目 - 其他一切都可以引用IMenuService
的接口项目 - 此时代码可以使用接口,但实际上并没有新建任何实现本身。
然后您可以在依赖项中的任何地方引用实施项目。
WebApp Solution:
WebApp Proj (Controllers etc.) --> Service Interface Proj
Service Impl Project --> Service Interface Proj
尽管如此,这是一个很好的方法,但它绝不是万无一失的 - 另一个组成部分是教育和代码审查,以提出适合您的团队的最佳实践,例如可测试性和依赖注入。
作为一般设计原则,接口(合同)应该在一个程序集中,而实现应该在另一个程序集中。 Contracts 程序集应在 MVC 项目中引用,并且已实现的程序集应复制到 "bin" 文件夹中。比使用“Dynamic Module Loading”来加载类型。这样您将避免上述问题,这是更广泛的解决方案。因为您可以在不构建 UI 和 Contact Assemblies 的情况下替换实现。
几个潜在的选择(我从未尝试过,但可能有一些好处):
您可以编写一个 FXCop 规则,如果在代码中使用构造函数,该规则会出错。
您可以将构造函数标记为过时,如果您在代码中使用过时的方法,构建服务器将失败。
如果 DI 容器通过反射使用它,这应该没问题(尽管在 FXCop 的情况下,如果它在 NInject 命名空间中的方法中,您可能不会抛出)
我一直在尝试在 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();
}
}
服务 class 中使用的存储库是:
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());
...这破坏了体系结构。如何防止以这种方式使用 'new'?
我假设手动实例化对象的部分问题可能来自与大型团队合作,其中一些成员以错误的方式使用构造函数注入技术。如果是这样的话,我发现通过对他们进行框架方面的教育几乎可以解决大部分问题。偶尔,您会发现有人以错误的方式做事,但并不常见。另一种选择是在控制器构造函数上添加一个 [EditorBrowsable(EditorBrowsableState.Never)]
属性。构造函数将从智能感知中消失;好吧,它似乎已经消失了。但是,它仍然可以使用。
您可以将实现分解为 MVC 项目不直接引用(隐式引用)的另一个 DLL,因此由于没有直接引用,您不能直接使用这些类型。对于每个项目引用的一个项目中的接口,以及间接引用实现的项目,因此将仅包含接口。如果您正在进行单元测试,我建议在单元测试项目中包含一个直接引用,以提高测试覆盖率。
一种方法是将您的接口和实现移动到单独的 Visual Studio 项目/程序集中,并且只在实际需要它的项目中引用实现项目 - 其他一切都可以引用IMenuService
的接口项目 - 此时代码可以使用接口,但实际上并没有新建任何实现本身。
然后您可以在依赖项中的任何地方引用实施项目。
WebApp Solution:
WebApp Proj (Controllers etc.) --> Service Interface Proj
Service Impl Project --> Service Interface Proj
尽管如此,这是一个很好的方法,但它绝不是万无一失的 - 另一个组成部分是教育和代码审查,以提出适合您的团队的最佳实践,例如可测试性和依赖注入。
作为一般设计原则,接口(合同)应该在一个程序集中,而实现应该在另一个程序集中。 Contracts 程序集应在 MVC 项目中引用,并且已实现的程序集应复制到 "bin" 文件夹中。比使用“Dynamic Module Loading”来加载类型。这样您将避免上述问题,这是更广泛的解决方案。因为您可以在不构建 UI 和 Contact Assemblies 的情况下替换实现。
几个潜在的选择(我从未尝试过,但可能有一些好处):
您可以编写一个 FXCop 规则,如果在代码中使用构造函数,该规则会出错。
您可以将构造函数标记为过时,如果您在代码中使用过时的方法,构建服务器将失败。
如果 DI 容器通过反射使用它,这应该没问题(尽管在 FXCop 的情况下,如果它在 NInject 命名空间中的方法中,您可能不会抛出)