技术开发 频道

基于ASP.NET 3.5与N层架构开发企业SNS

  【IT168 专稿】在完成数据库和数据访问层的构建后,将着手博客系统业务逻辑层的开发,本篇将从这里开始。正如以前提到的,业务逻辑层的目的是为视图层提供操作,而它自己使用数据访问层实现与数据库的通讯。

  业务逻辑层设计

  事实上,在开发数据访问层时,我们已经实现了部分的业务逻辑层,即业务对象。这些业务对象实质上起着连接业务逻辑层和数据访问层的作用。然后,业务对象层实现的第二部分便是构建业务对象管理器。

  业务对象管理器的任务就是负责管理业务对象的实例。业务对象管理器必须支持的操作在很大程度上依赖于具体应用程序系统的业务逻辑。从最小意义上分析,它们必须提供如下一些类似于在数据访问层中定义的操作:

  ? GetItem—把一个业务对象返回给视图层。

  ? GetList—把一个业务对象列表返回给视图层。

  ? Save—从视图层把一个业务对象保存到数据库。

  ? Delete—从数据库删除一个业务对象。

  切记,业务对象管理器并不直接与数据库交互,而是使用我们以前在数据访问层所定义的操作。这使得业务对象管理器的实现非常容易—只需要调用数据访问层即可。

  下面,让我们来观察一下与评论相关的业务对象管理器CommentManager的具体编码(定义于路径Code/Business下的文件Comment.cs中)。

public static int Count(int StartRow, int PageSize)
{
    return CommentDB.Count(StartRow, PageSize);
}

  上述Count方法调用数据访问层对象(CommentDB)的方法以检索数据库中评论信息总数,并返回这个数字。

[DataObjectMethod(DataObjectMethodType.Select, true)]
public static List<Comment> GetList()
{
    return CommentDB.GetList();
}

  上述GetList方法调用数据访问层以检索一个业务对象Comment的列表。注意,该方法使用一个DataObjectMethod属性进行修饰。这个属性能够把该方法标识为一个标准的数据操作方法。

  【注意】上述DataObjectMethod属性并非是必需的,但是有了这个属性在视图层使用ObjectDataSource组件时更为容易。

  篇幅所限,我们不打算再列举文件Comment.cs中定义的其他方法了,因为它们的实现逻辑基本一致——都是简单地调用我们以前在数据访问层中定义的数据访问方法。

  至此,有的读者可能要问:既然这些业务对象管理器没有它们自己的实现逻辑,那么专门定义它们并没有多大意义吧?!不然。事实上,这些业务对象管理器正是放置你要实现的业务逻辑(例如安全,工作流,日志等)的位置。而且,这个业务逻辑层能够使我们实现视图层与数据访问层的解耦:如果我们想替换数据源的话,我们也只需修改数据访问层,而无需改动视图层。

  在构建完所有的业务对象管理器后,很有必要对数据访问和业务逻辑层进行一番彻底的测试。你可以选择使用手工测试方案,也可以借助于现成的测试脚本(例如各种自动化单元测试)。

  至此,我们已经实现了博客系统整个后台部分的编程。这些内容看起来可能十分繁多,但是一旦你掌握了里面的规律,你的构建速度可能变得非常迅速。当我们构建视图层时,我们将看到我们所付出的努力所带来的成果:一旦拥有一个恰当设计的后端,创建前端视图页面将变得非常简单。在正式开发视图层之前,还是让我们来归纳一下我们在博客应用程序的后台所创建的内容(业务对象管理器、业务对象及数据访问层的数据对象)。


图1 博客系统主要后端对象类图

  归纳来看,对应于博客系统中的每一个实体,我们定义了四个类。我们不妨以BlogEntry(博客入口)实体为例:

  BlogEntryManager:BlogEntry业务对象的类实例管理器

  BlogEntry:仅拥有属性定义的“哑”业务对象

  BlogEntryDB:博客入口的数据对象

  blogentry(没有显示于上图中):这是博客入口的LINQ数据对象,定义于映射文件中。

  接下来,我们将着手构建博客系统的数据展示层—视图层。借助于上面看似“繁杂”的N层架构设计,在视图层的构建中,你会看到我们只需仅与上面所定义的业务对象管理器进行交互,其他的方面我们毋需考虑。

  开发视图层

  现在,我们终于可以着手构建可视化的视图界面了。存在许多种构建视图层的方案。这里归纳一下我开发这部分时遵循的思路:

  为了简化设计,可以先在网络上搜索一个合适的漂亮的模板作为参考。

  创建母版页面以便重用网站设计中的许多元素。

  构建用户接口页面。

  构建管理员页面。

  (一)设计系统页面的继承架构

  尽管本博客系统中涉及到许多页面,但是不少的页面在内容呈现上极为相似。根据最新的ASP.NET应用设计原则,我们尽量把相同的部分提取出现集中放置到母版页面中。此外,我们在页面设计中还尽量遵循如下原则:

  象背景,页眉和页脚这样的内容需要显示在每一个内容页面上。显然,这样的内容应当放到站点级别的母版页面中。

  用户接口页面继承站点级别母版页面中的元素,但是在两个内容栏目中各自拥有特定的内容。其中,右边栏目将有一组固定的导航选项,而左边栏目(主要内容栏)针对每页面将呈现不同的形式。

  管理员接口页面也将继承站点级别母版页面中的元素,但是在两个内容栏目中各自拥有特定的内容。这个内容将不同于用户接口页面—右边栏目中将出现一个固定数目的系统管理员选项,而对于每一个系统管理员页面左边栏目中内容将是不同的。

  因此,我们将要创建三个母版页面。第一个是站点级别的母版页面Site.Master,第二个用于继承自站点级别母版页面的用户接口母版页面Page.Master,第三个用于也是继承自站点级别母版页面的管理母版页面Admin.Master。我们创建的大部分页面要么继承自Page.Master母版页面(用户接口)要么继承自Admin.Master母版页面。但是两种特殊的页面除外,它们是错误页面和登录页面—它们将直接继承自站点级别母版页面。

  于是,我们可以勾勒出如下的页面继承架构:


