【问题标题】:C# Architectural Design Short-comingsC#架构设计的缺点
【发布时间】:2015-06-28 17:29:51
【问题描述】:

我在 C# 中使用了一个基于层概念的代码框架:

  • 在底层,我们有数据层(数据库、远程服务等)。
  • 正上方是访问层(与数据层交互的代码)。
  • 正上方是持久层。 (用于在应用程序逻辑和数据层要求之间转换和转换数据的代码,以及处理方法调用的任何复合到复杂对象图和操作的访问。)
  • 最后是流程层(带有通用应用程序逻辑的代码。)

每一层只与它上面或下面的层通信。所以一般的申请流程是

Processor -> Peristence -> Access -> Data
Data -> Access -> Persistence -> Processor

一般概念是,处理器充当包含 1 个或多个步骤的单个进程的一般表示。它由执行代码独立调用。但是一个过程很容易成为另一个更大过程的一个步骤。

我遇到的问题(或真正的潜在问题)是,当我有一个包含 1 个或多个子进程的进程,或者该进程使用持久性并且它有任何使用持久层的子进程时,我无法打开单个数据库连接供两个进程使用。每个进程都会实例化并打开自己的连接。例如,这里是一个实际的代码sn -p:

var contactProcessor = new SelectContactProcessor();
var contact = contactProcessor.GetContact(contactId);
if (contact == null)
    throw new Exception("Unable to locate contact");
viewModel.DisplayName = contact.DisplayName;
viewModel.City = contact.City;
viewModel.State = contact.State;

var processor = new PresentCurrentSurveyProcessor();
var data = processor.GetSurveyData();
if (data == null)
   throw new Exception("Unable to locate survey");
viewModel.SurveyId = data.ID;
viewModel.SurveyName = data.SurveyName;
viewModel.StartDate = data.StartDate.ToShortDateString();
viewModel.EndDate = data.EndDate.ToShortDateString();
viewModel.Questions = data.Questions;
return viewModel;

所以在本例中,SelectContactProcessor 将实例化一个将连接到 Db 的 Peristence 层,而 PersentCurrentSurveyProcessor 还将实例化一个不同的持久性对象,该对象也将连接到同一个 Db。但是由于这种架构,它们不会共享连接并且无法以事务方式执行(如果需要)。

虽然我有一些想法,但我很想知道其他人可能会看到哪些我会忽略的解决方案。我不想最终打破我关于层只与它们的直接邻居交互的规则。

【问题讨论】:

  • 创建一个 TransactionContext 对象,该对象可以选择性地传递到您的持久层调用中。该对象将包含使用现有连接所需的任何信息。如果传入,持久层将使用它,如果没有,持久层将创建一个并将其传回以供以后使用。为了在访问层中保留连接详细信息,这可以像访问层可以用来查找现有事务的令牌一样简单。添加一些记账代码,但让您的处理层不关心访问层。
  • 您可以根据需要将它添加到需要它的持久性对象中。不需要进行“改变一切”更新。
  • 嗯,首先,您必须了解创建两个连接并不是一件坏事,因为 .NET 使用连接池。事务问题可以通过加入 DTC 事务来解决,但这不会像本地事务那样执行。关键是,它可以支持具有多个连接的事务,但效率不会那么高。如果您关心的是效率,那么您将不得不为此制定一个横切关注点。
  • @Goblyn27 - 不,这是池的重点,它使连接保持打开状态。
  • @Goblyn27 提到 TransactionContext 是指 UnitOfWork 模式吗?

标签: c# architecture


【解决方案1】:

我将在这里稍微扩展一下我的评论。我的建议(以及@Goblyn27 显然建议的)是添加一个对象,该对象将包含一个打开连接的实例以及您需要使用它的方法(例如启动/提交事务)。

在我开发的 Web 应用程序中,我通常将此类对象放入 DI 容器中,因为每个请求只需要一个实例。然后由 DI 将其注入到任何需要它的服务中。

此对象还控制嵌套事务的使用(许多 DB 不支持,因此我保留了一组 startTransaction() 调用,由每个 'commit()' 弹出,并且仅在此堆栈为空时才实际提交。

您的应用程序略有不同,但您可以尝试使用相同的方式。

不过,我建议使用一些简单的 DI 容器,而不是传递全局变量。

【讨论】:

    猜你喜欢
    • 2011-02-19
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多