.Net 2.0正式版的正式发布,对我们程序开发人员来说无疑是一件很大的喜事,已经很多的人开始了解并使用.NET 2.0。Visual Studio 2005 的一些新的增强功能和ADO.NET 2.0的新特性让我们开发高可伸缩的多层数据库应用程序更加简单和快捷。本文将带领大家使用Visual Studio 2005来创建一个N层数据库应用的示例,并介绍一些NET 2.0和 Visual Studio 2005相关的知识。
在.NET 1.1下就可以使用强类型数据集,可是在Visual Studio 2003里开发起来还是有一些不方便,在Visual Studio 2005里对DataSet的设计视图做了很大的改进,因为ADO.NET 2.0增加了TableAdapter对象,所以现在也可以在DataSet设计视图里添加DataTable的表适配器。以前的typedDataSets只是一些数据实体(哑对象,只有属性的类)的集合,并不能很方便的自我管理持久化,以及数据完整性验证等。现在可以很方便的实现这些。
我们今天示范的这个例子是做一个显示消费者列表,并可以查看每个消费者订单细目的一个小程序。先用Visual Studio 2005创建一个typedDatasetSample.lib的类库项目,这个类库项目呢,我们主要保存一些强类型数据集,我们给这些强类型数据集加上一些自我持久的方法以及一些业务逻辑。然后呢我们就可以在构建桌面程序或者Web程序的时候使用它了,它就相当于我们的业务逻辑层,以后也许是通过Remoting来发布,也许通过Web服务发布,也许直接拷贝DLL来发布,一般把它放在Application Server上,以方便你的前台Smart Client,ASP.Net,以及移动应用来实例并使用。
这是一个企业应用的核心,所以我们先构建它,然后就可以用Composite UI Application Block (CAB)来创建Smart Client界面,用ASP.NET 2.0和微软的EntERPriseLibrary November 2005 CTP创建Web应用界面和移动应用界面。Asp.net 2.0加入了WebPart,MastPage,ObjectDataSource这几个控件大大简化了创建Web界面应用的过程,大大提高了开发者的用户体验(个人感觉是这样的,使用起来非常的舒服)。Asp.Net 2.0的设备自适应功能,设备过滤器让开发者构建移动应用和构建普通Web应用统一了起来,写一段程序可能不用任何修改就即可以在Internet Explorer上显示,也可以在Inernet Explorer for Pocket PC上显示,也可以在Openwave WAP Emulator上显示,新的MultiView,View,Pager控件也对你快速开发移动应用提供了很大的帮助。我推荐大家尽量使用微软推荐的工具和技术来实现自己的企业应用,毕竟微软还是更值得信赖的。
项目建好了,先删掉默认的Class1.cs,右键单击解决方案选择【Add】【New Item】添加一个名为Customer.xsd的数据集。Ctrl+Alt+S打开Server Explorer,右键单击【Data Connections】选择【Add Connection】,在打开的窗口里的【Server Name】输入框里输入“localhost\sqlexpress”,选中【Use Windows Authentication】单选框,在【Select or enter a database name】选择框里选择“Northwind”数据库,点击【Test Connection】,测试成功后单击【OK】按钮。这样我们就新建了一个数据连接。
在Server Explorer里展看依次展开localhost\sqlexpress节点,Tables节点,并把Customers表拖到数据集设计视图上。我们可以看到Visual Studio自动给我们添加了一个Customers的强类型数据集和一个叫CustomersTableAdapter的表适配器,并给这个表适配器自动添加了一个Fill的方法,这个方法是用来填充Customers数据集的。细心的朋友也许会发现在properties文件夹下添加了一个Settings.settings的文件,双击它可以看到里面保存了一个数据库连接字符串,这也是.NET 2.0的新特性,以后还可以以编程的方式来访问它,如果感兴趣也可以看看它自动生成的Settings.Designer.cs文件。
右键在CustomersTableAdapter最上面单击并选择【Add Query】,选择【Use SQL statements】,点击【next】按钮这个选项是我们直接编写SQL语句,下面两个选项分别是创建一个新的存储过程或者使用一个新的存储过程。下一个界面是让你选择查询的类型,是插入操作还是删除操作还是更新操作等,我们先创建一个插入操作,选择【Insert】进入下一步,点击【Query Bulider】来可视化的生成SQL语句,这个查询生成器我想大家都很熟悉吧,比我的wawaCodePro要灵活多了,你可以很方便的创建出多表连接的复杂查询语句,以可以手动创建一些定制的Insert,Update等SQL语句,大家不要以为自己手工写复杂的SQL语句是很酷的事情,其实这个会使用这个查询生成器才厉害呢,Access里就有的。创建完语句后可以点击下面的【Excute Quert】按钮来测试你的语句是否正确,因为我们是一个需要参数的SQL语句,所以会弹出【Query parameters】窗口来让你输入参数。最后我们生成的语句如下。
INSERT INTO Customers
(CustomerID, CompanyName, ContactName, ContactTitle,
Address, City, Region, PostalCode, Country, Phone, Fax)
VALUES (@CustomerID,@CompanyName,@ContactName,@ContactTitle,
@Address,@City,@Region,@PostalCode,@Country,@Phone,@Fax)
点击下一步键入自定义的方法名字“NewCustomer”,点【next】【finish】。如果要修改已经生成的查询,可以在要修改的查询上右键选择【Configure】以向导的方式来重新生成查询,或者选择相应查询,在【properties】面板里设置它的属性。分别以相同的方式添加删除和修改的查询。删除的语句如下:
DELETE FROM Customers WHERE (CustomerID = @Original_CustomerID)
删除的方法键入“DeleteCustomerByCustomerID”。
修改的语句如下:
UPDATE Customers
SET CompanyName = @CompanyName, ContactName = @ContactName, ContactTitle =
@ContactTitle, Address = @Address, City = @City,
Region = @Region, PostalCode = @PostalCode, Country = @Country, Phone =
@Phone, Fax = @Fax
WHERE (CustomerID = @Original_CustomerID)
修改的方法键入“UpdateCustomerByCustomerID”。
大家有兴趣可以看看自动在Customers.Designer.cs文件里生成的代码,一切的一切Visual Studio都给你生成了。定位到NewCustomer()方法可以看到它有个[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Insert, false)]属性,这是设置让ObjectDataSource,BindSource等数据源控件自动绑定相应操作的属性,估计用的是一种Ioc(控制反转)的机制来实现的,你在设置ObjectDataSource的时候它的插入方法会自动绑定到这个方法上,一会儿就会看到。
以上我们把显示消费者列表,以及消费者的修改,删除,添加等持久化操作代码生成了,我们还没有写一行代码,包括SQL语句哦。我们的数据实体(强类型数据集的ROW)虽然是哑对象,但是我们的表适配器可以以托管的形式来对这些实体进行CRUD持久化操作,类似于CMP(容器托管持久化,这里的表适配器就相当于容器)模式,然后我再告诉大家如何给数据实体添加自定义业务逻辑的方法(也许一个方法不能算是一个Case,但我感觉获取消费者的订单至少是一个Action吧)。当然了,我这里告诉大家的只是一种紧耦合的方法,如果你想设计灵活度比较高的高度可伸缩的应用,尽量使用一些Interface,Service,Event,properties(自定义属性)等技术来实现数据实体和数据实体具体操作的松散耦合,这样就可以做到我在UI层只引用Customer这个业务实体,然后调用Customer.GetOrderDetails()方法的时候,也许是在本地DLL里实现,也许是在用Rmorting方式连接到Application Server来实现,也许是通过一个叫做GetOrderDetailsByCustomerID的Web服务来实现的。
现在我们来做一个订单细目的业务实体以及获取某个消费者订单细目的操作,因为Northwind数据库里没有这么一个直接的表来直观的显示订单细目,Orders只是一个交叉引用表,Order Details表也是一个交叉引用表,而我们的订单细目要显示订单号,消费者名称,产品名称,购买数量,单价,雇员名称等信息,而这些信息分布在多个表中,我们不能简单的把一个表拖放到数据集设计视图来生成相应的业务实体和表适配器。我们可以在数据集设计视图里的空白处右键选择【Add TableAdapter】,然后按向导在【Quert Builder】里把Customers,Orders,Order Details,Procucts,Employees五个表拖到数据表栏上,然后他们之间的外键关系会自动的可视化的指定,然后分别用鼠标选择几个表的某些字段,最后生成语句如下。
SELECT Customers.CustomerID, Customers.
ContactName, [Order Details].UnitPrice, [Order Details].Quantity,
Products.ProductName,
Orders.OrderID,
Orders.OrderDate, Employees.FirstName + Employees.LastName AS fullname
FROM Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID INNER JOIN
[Order Details] ON Orders.OrderID = [Order Details].OrderID INNER JOIN
Products ON [Order Details].ProductID = Products.ProductID INNER JOIN
Employees ON Orders.EmployeeID = Employees.EmployeeID
WHERE (Customers.CustomerID = @CustomerID)
ORDER BY Customers.CustomerID DESC
方法名修改成“GetOrderDetailsByCustomerID”,并把生成的强类型表重命名为OrderDetails。
另外在表适配器向导里有一个【advert】按钮,点击打开后可以设置本查询是否支持乐观并发锁定等处理,因为在一个多用户的数据应用里进行删除和修改操作的时候要考虑并发问题的,这个是个很棘手的问题,Visual Studio也帮我们解决了(具体原理我还不太清楚,我没有详细研究数据集自动生成的代码,一般是用时间戳列或者 一长串where子句来处理并发的。)
目前为止,我们仍然一行代码也没有写,不过现在要写一┝恕K骺瞻状Γ蚩狢ustomers.cs文件。可以看到Customer类有个partial修饰符,这也是.net 2.0的新特性,因为Customer这个类一半在Customer.Desinger.cs里,而另一半分布在Customer.cs里,你重新修改了表适配器,会自动同步Customer.Desinger.cs里代码,而你自己写在Customer.cs里的代码不会给你覆盖,看来.NET 2.0的新特性考虑还是很体贴的。以前我们只能由代码生成器生成一个基类,然后手工把自定义代码写到一个子类里,以防止代码生成器重新生成代码的时候把你自己写的代码覆盖掉,对框架和架构感兴趣的朋友也许看过《.NET企业应用高级编程》里的那个WEO体系结构和ObjectBuilder代码生成工具。我们可以在Customer类里添加OnColumnChanging,OnRowChanging等方法来进行数据完整性的验证,详见MSDN,这里不做重点讲解。
因为默认生成的GetOrderDetails方法是一个非静态方法,使用起来有些不方便,为了便于使用,我们在Customer.cs里加入一下语句。用一个静态方法来重写(我也不知道是重载还是重写了)非静态方法GetOrderDetails。
partial class CustomersTableAdapter
{
public static Customer.OrderDetailsDataTable GetOrderDetails
(string CustomerID)
{
typedDatasetSample.lib.CustomerTableAdapters.OrderDetailsTableAdapter
= new typedDatasetSample.lib.CustomerTableAdapters.OrderDetailsTableAdapter();
return o.GetOrderDetailsByCustomerID(CustomerID);
}
}
因为默认生成的业务实体类是CustomersRow,我们来改进它,给它加一个自定义方法。(但是我感觉CustomersRow这个名字不好听,如果做一个它的子类叫CustomersEntity的话,可是子类又不能引用父类,所以这个问题我也不知道如何解决,所以先用分割类来添加一个新方法,而不是继承CustomersRow类然后给子类添加一个新方法)
partial class Customer
{
partial class CustomersRow
{
public Customer.OrderDetailsDataTable GetOrderDetails()
{
return CustomersTableAdapter.GetOrderDetails(this.CustomerID);
}
}
}
好了,这个类库就做到这里了,你当然可以添加其它一些业务逻辑,关闭这个项目。现在我们先创建一个typedDatasetSample.Web的网站。在解决方案里点击右键选择【Existing project】把刚才的数据集类库添加到网站解决方案里。然后在网站项目里右键选择【Add Reference】把typedDatasetSample.lib项目引用一下。
打开Default.ASPx的设计视图,在ToolBox里拖放一个DetailView控件进来,重命名为CustomerDetailsView。点击右上角的小三角Choose Data Source,选择new data source,选择【object】就是添加一个ObjectDataSource,命名为CustomerObjectDataSource。点击【next】按钮后【choose your business object】里选择CustomersTableAdapter,下一步后选择CRUD操作的映射方法,前面我们说过一些方法加上[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Insert, false)]属性就会自动在这里绑定,所以我们直接在这里设置一下就行了。因为在系统自动生成了一些CRUD方法,我们不用它们,我们用我们的自定义查询和方法,比如说NewCustomer()方法,而不是默认的Insert()方法,因为这个不直观。然后把Enable Paging,Instering,Editing,Deleteing的勾打上。因为Customers表里数据太多,我们把ObjectDataSource的FilterExpression属性设置成“ContactTitle='Owner'”以减少检索的数据,其实CustomerObjectDataSource就相当于一个实际的数据源,它有好多模仿数据源的特性,大家可以详细看看。在ASP.NET 2.0里可以让你不知不觉就使用了MVC模式。
好了,我们又没有写一行代码,按F5运行一下。看一下添加,删除,修改,浏览是不是都能用了呀,就是这么简单。其实如果我们把添加,删除,修改等业务逻辑用Oracle实现一遍,其实我们的asp.net程序几乎不用修改代码,只重新换一下objectdatasource就可以了。
下面拖放一个GridView控件,命名为OrderDetailGridView。选中CustomerDetailsView然后在属性窗口,点击属性表头的闪电按钮,所有可用的时间就显示了,双击DataBound会在Default.cs文件里添加CustomerDetailsView_DataBound方法,这个事件是在Detail控件数据绑定的时候触发的,修改成以下的代码。
protected void CustomerDetailsView_DataBound
(object sender, EventArgs e)
{
//获取当前的数据绑定项并强制转换为DataRowView类型
DataRowView drv = (DataRowView)CustomerDetailsView.DataItem;
//如果当前是添加新项目状态的话,不会获取DataRowView引用,所以要加一个IF判断。
if (drv != null)
{
//获取row并强制转换成CustomerEntity业务实体类
typedDatasetSample.lib.
Customer.CustomersRow customer
=(typedDatasetSample.lib.Customer
.CustomersRow)drv.Row;
//运行业务实体类的一个自定义方法并把返回结果作为GridView的数据源
OrderDetailGridView.DataSource = customer.GetOrderDetails();
//绑定GridView
OrderDetailGridView.DataBind();
}
F5运行一下,看看GridView是不是自动绑定了,翻页看看GridView是不是显示每个客户的订单细目呢。
Asp.net的应用就开发完了,下面我们来开发一个桌面程序。关闭网站解决方案,创建一个typedDatasetSample.Win的Windows应用程序,然后把typedDatasetSample.lib项目包含进去,并引用到typedDatasetSample.Win项目里。然后把一个DataGridView拖进来命名为customersDataGridView,并把Dock属性设置为fill,让它能填充满窗体。再拖一个BindSource进来命名为customersBindingSource,再拖一个CustomerTableAdapter进来并命名为ta,Visual Studio 2005支持几个简单从重构手段,其中包括重命名,所以你重命名控件名称的时候,它会自动帮你修改一些相关的代码,比较智能的。然后选择窗体,在事件窗格里双击Load事件,键入如下代码。
private void Form1_Load(object sender, EventArgs e)
{
this.customersBindingSource.DataSource = ta.GetData();
this.customersBindingSource.Filter = "ContactTitle='Owner'";
this.customersBindingSource.Sort = "Country DESC, Address ASC";
this.customersDataGridView.DataSource = customersBindingSource;
}
好了,按F5运行以下,看看DataGridView是不是被数据填充了呢。因为我个人对桌面程序不是太感兴趣,也没有很多经验,所以只演示一个数据填充的业务逻辑,其它的持久操作以及自定义操作,对WinForm感兴趣的朋友可以自己做一做,我这里是以写代码的方式来完成的数据填充,其实也可以以可视化的操作来完成数据网格的填充。