图2  博客系统总体页面设计架构示意图

  从上图中可以看出,母版页面Site.Master是站点级别的母版页面。站点中所有的页面都直接或者间接继承自这个页面。另外,我们还会注意到,母版页面Site.Master定义了两个ContentPlaceHolder,一个相应于左边栏目,另一个相应于右边栏目。

  Page.Master和Admin.Master页面都是嵌套的母版页面;它们都继承自母版页面Site.Master。这两个母版页面的右边栏目都是继承自母版页面Site.Master。对于母版页面Page.Master,右边栏目用于显示各种博客导航选项(即通过标签,月份,文章和回馈)。对于母版页面Admin.Master,右边栏目用于显示各种系统管理员导航选项(即管理博客入口,日志和设置等)。

  所有的用户接口页面都继承自母版页面Page.Master。在这个继承链中,仅有左边的栏目(即内容栏)在新的页面中进行调整,而所有其他的部分都使用了继承。在整个用户页面中,仅有错误页面和登录页面特别一些,这些页面中不需要存在导航选项,因此它们直接继承自站点级别母版页面。

  所有的系统管理员页面都继承自母版页Admin.Master。在这个继承链中,仅有左边的栏目(即内容栏)在新的页面中进行调整,而所有其他的部分都使用了继承。

  另外值得注意的是,除了母版页面继承之外,我们的博客系统中还使用了一个基页面。每一个用户接口页面的后台代码文件都继承自这个基页面(位于View/Masters/_PageBase.cs文件中)。通过这个基类PageBase,所有的继承自此基类的页面都可以使用一个UI格式化函数—ImplodeTags函数。

  (二)设计站点母版页面——Site.Master

  设计好上面的页面继承架构后,接下来,创建新的、漂亮的且外观一致的web页面就变得非常容易和迅速了。

  注意,在Visual Studio 2008中,嵌套的母版页面在设计时刻的显示效果非常不错,这使得当我们设计一个具体的内容页面时可以很容易地看到最终的结果形式。

  下面是博客系统的站点母版页面Site.Master中的一段标记代码,其中指出了ContentPlaceHolders元素所在的位置:

<div id="primarycontent">
<!—主内容开始 -->
<asp:ContentPlaceHolder ID="ContentPlaceHolderPrimary" runat="服务器"
                        EnableViewState
="False">
</asp:ContentPlaceHolder>
<!--主内容结束-->
</div>
<div id="secondarycontent">
<!—第二个内容开始 -->
<asp:ContentPlaceHolder ID="ContentPlaceHolderSecondary" runat="服务器"
                        EnableViewState
="False">
</asp:ContentPlaceHolder>
<!--第二个内容结束-->
</div>

  上面描述的过程只列出了站点母版页面中静态标记创建部分。然而,我们至少要让一个部分是动态的:页眉部分的菜单栏。这个菜单栏是一个站点级的导航栏,正好位于顶部图片的下面。

  在本博客系统中,上述菜单栏的数据源对应于应用程序的网站地图文件Web.sitemap。默认情况下,这个网站地图文件如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  
<siteMapNode>
    
<siteMapNode url="View/Pages/Home.aspx" title="首页"  description="首页"></siteMapNode>
    
<siteMapNode url="View/Pages/Aboutme.aspx" title="关于"  description="关于"></siteMapNode>
    
<siteMapNode url="View/Pages/Contact.aspx" title="联系"  description="联系"></siteMapNode>
  
</siteMapNode>
</siteMap>

  典型情况下,应该由系统管理员来维护这个文件中的菜单内容。为了正确地检索和显示头部菜单中的链接,我们使用了新的ListView控件和SiteMapDataSource控件。

  下面给出的是站点母版页面Site.Master文件中的与此部分相关的部分标记代码:

<div id="menu">
  
<asp:ListView ID="ListViewHeaderMenu" runat="server"
        DataSourceID
="SiteMapDataSourceHeader" ItemPlaceholderID="itemContainer" EnableViewState="False">
        
<LayoutTemplate>
            
<ul>
            
<asp:PlaceHolder ID="itemContainer" runat="server" />
            
</ul>
        
</LayoutTemplate>
        
<ItemTemplate>
            
<li><a href="<%# Eval("Url") %>"><%# Eval("Title") %></a></li>
        
</ItemTemplate>
    
</asp:ListView>
    
<asp:SiteMapDataSource ID="SiteMapDataSourceHeader" runat="server" ShowStartingNode="False" StartingNodeOffset="0" EnableViewState="False" />

<div id="date"><a href="~/View/Pages/Admin/Admin.aspx" runat="server">管理</a></div>
</div>

  至此,读者可能急于想了解如何构建和运行用户接口页面,而不是欣赏上面的管理页面。但是,让我们首先还是先构建用户接口母版页面Page.Master,因为它是所有用户接口页面的基础。

  小结

  在本篇中,我们剖析了系统中业务逻辑层及系统页面继承架构设计。在接下来的第四篇中,我们将讨论系统中主要用户接口页面的设计问题。

0
相关文章