假设您有 3 层:UI、业务、数据。
如果业务层需要访问会话,这是否表明设计不佳?感觉有些不对劲。是否有任何专门针对 Web 应用程序需要遵循的准则?
我使用 c# 2.0 .net
假设您有 3 层:UI、业务、数据。
如果业务层需要访问会话,这是否表明设计不佳?感觉有些不对劲。是否有任何专门针对 Web 应用程序需要遵循的准则?
我使用 c# 2.0 .net
不。如果你有一个“控制器”层,你应该在那里访问它。从 Session 中获取您需要的内容并将其交给您的业务层。
叹。
广泛的共识将是不。业务层和控制器/Web 层应该以不同的方式维护,因为它们是独立的关注点。
事实是,您似乎将此标记为“纯粹与现实”的问题,这是非常短视且略显令人讨厌的问题。它也违背了提出问题的意义;如果您不考虑提出的意见,那为什么要征求他们的意见?
确实,预先更仔细地将事物分开需要更多的前期努力,更多的时间,最终可能会花费更多。确实,您可能无法从中感受到任何直接的好处。然而,几十年来大量程序员分享的大量啜泣故事表明,在可能的情况下,你所谓的“纯洁性”会在五年后减少痛苦;天哪;你真的必须认真地做一些重构,这并不令人愉快,因为你的责任正在渗透。
为 Web 应用程序设想层的更好方法可能是考虑表示、交互、业务规则和数据;从上到下。您的数据是数据库、数据访问等,业务规则对该数据实施任何额外的约束、处理验证、计算等。然后交互在表示层(基本上是您的用户界面)和业务逻辑之间分支,执行驱动您的应用程序的用例。
到目前为止,用户界面都是无关紧要的。用户是否在命令行应用程序中输入客户数据,或者使用存储在会话中的数据浏览一些多页 Web 表单并不重要。假设您选择后者;在上面粘贴一个网络前端。现在您关心的是编写相对简单的代码来处理检索请求的数据并将其呈现给用户。关键是,您的 Web 应用程序;前端,那个是您的整个用户界面;会议和所有。只有在您准备说“嘿,让我们将客户数据存入数据库”时,您才会去调用那些精心设计的服务层,传递您的 Web 应用程序获得的每一点信息藏匿;用户输入,进行更改的用户的姓名;所有这些废话。您的服务层会处理它。或者,或者,婊子,因为你忘记了必填字段。
因为您已经将事物清晰地分离出来,所以您的应用程序的核心可以像其他人建议的那样被改造(或“借用”)以在任何其他应用程序中使用,并且服务层保持无状态、干净并准备好处理事物。它会进行您的验证,因此您的验证在任何地方都是一致的。但它不知道谁登录了 Web 前端、控制台应用程序或在终端上运行的花哨的富客户端应用程序,它也不在乎,因为这些细节只对那些应用程序很重要。
需要添加新的验证规则?没问题; 让服务层进行验证,并根据需要在链的更高层处理用户界面问题。需要改变计算方式吗?在业务层进行更改。没有其他东西需要受到影响。
我闻起来很有趣。也许您需要一个表示层来管理会话和任何其他状态信息?
我认为业务逻辑不应该与表示选择绑定,如果 Session 位于该层,它将被绑定。
我认为 Session 的不必要使用通常是一种代码异味,通常查询字符串、cookie 和视图状态更轻量级并且具有更好的“范围”
至于 Sessions 在业务层中的角色,这取决于您目前正在阅读的架构大师。如果业务逻辑层是代码独立于 UI 的地方,那么 Session 就不是引入业务层的东西。
例如,在控制台应用程序、ASP.NET Web 应用程序、Windows 服务和 Windows 窗体应用程序中,只有 ASP.NET 具有会话。
也就是说,能够支持多个 UI 是一个被高度高估的功能,并且不需要完美的远见来估计您是否会将您的应用程序移植到不同的用户界面。如果您非常确信您的逻辑从现在到永远只会在 ASP.NET 应用程序中运行,那么没关系。
一个例外是单元测试。nUnit 测试运行器构成不同的 UI,很难模拟请求、响应、会话、应用程序等。
我的感觉是业务层是唯一放置会话数据访问的地方,因为它确实是您逻辑的一部分。如果您将其烘焙到表示层中,那么如果您更改该数据的存储位置(假设您不再想使用会话状态),那么进行更改会更加困难。
我要做的是对于所有会话状态的数据,我会创建一个类来封装对该数据的检索。这将允许未来的变化。
编辑:UI 应该只用于执行以下操作: 1. 调用业务层。2. 与用户交互(消息和事件)。3. 操作屏幕上的视觉对象。
我听说人们将会话数据访问视为数据层的一部分,这是语义上的,取决于您对数据层的看法。
如果您保留列出的三层,“业务”层可能最适合处理 Session 对象。
由于会话更多地与将应用程序绑定在一起的实际框架有关,而不是与任何实际业务逻辑有关,因此您可能希望创建一个控制层,将数据从 Session 对象转换为业务数据输入到业务层。
我认为这取决于使用情况,但是是的,我们一直从业务层访问会话。这里也有“纯粹与现实”的争论。
例如,在我们的应用程序中,每个客户端都有一个数据库,但只有一个代码库。因此,我们在会话中为每个用户提供了客户端信息。当然,我们可以将这些数据从 Web 层的会话中提取出来,然后将其传递给业务层。当然,业务层甚至不关心它,但是数据层需要它来连接正确的数据库。因此,业务层需要将其传递给数据层。似乎有很多参数传递没有很好的商业理由。在我们的例子中,我们的数据层检查连接字符串的会话对象并从那里运行。如果它不是 Web 应用程序(并且我们有 Windows 服务和 .exe 帮助应用程序),我们围绕没有会话的问题进行了编码,如下所示:
protected virtual string GetConnectionString()
{
string connectionString;
string connectionStringSource;
//In app.config?
if (ConfigurationManager.AppSettings[_ConnectionStringName] != null &&
ConfigurationManager.AppSettings[_ConnectionStringName] != "")
{
connectionString = ConfigurationManager.AppSettings[_ConnectionStringName];
connectionStringSource = "Config settings";
}
//Nope? Check Session
else if (null != HttpContext.Current && null != HttpContext.Current.Session &&
null != HttpContext.Current.Session[_ConnectionStringName])
{
connectionString = (string)HttpContext.Current.Session[_ConnectionStringName];
connectionStringSource = "Session";
}
//Nope? Check Thread
else if (null != System.Threading.Thread.GetData(
System.Threading.Thread.GetNamedDataSlot(_ConnectionStringName)))
{
connectionString = (string)System.Threading.Thread.GetData(
System.Threading.Thread.GetNamedDataSlot(_ConnectionStringName));
connectionStringSource = "ThreadLocal";
}
else
{
throw new ApplicationException("Can't find a connection string");
}
if (debugLogging)
log.DebugFormat("Connection String '{0}' found in {1}", connectionString,
connectionStringSource);
return connectionString;
}
从业务层访问会话绝对是代码异味。
如果您需要按所述“切换”业务层,则您的表示层或控制器只需要担心会话变量是什么。
例如,假设您想将一组对象用于一个会话变量,而将另一组对象用于另一个变量。您的控制器可以调用服务层并传入变量。然后服务层将返回适当的业务对象。
您的业务层不知道任何关于会话的事情,就像您的表示层不知道您如何连接到数据库一样。
会话对象与特定的 UI 实现相关联,因此让您的业务层因会话而死是一种味道。
另外,您可能应该能够对您的业务层进行单元测试——如果会话存在依赖关系,您将如何做到这一点?