在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统(二)
 
2008-12-10 来源:EntLib.com
 

使用LINQ实现数据访问层

数据访问层包含与Data Linq交互的代码,它使用集成语言查询来访问Data Linq层。下图2.1 展示了数据访问层的细节。基本上,它包括了所有与上层交互的方法,并完成与数据库相关的操作。

图2.1:数据访问层-详细视图

在示例程序中,数据访问层包含了一个简单的组件DALCustomer,相关代码如下(http://www.EntLib.com 开源小组注: 这里相关的示例代码采用C#,原文为VB代码):

 代码片段1.1:数据访问层
    public class DALCustomer
    {
        private DBLinqDataContext objDataContext = new DBLinqDataContext();
 
        public Table<Customer> SelectRecordAll()
        {
            try
            {
               return objDataContext.Customers;
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
 
        public Customer SelectRecordByID(string customerID)
        {
            try
            {
                return (from cust in objDataContext.Customers
                        where cust.CustomerID == customerID
                        select cust).Single();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        public List<Customer> SelectRecordByIDListable(string customerID)
        {
            List<Customer> localTable;
            try
            {
                localTable = (from cust in objDataContext.Customers
                              where cust.CustomerID == customerID
                              select cust).ToList();
 
                return localTable;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        public string InsertRecord(Customer localTable)
        {
            try
            {
                objDataContext.Customers.InsertOnSubmit(localTable);
                objDataContext.SubmitChanges();
 
                return localTable.CustomerID;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        public void UpdateRecord(Customer localTable)
        {
            try
            {
                objDataContext.Customers.Attach(localTable);
                objDataContext.Refresh(RefreshMode.KeepCurrentValues, localTable);
                objDataContext.SubmitChanges(ConflictMode.ContinueOnConflict);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        public void DeleteRecord(string customerID)
        {
            try
            {
                objDataContext.Customers.DeleteOnSubmit((from cust in objDataContext.Customers
                                                        where cust.CustomerID==customerID
                                                             select cust).Single());
                objDataContext.SubmitChanges();
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
 
        public Table<Order> SelectAllOrder()
        {
            try
            {
                return objDataContext.Orders;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        public Table<Order_Detail> SelectAllOrderDetail()
        {
            try
            {
                return objDataContext.Order_Details;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

在数据访问层创建的DataContext实例负责访问数据库操作相关的方法和属性。正如我们前面讨论的,DataContext类包含了数据库表的属性和产生动态SQL脚本执行数据库操作的相关方法。对于每一个数据表,均有一个对应的实体类,实体类的属性映射到实际的数据表。访问DataContext类的属性,可以返回一个Table实体集合。

DALCustomer组件包含了下面类图中的公有方法列表。SelectRecordAll 方法访问DataContext类的属性,检索所有的记录。代码如下:

return objDataContext.Customers;

SelectRecordAll()方法实际上调用DataContext类的GetTable()方法,检索所有记录。并且这些返回的记录存放在Table对象内,可以通过LINQ进行检索。可以注意到SelectRecordByID()方法查询objDataContext.Customers 返回的Table实体集合,并得到一条唯一的记录。

return (from cust in objDataContext.Customers
        where cust.CustomerID == customerID
        select cust).Single();

Insert()方法接收传入的实体对象参数,该参数包含了需要插入到数据库的新纪录。代码如下:

                objDataContext.Customers.InsertOnSubmit(localTable);
                objDataContext.SubmitChanges();

增加新纪录到数据集合,并调用SubmitChanges() 方法传入更新信息到数据库。

Update() 方法接收传入的实体对象参数,该参数包含有更新的记录。代码如下:
                objDataContext.Customers.Attach(localTable);
                objDataContext.Refresh(RefreshMode.KeepCurrentValues, localTable);
                objDataContext.SubmitChanges(ConflictMode.ContinueOnConflict);

附带(Attach)有更新的记录到数据集合,然后调用Refresh() 方法,定义如何处理附带的记录,SubmitChanges() 方法-传入ConflictMode.ContinueOnConflict 参数,决定如何更新数据库。

DeleteRecord() 方法接收传入的ID参数,并查询数据集合,选择一条唯一的记录,然后调用DataContext类的DeleteOnSubmit () 方法,删除前面从数据集合中选择的记录。最后,SubmitChanges() 方法负责将更新的信息传回数据库。

objDataContext.Customers.DeleteOnSubmit((from cust in objDataContext.Customers
                                         where cust.CustomerID==customerID
                                         select cust).Single());
objDataContext.SubmitChanges();

DALCustomer组件的类图如下所示:

图 2.2:数据访问层 – 类图

构建业务外观层(Business Facade Layer)

所有业务逻辑将在这一层实现。一般而言,这一层负责处理数据和在表现层与数据访问层之间传递数据。这一层从物理上提供上层接口隔离表现层代码和数据访问层代码,ASP.NET页面不能直接与数据访问层交互。相反,页面应该调用业务外观层的方法。图3.1 展示了业务外观层的详细视图。

图3.1:业务外观层-详细视图

在我们的示例程序中,业务外观层仅仅包含一个组件-BFLCustomer。示例代码如下:

代码片段1.2:业务外观层
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataAccess;
using System.Data.Linq;
using DataLinq;
 
namespace BusinessFacade
{
    public class BFLCustomer
    {
        private DALCustomer objDataAccess = new DALCustomer();
 
        public Table<Customer> SelectRecordAll()
        {
            return objDataAccess.SelectRecordAll();
        }
 
        public Customer SelectRecordByID(string customerID)
        {
            return objDataAccess.SelectRecordByID(customerID);
        }
 
        public List<Customer> SelectRecordByIDListable(string customerID)
        {
            return objDataAccess.SelectRecordByIDListable(customerID);
        }
 
        public string InsertRecord(Customer localTable)
        {
            return objDataAccess.InsertRecord(localTable);
        }
 
        public void UpdateRecord(Customer localTable)
        {
            objDataAccess.UpdateRecord(localTable);
        }
 
        public void DeleteRecord(string customerID)
        {
            objDataAccess.DeleteRecord(customerID);
        }
 
        public Table<Order> SelectAllOrder()
        {
            return objDataAccess.SelectAllOrder();
        }
 
        public Table<Order_Detail> SelectAllOrderDetail()
        {
            return objDataAccess.SelectAllOrderDetail();
        }
    }
}

BFLCustomer 组件包括下面类图所示的公共方法列表,这些方法用来与数据访问层交换。

注:业务外观层没有创建DataContext类的实例,因此它不能够实现任何数据访问逻辑。

BFLCustomer 组件的类图如下:

图3.2:业务外观层 – 类图

设计开发表现层

表现层负责应用程序的用户界面,图4.1是表现层的详细视图,包括Web Forms, Web User Control, Code-Behind 和共享的用户界面代码。表现层负责推送信息给终端用户和应用程序。在基于.NET的WEB应用程序中,这一层有.aspx / .ascx / .cs / .vb / web.config / .xml / .js / .resx / .css 等等。所有的这些文件集成在一起组成了表现层,提供给用户友好的界面。

图4.1:表现层 – 详细视图

ASP.NET 为web页面的开发提供了丰富的控件,通过这些控件,我们可以开发出可视化的界面和功能。在本示例程序中,表现层包括一个WebForm和一个Web User Control,使用GridView / DetailsView 控件给用户提供友好的界面,实现基本的功能,如查询、插入、更新和删除等等操作。下面展示部分HTML脚本和code-behind代码,这些脚本用来在GridView中显示数据。可以了解如何通过LINQ访问Table对象:

代码片段1.3:用户界面-HTML

            <asp:GridView ID="gvCustomer" runat="server" Width="100%" AutoGenerateColumns="False"
                AllowPaging="True" DataKeyNames="CustomerID"
                onpageindexchanging="gvCustomer_PageIndexChanging"
                onrowediting="gvCustomer_RowEditing" >
                <FooterStyle CssClass="GridFooter"></FooterStyle>
                <RowStyle CssClass="GridItem"></RowStyle>
                <AlternatingRowStyle CssClass="GridAltItem"></AlternatingRowStyle>
                <HeaderStyle CssClass="GridHeader" HorizontalAlign="Left"></HeaderStyle>
                <Columns>
                    <asp:BoundField DataField="CustomerID" HeaderText="客户ID" />
                    <asp:BoundField DataField="CompanyName" HeaderText="公司名称" />
                    <asp:BoundField DataField="ContactName" HeaderText="联系人" />
                    <asp:BoundField DataField="ContactTitle" HeaderText="联系人头衔" />
                    <asp:BoundField DataField="Address" HeaderText="地址" />
                    <asp:CommandField ButtonType="Link" HeaderText="选择" ShowEditButton="true" EditText="选择"
                        CausesValidation="false" />
                </Columns>
            </asp:GridView>

代码片段1.4:code-behind

        private void BindGridView()
        {
            Table<Customer> localDataTable;
            localDataTable = objCustomer.SelectRecordAll();
 
            gvCustomer.DataSource = (from tbl in localDataTable
                                     select new
                                     {
                                         tbl.CustomerID,
                                         tbl.CompanyName,
                                         tbl.ContactName,
                                         tbl.ContactTitle,
                                         tbl.Address
                                     });
            gvCustomer.DataBind();
 
            if(localDataTable.Count() == 0)
            {
                lblMessage.Text = "没有找到记录!";
            }
        }

上面的代码是Web页面后台代码,该代码调用业务外观层的SelectRecordAll()方法,该方法返回Table集合记录。然后查询Table集合,绑定返回结果到GridView。

注:表现层没有实现任何业务逻辑或数据访问逻辑,因此它仅仅包含用户界面代码。在表现层和业务外观层编写代码时,确保不要创建DataContext类的实例,仅仅创建业务实体类的示例。

当完成这些后,现在你可以看看你做了哪些工作。下面是Customer 的Web界面:

图4.2:Customer用户界面

总结

希望你已经了解了如何使用LINQ创建Web应用程序。这篇文章是作者使用LINQ进行N-Tire架构设计的分析和经验。使用LINQ to SQL可以减少大量的数据库操作代码,同时减少应用程序的开发时间,减少编码错误和是应用程序的维护更加容易。

最后,LINQ to SQL 对传统ADO.NET编程说“不”!

本文翻译《Building Multi-Tier Web Application in .NET 3.5 Framework Using LINQ to SQL》。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织