对数据库做增,删,改操作
至今,我们已经添加代码,从数据层(DataLayer)绑定数据到表现层中的数据绑定控件(DataGridView).这里,用户可以添加,删除或者修改Customer或者Order相关数据,通过DataGridView提供的界面接口.经过添加,删除或者修改数据后,用户可以选择更新输入的信息到数据库中.在本篇剩下的部分,我们将描述有关的必要的步骤,怎么用新的,修改的或者删除的数据区更新数据库.
这里缺少一些相关的代码,来更新我们的Customer对象到数据层(DataLayer),首先,我们将添加两个List实例,做为我们Customer管理窗体的私有成员.这些List实例将保持明确删除的Customers和Orders.
private List<CustomerBO> m_deletedCustomers = null;
private List<OrderBO> m_deletedOrders = null;
下一步,我们应该处理customerBOBindingSource的AddingNew事件,为BindingSource处理一个新的Customer对象的添加(如图19).
图19.处理CustomerBindingSource的AddingNew事件
处理添加一个新Customer到我们的CustomerBindingSource的代码,如下所示.
private void customerBOBindingSource_AddingNew(object sender, AddingNewEventArgs e)
{
CustomerBO customer = new CustomerBO();
customer.IsNew = true;
customer.IsDirty = true;
customer.CompanyName = "[新客户名]";
customer.CustomerId = "[新客户ID]";
e.NewObject = customer;
customer = null;
}
相同的事件处理过程应该被激活,为我们的OrderBindingSource.注意(看下面的代码)到,用于添加一个新订单的代码,也应该用一个到父集合(Customer)的引用.因为我们的订单对象必须有个唯一的OrderID,我将设置该ID为一个负自增值.我们的"Orders"数据集DataSet包含一个OrderID列,该列设置为一个自增列;Order对象中的ID属性设置为一个负值,是为了避免在将一个新的Order行插入到数据库中时,发生冲突.在服务器端,当在Order表中插入一个新行时,数据库服务器将添加一个新的OrderID值,并且,一个新的唯一OrderID值将在更新的DataSet中返回,而且,应该与客户端的对象的值保持同步(我将在本文档的下个小结中解释该特性).
private void ordersBindingSource_AddingNew(object sender, AddingNewEventArgs e)
{
OrderBO order = new OrderBO();
order.IsNew = true;
order.IsDirty = true;
order.OrderDate = DateTime.Today;
order.ProductName = "[新产品名]";
//--- Set Temporary OrderID - Will be set to negative
//value to avoid update conflicts
m_lastOrderID--;
order.OrderId = m_lastOrderID;
//--- Get the Current Customer
CustomerBO currentCustomer = (CustomerBO) customerBOBindingSource.Current;
if (!(currentCustomer == null))
{
order.Customer = currentCustomer;
}
else
{
throw new NullReferenceException("订单的客户找不到!");
}
e.NewObject = order;
order = null;
}
处理改进的事件对于表现层的开发人员来说非常显然.这里没有什么专门处理CustomerBO或者OrderBO的属性修改部分的类.我们将逐步地讨论这些类.这些辅助类部署在解决方案的业务层集合中.
让我们以IEventPublisher接口开始介绍,该接口有一套对象属性修改事件的蓝图.让我们进一步考察这个接口类:
public interface IEventPublisher
{
void RegisterListener<T>(T p_listener) where T : IEventListener;
void UnregisterListener<T>(T p_listener) where T : IEventListener;
void NotifyListeners();
bool IsDirty { get; set;}
bool Initializing { get; set;}
bool IsNew { get; set;}
}
该IEventPublisher接口有为监听对象注册和反注册所用的方法接口,当一个对象属性值改变时,通知监听者,还有2个属性设置,IsDirty属性用来将一个附加对象标识为修改的状态,另外,当加载对象数据时,Initializing属性设置为true(因此要预防加载对象中的数据过程中,启动该修改事件).
下一步,我们看看IEventListener接口.该接口含有一个到Publisher对象的引用(我们的概念中,指customer或者order对象),并且描述了一个方法的签名,该方法专门用来处理对象属性"修改"事件.
public interface IEventListener
{
void OnNotification(IEventPublisher p_publisher);
}
Publisher类是每个自定义对象业务类的基础类.业务对象直接继承该Publisher类.该Publisher类含有"注册"和"反注册"监听对象的方法.监听对象就是指那些需要在业务对象类别的属性修改时,被通知的对象.一个典型的监听者是一个Windows Form.该基类也包含一些基础的属性,用来设置业务对象到一些初始状态,如"IsNew","IsDirty"(修改过的),"IsInitializing"状态.
public abstract class Publisher : IEventPublisher
{
private delegate void m_eventHandler(IEventPublisher p_publisher);
private event m_eventHandler m_event;
IEventPublisher 成员#region IEventPublisher 成员
public void RegisterListener<T>(T p_listener) where T : IEventListener
{
m_event += new m_eventHandler(p_listener.OnNotification);
}
public void UnregisterListener<T>(T p_listener) where T : IEventListener
{
m_event -= new m_eventHandler(p_listener.OnNotification);
}
public void NotifyListeners()
{
if (m_event != null)
{
m_event(this);
}
}
protected bool m_isDirty = false;
public bool IsDirty
{
get
{
return m_isDirty;
}
set
{
m_isDirty = value;
}
}
protected bool m_initializing = true;
public bool Initializing
{
get
{
return m_initializing;
}
set
{
m_initializing = value;
}
}
protected bool m_isNew = false;
public bool IsNew
{
get
{
return m_isNew;
}
set
{
m_isNew = value;
}
}
#endregion
}
ObjectChangedListener类包含了OnNotification方法,当业务对象类(是注册到NotifyChanged事件中的)属性值发生改变时,执行该方法;
public class ObjectChangedListener : IEventListener
{
IEventListener 成员#region IEventListener 成员
public void OnNotification(IEventPublisher p_publisher)
{
if (!p_publisher.Initializing)
{
p_publisher.IsDirty = true;
}
}
#endregion
}
因此(看下面的代码),Notification事件被附加到set属性中.所以,当一个属性值变化时,该事件将抛出(CustomerBO的属性).
public string CustomerId
{
get { return m_CustomerId; }
set
{
m_CustomerId = value;
NotifyListeners();
}
}
public string CompanyName
{
get { return m_CompanyName; }
set
{
m_CompanyName = value;
NotifyListeners();
}
}
当使用者单击保存按钮,下一步,代码将被执行,用新增的,修改的或者删除的对象,回写更新数据库.因为这里有很多相关的代码,我将在代码内部进行说明.
private void customerBOBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
//--- Validate the Value which loses control first
this.Validate();
//--- End the CurrentEdit on the BindingSources
customerBOBindingSource.EndEdit();
ordersBindingSource.EndEdit();
//--- Create Customer and Order List to hold the
//Changed objects
List<CustomerBO> changedCustomers = null;
List<OrderBO> changedOrders = null;
//--- Loop through the ObjectStacks to catch changed
//property data
foreach (Object obj in customerBOBindingSource)
{
CustomerBO customer = (CustomerBO) obj;
if (customer.IsDirty)
{
if (changedCustomers == null)
changedCustomers = new List<CustomerBO>();
changedCustomers.Add(customer);
}
foreach (OrderBO order in customer.Orders)
{
if (order.IsDirty)
{
if (changedOrders == null)
changedOrders = new List<OrderBO>();
changedOrders.Add(order);
}
}
customer = null;
}
if (changedCustomers != null || changedOrders != null || m_deletedCustomers != null || m_deletedOrders != null)
{
bool IsUpdateOK = true;
try
{
//--- Update through Business Object Layer
int numUpdate = m_customerBO.SaveCustomerOrders(changedCustomers,
m_deletedCustomers,
changedOrders,
m_deletedOrders);
//--- Display Success
MessageBox.Show(String.Format("{0}行成功更新到数据库!", numUpdate.ToString()));
}
catch (Exception ex)
{
IsUpdateOK = false;
//--- Show Error
MessageBox.Show(ex.Message, "发生错误,更新失败!");
}
finally
{
//--- Reset object state & Release Resources
if (IsUpdateOK)
{
//--- First Syncronize the OrderID's with
//Server Versions
if (changedOrders != null)
{
m_customerBO.SyncroOrderID(changedOrders);
}
if (changedCustomers != null)
{
foreach (CustomerBO customer in changedCustomers)
{
customer.IsDirty = false;
customer.IsNew = false;
}
changedCustomers = null;
}
if (changedOrders != null)
{
foreach (OrderBO order in changedOrders)
{
order.IsDirty = false;
order.IsNew = false;
}
changedOrders = null;
}
//--- Release Helper Objects
m_deletedCustomers = null;
m_deletedOrders = null;
//--- Refresh the OrderGrid
ordersDataGridView.Refresh();
}
}
}
}
添加的和修改的对象可以在包含列表中被跟踪,删除的不能.因此,我们必须手动跟踪这些对象.所以,我们下一步必须在代码中部署下一个事件处理程序.
在定义部分:
public partial class CustomerAdmin : Form
{
private CustomerBO m_customerBO;
private List<CustomerBO> m_deletedCustomers = null;
private List<OrderBO> m_deletedOrders = null;
…
}
处理删除Customer或者Order对象的代码:
private void customerBODataGridView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
{
if (m_deletedCustomers == null)
m_deletedCustomers = new List<CustomerBO>();
m_deletedCustomers.Add((CustomerBO)e.Row.DataBoundItem);
if (((CustomerBO)e.Row.DataBoundItem).Orders != null)
{
if (m_deletedOrders == null)
m_deletedOrders = new List<OrderBO>();
m_deletedOrders.AddRange(((CustomerBO) e.Row.DataBoundItem).Orders);
}
}
private void ordersDataGridView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
{
if (m_deletedOrders == null)
m_deletedOrders = new List<OrderBO>();
m_deletedOrders.Add((OrderBO)e.Row.DataBoundItem);
}
最后,我们的BusinessLayer Class解决处理到DAL的更新.如下所示的实现方法,还用了Optimistic的途径处理了DbConcurrency问题.
public int SaveCustomerOrders(List<CustomerBO> p_addedOrModifiedCustomers,
List<CustomerBO> p_deletedCustomers,
List<OrderBO> p_addedOrModifiedOrders,
List<OrderBO> p_deletedOrders)
{
//--- Create the Service to update the Data
CustomerOrderService dataService = new CustomerOrderService();
Step 1 : Add Deleted Customer Object Information#region Step 1 : Add Deleted Customer Object Information
if (p_deletedCustomers != null)
{
foreach (CustomerBO deletedCustomerObject in p_deletedCustomers)
{
//--- Create New CustomerRow to Hold Reference to the Deleted CustomerRow
CustomerOrderTDS.CustomersRow deletedCustomerRow = m_dataContainer.Customers.NewCustomersRow();
//--- Check if Row Exists in Our DataContainer
deletedCustomerRow = m_dataContainer.Customers.FindByCustomerID(deletedCustomerObject.CustomerId);
//--- Set RowState to Delete in our DataContainer
if (deletedCustomerRow != null)
{
deletedCustomerRow.Delete();
}
}
}
#endregion
Step 2 : Add Deleted Order Object Information#region Step 2 : Add Deleted Order Object Information
if (p_deletedOrders != null)
{
foreach (OrderBO deletedOrderObject in p_deletedOrders)
{
//--- Create a New OrderRow to Hold Reference to the Deleted OrderRow
CustomerOrderTDS.OrdersRow deletedOrderRow = m_dataContainer.Orders.NewOrdersRow();
//--- Check if Row Exists in Our DataContainer
deletedOrderRow = m_dataContainer.Orders.FindByOrderID(deletedOrderObject.OrderId);
//--- Set RowState to Delete in our DataContainer
if (deletedOrderRow != null)
{
deletedOrderRow.Delete();
}
}
}
#endregion
Step 3 : Add New/Modified Customer Object Information#region Step 3 : Add New/Modified Customer Object Information
if (p_addedOrModifiedCustomers != null)
{
foreach (CustomerBO addedOrModifiedCustomerObject in p_addedOrModifiedCustomers)
{
//--- First Check If Current Customer Object is a New or Modified Object in our DataContainer
if (addedOrModifiedCustomerObject.IsNew)
{
//--- Add a New CustomerRow to Our DataContainer.Customer Table
CustomerOrderTDS.CustomersRow newCustomerRow = m_dataContainer.Customers.NewCustomersRow();
//--- Map object properties to rowcolumns
ORM.ObjectToRelational(addedOrModifiedCustomerObject, newCustomerRow);
//--- Add the New Customer Row to Our DataContainer
m_dataContainer.Customers.AddCustomersRow(newCustomerRow);
}
else
{
//--- Get Modified Row Information
CustomerOrderTDS.CustomersRow modifiedCustomerRow = m_dataContainer.Customers.NewCustomersRow();
//--- Map object properties to rowcolumns
ORM.ObjectToRelational(addedOrModifiedCustomerObject, modifiedCustomerRow);
if (modifiedCustomerRow != null)
{
//--- Search modified Row in DataContainer
CustomerOrderTDS.CustomersRow customerRowToModify =
m_dataContainer.Customers.FindByCustomerID(modifiedCustomerRow.CustomerID);
//--- Map Changed Data, RowState will be set to True for our DataContainer.Customer Row !
if (customerRowToModify != null)
{
for (int i = 0; i < m_dataContainer.Customers.Columns.Count; i++)
{
customerRowToModify[i] = modifiedCustomerRow[i];
}
}
}
}
}
}
#endregion
Step 4 : Add New/Modified Order Object Information#region Step 4 : Add New/Modified Order Object Information
if (p_addedOrModifiedOrders != null)
{
foreach (OrderBO addedOrModifiedOrderObject in p_addedOrModifiedOrders)
{
//--- First Check if Current Order is a New or Modified object in our DataContainer
if (addedOrModifiedOrderObject.IsNew)
{
//--- Add a New OrderRow to Our DataContainer.Order Table
CustomerOrderTDS.OrdersRow newOrderRow = m_dataContainer.Orders.NewOrdersRow();
//--- Map object properties to rowcolumns
ORM.ObjectToRelational(addedOrModifiedOrderObject, newOrderRow);
//--- Map Foreign Key for Customers
newOrderRow.CustomerID = addedOrModifiedOrderObject.Customer.CustomerId;
//--- Add the New Order Row to Our DataContainer
m_dataContainer.Orders.AddOrdersRow(newOrderRow);
}
else
{
//--- Get Modified Row Information
CustomerOrderTDS.OrdersRow modifiedOrderRow = m_dataContainer.Orders.NewOrdersRow();
//--- Map object properties to rowcolumns
ORM.ObjectToRelational(addedOrModifiedOrderObject, modifiedOrderRow);
if (modifiedOrderRow != null)
{
//--- Search Modified Row in DataContainer
CustomerOrderTDS.OrdersRow orderRowToModify = m_dataContainer.Orders.FindByOrderID(modifiedOrderRow.OrderID);
//--- Map Changed Data, RowState will be set to True for our DataContainer.Order Row !
if (orderRowToModify != null)
{
for (int i = 0; i < m_dataContainer.Orders.Columns.Count; i++)
{
System.Data.DataColumn column = orderRowToModify.Table.Columns[i];
if (!column.ReadOnly)
{
orderRowToModify[i] = modifiedOrderRow[i];
}
}
//--- Re-Map Foreign Key for Customer
orderRowToModify.CustomerID = addedOrModifiedOrderObject.Customer.CustomerId;
}
}
}
}
}
#endregion
Update through the DataService class#region Update through the DataService class
if (m_dataContainer.HasChanges())
{
bool updateOK = true;
try
{
return dataService.SaveWithTransaction(m_dataContainer, false);
}
catch (DBConcurrencyException dbconcEx)
{
//--- DbConcurrency Occured, ask User either to Persist his changes or reload from server
string message = dbconcEx.Message + "\r\n";
message += "Persist changes to the DataBase [Yes]\r\n" +
"Reload the changed Data from the Server [No]";
string caption = "DbConcurrency !";
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
DialogResult result;
// Displays the MessageBox.
result = MessageBox.Show(message, caption, buttons);
if (result == DialogResult.Yes)
{
//--- Persist the changes to the Database
try
{
return dataService.SaveWithTransaction(m_dataContainer, true);
}
catch (Exception ex)
{
m_dataContainer.RejectChanges();
updateOK = false;
throw ex;
}
}
else
{
try
{
//--- Reload data from server and merge with local data
if (m_dataContainer.HasErrors)
{
if (m_dataContainer.Customers.HasErrors)
{
//------------------------------------------
//--- Resolve DbConcurrency For Customers---
//------------------------------------------
//---Get Serverside RowData
CustomerOrderTDS.CustomersRow[] customerErrorRows
= (CustomerOrderTDS.CustomersRow[])m_dataContainer.Customers.GetErrors();
//---Merge with local DataSet
foreach (CustomerOrderTDS.CustomersRow customerErrorRow in customerErrorRows)
{
m_dataContainer.Customers.Merge(dataService.GetCustomerRow(customerErrorRow.CustomerID));
}
//--- Remap changed Data to object Data
foreach (CustomerBO customerObject in p_addedOrModifiedCustomers)
{
//--- Get the Updated Row from our DataContainer
CustomerOrderTDS.CustomersRow customerRow =
(CustomerOrderTDS.CustomersRow)m_dataContainer.Customers.FindByCustomerID(customerObject.CustomerId);
//--- Only Update Error Objects
bool isErrorRow = false;
foreach (CustomerOrderTDS.CustomersRow customerErrorRow in customerErrorRows)
{
if (customerErrorRow.CustomerID == customerObject.CustomerId)
{
isErrorRow = true;
break;
}
}
if (customerRow != null && isErrorRow)
{
ORM.RelationalToObject(customerObject, customerRow);
}
}
}
//------------------------------------------
//--- Resolve DbConcurrency For Orders ---
//------------------------------------------
if (m_dataContainer.Orders.HasErrors)
{
//--- Get ServerSideData
CustomerOrderTDS.OrdersRow[] orderErrorRows
= (CustomerOrderTDS.OrdersRow[])m_dataContainer.Orders.GetErrors();
//--- Merge with local DataSet
foreach (CustomerOrderTDS.OrdersRow orderErrorRow in orderErrorRows)
{
m_dataContainer.Orders.Merge(dataService.GetOrderRow(orderErrorRow.OrderID));
}
//--- Remap changed Data to object Data
foreach (OrderBO orderObject in p_addedOrModifiedOrders)
{
//--- Get the Updated Row from our DataContainer
CustomerOrderTDS.OrdersRow orderRow =
(CustomerOrderTDS.OrdersRow)m_dataContainer.Orders.FindByOrderID(orderObject.OrderId);
//--- Only Update Error Objects
bool isErrorRow = false;
foreach (CustomerOrderTDS.OrdersRow orderErrorRow in orderErrorRows)
{
if (orderErrorRow.OrderID == orderObject.OrderId)
{
isErrorRow = true;
break;
}
}
if (orderRow != null && isErrorRow)
{
ORM.RelationalToObject(orderObject, orderRow);
}
}
}
}
}
catch (Exception ex)
{
m_dataContainer.RejectChanges();
updateOK = false;
throw ex;
}
}
}
finally
{
if (updateOK)
{
//--- Accept the Changes on the DataSet
m_dataContainer.AcceptChanges();
}
}
}
#endregion
return 0;
}
瞧… 都讲完了! 希望你喜欢这篇文章.