21.4.10 添加新订单(1)
必须完成的最后一段程序是添加新订单的代码。添加订单总是由CProductView类的OnSelectproducts()成员完成的。按下Select Products按钮的结果取决于数据成员m_OrderAdded的值。如果该成员的值是false,则OnSelectproducts()函数应该给Orders表添加新记录,还要给Order Details表添加新记录。如果m_OrderAdded是true,则只有Order Details表应该添加新记录,因为这次是相同订单的另一种产品。新Orders记录需要的所有数值都存储在文档类的m_Order成员中,只需将它们复制到也是文档类成员的COrderSet对象的成员中即可。文档对象处于处理这项任务的强位置,因此给CDBSimpleUpdateDoc类添加一个返回类型为bool的成员函数AddOrder(),其实现如下所示:
- bool CDBSimpleUpdateDoc::AddOrder()
- {
- try
- {
- if(!m_OrderSet.IsOpen()) // If recordset is not open
- m_OrderSet.Open(); // open it
-
- if(m_OrderSet.CanAppend()) // If we can add a record
- { // then add it
- m_OrderSet.AddNew(); // Start adding new record
- m_Orderm_OrderSet.m_CustomerID = m_Order.m_CustomerID;
- m_Orderm_OrderSet.m_EmployeeID = m_Order.m_EmployeeID;
- m_Orderm_OrderSet.m_Freight = m_Order.m_Freight;
- m_Orderm_OrderSet.m_OrderDate = m_Order.m_OrderDate;
- m_Orderm_OrderSet.m_OrderID = m_Order.m_OrderID;
- m_Orderm_OrderSet.m_RequiredDate = m_Order.m_RequiredDate;
- m_Orderm_OrderSet.m_ShipAddress = m_Order.m_ShipAddress;
- m_Orderm_OrderSet.m_ShipName = m_Order.m_ShipName;
- m_Orderm_OrderSet.m_ShipPostalCode = m_Order.m_ShipPostalCode;
- m_Orderm_OrderSet.m_ShipRegion = m_Order.m_ShipRegion;
- m_Orderm_OrderSet.m_ShipVia = m_Order.m_ShipVia;
-
- // No value for the Shipped Date field
- m_OrderSet.SetFieldNull(&m_OrderSet.m_ShippedDate);
-
- m_OrderSet.Update(); // Complete adding new record
- return true; // Return success
- }
- else
- AfxMessageBox(_T("Cannot append to Orders table"));
- }
- catch(CException* pEx) // Catch any exceptions
- {
- pEx->ReportError(); // Display the error message
- }
- return false; // Here we have failed
- }
在本章前面看到,记录集对象中用于添加和编辑记录的函数都可以抛出异常,因此这里将函数代码放入try代码块中,并捕获任何异常,以避免因异常发生而退出应用程序。
在确认COrderSet记录集已经打开之后,通过调用CanAppend()成员,核实是否可以给该记录集添加记录。添加新记录涉及3个步骤:
(1) 首先调用记录集的AddNew()成员,以启动添加过程,并保存记录集中数据成员的当前值(因为将要更改它们的值),然后将数据成员的值设置为空值。这里的空值与空指针毫无关系,也不是0,而仅仅意味着还没有给某个变量赋值。
(2) 将记录集中所有数据成员设置为新记录需要的数值。这一步十分简单。只需将m_Order对象的成员中存储的数值复制到记录集对象的成员中即可。m_ShippedDate成员是空值,因为这里还没有给该成员赋值。
(3) 调用Update()函数以实际地写出新记录,同时恢复记录集对象中原来的数值。恢复原来的数值不适用这里的情形,但如果想显示新添加的记录集,则需要调用记录集对象的Requery()成员才能使新记录值显示出来。
现在,可以提出CProductView类中OnSelectproduct()处理程序的基本逻辑。需要为产品视图调用UpdateData()函数,以使在编辑控件中输入的数据传输给视图对象的数据成员。下面是该处理函数的轮廓代码:
- void CProductView::OnSelectproduct()
- {
- UpdateData(TRUE); // Transfer data from controls
-
- // Get a pointer to the document
- CDBSimpleUpdateDoc* pDoc =
- static_cast<CDBSimpleUpdateDoc*>(GetDocument());
-
- if(!m_OrderAdded) // If order not added
- m_OrderAdded = pDoc->AddOrder(); // then try to add it
-
- if(m_OrderAdded)
- // Code to add new Order Details record...
- }
在为CProductView对象调用UpdateData()函数之后,获得指向文档对象的指针。需要该指针来调用文档对象的AddOrder()成员,以完成添加订单的工作。接下来,检查m_OrderAdded成员。仅当该成员为false时,才需要给Orders添加记录。如果记录被成功添加,则文档对象的AddOrder()成员返回bool值true;如果添加操作失败,则返回false。使用该返回值设置CProductView的m_OrderAdded成员,并将其用作是否可以继续添加其他订单信息的指示器。不必在添加操作失败的情况下显示任何消息,AddOrder()函数已经包括显示出错消息的代码。
给Order Details表添加记录的代码由文档对象处理同样可能是最合适的,但文档类中做这件事的成员函数需要访问CProductView和CProductSet类中4个成员的值,它们是产品ID、订购数量、单价和适用的折扣。文档类可以从其m_Order成员中得到订单ID,因此不必担心该数值。可以给CDBSimpleUpdateDoc类添加一个给Order Details表添加记录的函数AddOrderDetails()。该函数的返回类型应该是void,其4个形参分别是long类型的ID、double类型的price、int类型的quantity和float类型的discount。
该函数的实现如下:
- void CDBSimpleUpdateDoc::AddOrderDetails(long ID, double price,
- int quantity, float discount)
- {
- try
- {
- if(!m_DBSimpleUpdateSet.IsOpen()) // If recordset is not open
- m_DBSimpleUpdateSet.Open(); // open it
-
- m_DBSimpleUpdateSet.AddNew(); // Start adding new record
-
- // Set Product Details recordset data member values
- m_DBSimpleUpdateSet.m_OrderID = m_Order.m_OrderID;
- m_DBSimpleUpdateSet.m_Quantity = quantity;
- m_DBSimpleUpdateSet.m_Discount = discount;
- m_DBSimpleUpdateSet.m_ProductID = ID;
- m_DBSimpleUpdateSet.m_UnitPrice = price;
- m_DBSimpleUpdateSet.Update(); // Complete adding new record
- }
- catch(CException* pEx) // Catch any exceptions
- {
- pEx->ReportError(); // Display the error message
- }
- }
该函数设置m_DBSimpleUpdateSet的各个成员的数值,然后以基本上与更新Orders表相同的方式更新Order Details表。再次将更新代码放入try代码块中,以捕获任何可能由AddNew()或Update()抛出的异常。
我们希望在每次调用CProductView类中Select Product按钮的处理程序时都调用该函数,因此应该将该按钮的处理程序修改成下面这样:
- void CProductView::OnSelectproduct()
- {
- UpdateData(TRUE); // Transfer data from controls
-
- // Get a pointer to the document
- CDBSimpleUpdateDoc* pDoc =
- static_cast<CDBSimpleUpdateDoc*>(GetDocument());
-
- if(!m_OrderAdded) // If order not added
- m_OrderAdded = pDoc->AddOrder(); // then try to add it
- if(m_OrderAdded)
- {
- pDoc->AddOrderDetails(m_pSet->m_ProductID,
- m_pSet->m_UnitPrice,
- m_Quantity,
- m_Discount);
- // Now reset the values in the quantity and discount controls
- m_Quantity = 1;
- m_Discount = 0;
- UpdateData(FALSE); // Transfer data to controls
- }
- }