如何防止在 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 命名空间中的方法中,您可能不会抛出)