【问题标题】:Dependency Injection - Layered Application依赖注入 - 分层应用
【发布时间】:2012-04-15 08:59:49
【问题描述】:

我希望在新解决方案中使用 IoC 容器,例如 StructureMap。解决方案如下:

  • 桌面.UI
  • Desktop.Models
  • 桌面视图
  • Desktop.Presenters
  • 业务逻辑
  • 业务对象
  • 数据访问

Desktop.Models、BusinessLogic 和 DataAccess 都引用了 BusinessObjects。

Desktop.UI 项目将是配置 DI 的入口点,但我并不想仅为 DI 添加对 DataAccess 的引用。

我在 SO 上看到了一些其他问题,一个答案是为 IoC 创建一个单独的项目,该项目引用相关项目,然后从 Desktop.UI 引用 IoC 项目。但是 Desktop.UI 需要 DI 配置来实例化视图的演示者,因此不确定单独的项目是否可以工作。

我已经看到了一些定义注册表类和使用 StructureMap 扫描功能自动检测注册表类的基本示例。我的第一个想法是将这些 Registry 类放在他们正在配置的项目中,但这会导致所有项目都必须引用 StructureMap。

在分层应用程序中设置 DI 的推荐方法是什么?

【问题讨论】:

  • 如果我是你,我会创建一个框架项目来存储单例 IoC 并在其中包含引导代码。
  • @jameslewis 这将如何与 UI 项目一起使用,因为 UI 项目需要 DI 供演示者使用,所以如果 IoC 位于 UI 引用的单独项目中,那么 IoC 项目也不能引用 UI?跨度>

标签: .net dependency-injection inversion-of-control


【解决方案1】:

根据我的经验,我发现我在整个服务器端代码和客户端代码中都使用了一种依赖注入形式。例如,我最近从事的 [sigh] WinForms 项目在服务器端使用 Unity(业务逻辑、数据访问)和在客户端使用智能客户端软件工厂(用于 Winforms 的 MVP 框架,包括它自己的 DI 形式)。因此,在您的示例中,我想说,如果您在尝试让 DI 框架跨应用程序的所有层工作时遇到困难,那是因为您不应该这样做。您可能遇到过一种情况,您希望有两种形式的 DI,一种用于客户端,一种用于服务器。

除此之外,我认为大多数 DI 框架允许您在配置文件中引导您的依赖项是正确的。配置文件告诉框架您的依赖项的所有信息,包括它们所在的程序集。这意味着您不需要在实例化 IoC 容器的项目中引用任何实际项目。因此,在我的框架项目示例中,您将有一个单例实例化您的 IoC 容器并从配置文件中读取所有依赖项。

这里有一些链接:

希望对你有所帮助!

问候,

詹姆斯

【讨论】:

  • 感谢您的回复。我的桌面客户端、业务逻辑和数据访问项目都部署在这个解决方案中,所以仍然可以使用流利的语法而不是配置文件单独配置它们,或者如果我不想配置文件是要走的路从 UI 引用数据访问?
  • 我会使用配置文件。就个人而言,我们编写了一个更“智能”的引导程序,它使用反射来解决依赖关系,所以我有一段时间没有使用 config 方法(而且我们在引用程序集方面也没有问题)。但我想说,解耦一切是配置选项的作用,所以在你的情况下,这听起来就是要走的路。
  • @MattF 我绝对不会使用配置文件。维护开销太大。此外,它更容易出错。 martinfowler.com/articles/…看看我的回答。
【解决方案2】:

对于 WindowsForms,我有一个单独的项目来处理我的 IoC 内容。我称之为CompositionRoot。

CompositionRoot(启动项目)的入口点:

public static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        BootsTrapper.Boot();

        using (var mainForm = ObjectFactory.GetInstance<IPresenter<IMainView>>())
        {
            Application.Run((Form)mainForm.CurrentView);
        }
    }
}

BootsTrapper 负责注册,是CompositionRoot 的一部分。

MainViewPresenter 显示第一个表单。 MainViewPresenter 可以使用包含抽象 Presenter 工厂的外观服务IMainViewPresenterFacade 来显示其他表单。

public class MainViewPresenter : Presenter<IMainView>
{
    readonly IArticleRepository _articlesRepository;
    readonly IMainViewPresenterFacade _presenterFactory;
    readonly IUnitOfWork _unitOfWork;

    public MainViewPresenter(IMainView currentView, IArticleRepository articlesRepository, IUnitOfWork unitOfWork,
                             IMainViewPresenterFacade presenterFactory)
        : base(currentView, unitOfWork)
    {
        _articlesRepository = articlesRepository;
        _unitOfWork = unitOfWork;
        _presenterFactory = presenterFactory;

        Ensure.That(articlesRepository).IsNotNull();
        Ensure.That(presenterFactory).IsNotNull();

        CurrentView.DetailsClick += View_DetailsClick;
        CurrentView.CloseClick += ViewCloseClick;
        CurrentView.CreateClick += View_CreateClick;
        CurrentView.DeleteClick += View_DeleteClick;

        CurrentView.BindModel(_articlesRepository.GetAll().Select(x => new ArticleViewModel { Id = x.ArticleId, Name = x.Description }));
    }

    public override void Dispose()
    {
        base.Dispose();

        CurrentView.DetailsClick -= View_DetailsClick;
        CurrentView.CloseClick -= ViewCloseClick;
        CurrentView.CreateClick -= View_CreateClick;
        CurrentView.DeleteClick -= View_DeleteClick;
    }

    void View_DeleteClick(object sender, EventArgs e)
    {
        var selectedArticle = CurrentView.GetSelectedArticle();
        var article = _articlesRepository.GetById(selectedArticle.Id);
        _articlesRepository.Delete(article);
        _unitOfWork.Commit();
    }

    void View_CreateClick(object sender, EventArgs e)
    {
        using (var createPresenter = _presenterFactory.CreateCreatePresenter())
        {
            ShowDialog(createPresenter.CurrentView, CurrentView);
        }
    }

    void ViewCloseClick(object sender, EventArgs e)
    {
        CurrentView.Close();
    }

    void View_DetailsClick(object sender, EventArgs eventArgs)
    {
        var article = CurrentView.GetSelectedArticle();

        if (article == null) return;

        using (var detailPresenter = _presenterFactory.CreateDetailPresenter(article))
        {
            ShowDialog(detailPresenter.CurrentView, CurrentView);
        }
    }
}

看看我的SimpleMVP 项目或这个问题Dependency Injection and project structure for Console applications 并随时提出更多问题:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多