如何防止c#类中的构造函数滥用

时间:2015-04-21 02:49:43

标签: c# asp.net asp.net-mvc dependency-injection inversion-of-control

我一直在尝试在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());

......打破了架构。如何防止以这种方式使用“新”?

4 个答案:

答案 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和联系程序集的情况下替换实现。