依赖注入和程序集引用

3
我有一个希望得到满意答案的基本问题,涉及MVC应用程序中的依赖注入。我的应用程序包括一个MVC 3项目、一个服务层类库(SL)和一个数据访问类库(DAL)。SL和DAL都包含它们各自的接口和实现。MVC项目引用了SL和DAL项目。MVC项目将创建一个DAL的实现,并将其注入到接受DAL接口的SL构造函数中。
我关心的是,为了使SL接受DAL接口作为参数,它还需要引用DAL项目(其中接口和实现都存在),这似乎违反了DI,因为现在MVC和SL项目都需要引用DAL程序集。
长话短说,将DAL接口移动到自己的项目中是否更有意义,这样SL只需要引用接口项目而不是实现项目?MVC项目显然需要引用接口和实现项目,而DAL也需要引用接口项目。尽管这会向我的解决方案添加另一个项目,但这似乎是一种更清晰的做法,这并不让我感到困扰。我还看到有人建议将DAL接口存储在SL中,并让DAL引用SL,但这对我来说似乎不正确。任何见解或建议都将不胜感激。谢谢!
4个回答

5

谢谢您的快速回复。我需要花些时间查看Jeff的代码示例,以理解洋葱概念。 - Ryan
看起来洋葱概念的要点是有一个包含接口和可能是DTO类等内容的核心程序集,该程序集由MVC应用程序和一个单独的基础设施程序集共享,后者包含数据访问或电子邮件功能。这个解释合理吗? - Ryan
概念是将业务对象放在洋葱的内部。应用程序服务将位于第一层外部,取决于这些对象。您还可以在同一级别上拥有基础架构接口,以便应用程序服务可以依赖它们。在边缘上,您将拥有UI组件和基础架构/DTO组件。基本上,您可以更改UI或基础架构代码,而不影响核心应用程序对象/服务。 - Dmitry S.

2

真正的组件必须将它们的接口和实现分别放在不同的程序集中。

这样可以像插件一样动态加载适当的实现。通常这也是避免循环引用的唯一方法。例如,数据访问层需要了解模型(业务类),而模型可能希望通过数据访问层调用来惰性加载一个依赖模型,从而创建了一个循环引用(.NET禁止循环程序集引用)。如果接口在单独的程序集中,则数据访问层和模型程序集都引用了这两个接口程序集,并通过构造函数注入了依赖项。

// Assembly: Model contracts

public interface IModelA
{
    IModelB ModelB { get; }
    ...
}

public interface IModelB
{
    ...
}

public interface IModelFactory
{
    IModelA CreateModelA();
    IModelB CreateModelB();
}

// Assembly: DAL contracts, references Model contracts

public interface IDAL
{
    IModelA LoadA(int id);
    IModelB LoadB(int id);
}

// Assembly: Model implementation, references Model and DAL contracts

public class ModelA : IModelA
{
    private IDAL _dal;

    public ModelA (IDAL dal)
    {
        _dal = dal;
    }

    private IModelB _modelB;
    public IModelB ModelB
    {
        get {
            if (_modelB == null) {
                _modelB = _dal.LoadB(5);
            }
            return _modelB;
        }
    }
}

// Assembly: DAL implementation, references Model and DAL contracts

public class DAL : IDAL
{
    private IModelFactory _modelFactory;

    public DAL(IModelFactory _modelFactory)
    {
        _modelFactory = modelFactory;
    }

    public IModelA LoadA(int id)
    {
        IModelA modelA = _modelFactory.CreateModelA();
        // fill modelA with data from database
        return modelA;
    }

    public IModelB LoadB(int id)
    {
        IModelB modelB = _modelFactory.CreateModelB();
        // fill modelB with data from database
        return modelB;
    }
}

1

根据规则,只要您依赖于接口而不是具体实现,就可以了。因此,您可以将接口放在单独的程序集中,并在需要的任何地方引用它们。


1

引用包含具体类型的程序集可能会成为一个问题,但这取决于您想要实现什么。通过让服务仅依赖于抽象,您可以实现松耦合的代码。这提高了可维护性。

让您的 BL 程序集仅引用包含抽象(而不是实现)的程序集,可以使您独立部署软件的部分。例如,您想将软件部署到不同的客户端,并且您有多个 DA 层的变体,但不想将所有 DA 变体都部署到所有客户端(例如因为他们需要按 DA 层付费,或者您试图保护您的知识产权)。

因此,如果分离对于部署不是一个问题,您就不必创建单独的程序集。


感谢所有回复的人。我认为将接口放在单独的程序集中是我现在要走的方向,因为它似乎可以使依赖注入更加清晰,并解决我的最初问题。我肯定对洋葱概念(感谢Dmitry)很感兴趣,但我不确定自己是否已经准备好深入研究它,因为我对依赖注入还比较新,希望在进入看起来稍微高级一些的概念之前先掌握基础知识。 - Ryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接