上一篇讲到WCF如何正确调用LINQ TO SQL,只涉及到了新增记录的操作。到于为什么要把更新操作分开来讲呢?因为更新确实有点麻烦,相对于新增操作来说,稍微有点难。
还是使用上一篇的项目,没有源码的兄弟可以到这里下载上一篇的项目:https://files.cnblogs.com/viter/WCF.rar。
首先我们新建一个WinForm窗体UpdateForm,在客户端界面添加一个DataGridView,用来存储从服务读取到的数据,命名为:dgvPurchaseOrder,并在代码中设置自动生成列为False。其实用BindingSource更快,呵呵。
那好,还是使用BindingSource,不过这个BindingSource是自定义的,因为用来做数据显示控制没有比它更称职的了。
BindingSource
private BindingSource bdsOrder = new BindingSource();
private PurchaseOrderHeader purchaseOrderHeader;
public UpdateOrderForm()
{
InitializeComponent();
bdsOrder.CurrentChanged += new EventHandler(bdsOrder_CurrentChanged);
}
为这个BindingSource定义一个事件,就是BindingSource的CurrentChanged事件。
这个事件里的代码如下,很简单。
CurrentChanged
void bdsOrder_CurrentChanged(object sender, EventArgs e)
{
try
{
purchaseOrderHeader = bdsOrder.Current as PurchaseOrderHeader;
txtOrderID.Text = purchaseOrderHeader.PurchaseOrderHeaderId.ToString();
txtDueTotal.Text = purchaseOrderHeader.DueTotal.ToString();
txtOrderDate.Text = purchaseOrderHeader.ModifiedDate.ToString();
}
catch (Exception ex)
{
MessageBox.Show("数据显示出错"+ex.Message,"出错");
}
}
在界面加载的事件从服务器调取数据
client
try
{
PurchaseOrderClient client = new PurchaseOrderClient();
bdsOrder.DataSource = client.QueryAllOrder();
dgvPurchaseOrder.DataSource = bdsOrder;
}
catch (Exception ex)
{
MessageBox.Show("服务器调用出错!"+ex.Message);
}
现在我们的数据出来了,好,我们修改一下订单总额,提交到服务器。
我们将出错信息写到控制台,好好欣赏一下吧,呵呵。好像是真的一样。
为什么会出现这个错误?我们还是来调试一下,看得更仔细点。
看,好像告诉我们,你要修改的数据已经存在,不能添加。想想有点不妥啊!非常不妥,我不是要添加数据,是要修改数据,为什么会提示“无法附加已经存在的实体”?好,注意看,MS用词还是很恰当的,看这一句:“无法附加已经存在的实体”,注意,是实体!!!那意思是我们的操作没有任何的问题,是实体出了问题,再想一下,我们的实体是通过什么方式来取得的呢?你肯定在电光火石之间想到了,是DataContext,对,是数据上下文,如果你对单张表进行操作,我敢保证你一辈子也不会遇上这个异常,真的不会。好了,现在我们来看下数据库关系图
这两张表是有关联的,不是独立的表。可能这样还是有点看不明白,不要紧,再看一下生成的DBMl文件。
PurchaseOrderHeader类里面有一个属性
EntitySet
private EntitySet<PurchaseOrderDetail> _PurchaseOrderDetail;
[Association(Name="PurchaseOrderHeader_PurchaseOrderDetail", Storage="_PurchaseOrderDetail", OtherKey="PurchaseOrderHeaderId")]
[DataMember(Order=6, EmitDefaultValue=false)]
public EntitySet<PurchaseOrderDetail> PurchaseOrderDetail
{
get
{
if ((this.serializing
&& (this._PurchaseOrderDetail.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._PurchaseOrderDetail;
}
set
{
this._PurchaseOrderDetail.Assign(value);
}
}
相对的,PurchaseOrderDetail类里面也有一个对应的属性,用来关联
EntityRef
private EntityRef<PurchaseOrderHeader> _PurchaseOrderHeader;
[Association(Name="PurchaseOrderHeader_PurchaseOrderDetail", Storage="_PurchaseOrderHeader", ThisKey="PurchaseOrderHeaderId", IsForeignKey=true)]
public PurchaseOrderHeader PurchaseOrderHeader
{
get
{
return this._PurchaseOrderHeader.Entity;
}
set
{
PurchaseOrderHeader previousValue = this._PurchaseOrderHeader.Entity;
if (((previousValue != value)
|| (this._PurchaseOrderHeader.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._PurchaseOrderHeader.Entity = null;
previousValue.PurchaseOrderDetail.Remove(this);
}
this._PurchaseOrderHeader.Entity = value;
if ((value != null))
{
value.PurchaseOrderDetail.Add(this);
this._PurchaseOrderHeaderId = value.PurchaseOrderHeaderId;
}
else
{
this._PurchaseOrderHeaderId = default(int);
}
this.SendPropertyChanged("PurchaseOrderHeader");
}
}
}
可以看到,这两个属性是标记数据库两张表的一对多的关系。
那么我们分析一下,在我们从数据库检索数据的时候,这个关系是否存在?答案是肯定的!
不好意思,这个图切不下来,各位可以在这里设置断点查看
通过这里可以看到,关系是存在的,只是由于Linq的延迟加载的特性,数据并没有读取出来而已。
那么我们就要问了,在查询的时候会有关系,那么向数据库添加数据的时候呢?看一下下面的表格,已经有很好的答案
好了,既然要关系,我们就加一个试试,在这之前你需要引用System.Data.Linq.dll,并引用System.Data.Linq命名空间。
加关系很简单,在原来的Update方法里面加一个判断就可以了,如果是修改或删除的操作,那么我们就给它加上关系。
1~~*和*~~1的关系设置不一样,注意代码里面的设置。
关系
#region 添加订单头
//设置行最后修改时间
foreach (var item in purchaseOrderHeaderList)
{
//如果是新增或者是删除操作,则设置关系
if (item.CurrentStatus == EntityStatus.Update || item.CurrentStatus == EntityStatus.Delete)
{
//一对多的关系
item.PurchaseOrderDetail = default(EntitySet<PurchaseOrderDetail>);
}
item.ModifiedDate = DateTime.Now;
}
result = Common<PurchaseOrderHeader, PurchaseOrderDetail>.GenericUpdate(purchaseOrderHeaderList, "PurchaseOrderDetail", PurchaseTable.PurchaseOrderDetail);
#endregion
#region //添加订单细目
foreach (var headeritem in purchaseOrderHeaderList)
{
if (headeritem.PurchaseOrderDetails != null)
{
foreach (var detailitem in headeritem.PurchaseOrderDetails)
{
//如果是新增或者是删除操作,则设置关系
if (detailitem.CurrentStatus == EntityStatus.Update || detailitem.CurrentStatus == EntityStatus.Delete)
{
//多对一的关系
detailitem.PurchaseOrderHeader = new PurchaseOrderHeader() { PurchaseOrderHeaderId = detailitem.PurchaseOrderHeaderId };
}
//订单头添加完成后,将会产生一个新的PurchaseOrderHeaderId
detailitem.PurchaseOrderHeaderId = headeritem.PurchaseOrderHeaderId;
//设置行最后修改时间
detailitem.ModifiedDate = DateTime.Now;
detailList.Add(detailitem);
}
}
}
#endregion
好,我们现在来修改订单编号为1的记录,将订单总额改为5000.
成功!
欢迎转载,但请注明出处--lostcode博客(http://www.cnblogs.com/viter/)!
下一篇将介绍“如何将WCF和WF无缝的结合”。谢谢大家的关注!
点击下面下载服务端和客户端源代码.
https://files.cnblogs.com/viter/WCF2.rar
说得不对的地方,欢迎拍砖!