上一篇讲到WCF如何正确调用LINQ TO SQL,只涉及到了新增记录的操作。到于为什么要把更新操作分开来讲呢?因为更新确实有点麻烦,相对于新增操作来说,稍微有点难。
还是使用上一篇的项目,没有源码的兄弟可以到这里下载上一篇的项目:http://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.
成功!

欢迎转载,但请注明出处--梁规晓博客(http://www.cnblogs.com/viter/)!
下一篇将介绍“如何将WCF和WF无缝的结合”。谢谢大家的关注!
点击下面下载服务端和客户端源代码.
http://files.cnblogs.com/viter/WCF2.rar
说得不对的地方,欢迎拍砖!
摘要: 在上一篇,我们说到了通过设置Linq上下文的Serialization Mode属性为Unidirectional,使Linq自动支持WCF的序列化。在这一篇我想说一下如何设置Linq实体类各种属性使WCF可以正确的调用Linq完成数据操作,并通过一些异常来说明为何要那样去做,并且在这一次,我会加入一些调料,比如工厂模式。好吧,让我们马上开始!项目还是使用上一篇所建立的采购项目,如果有不明白的朋友...
阅读全文
这篇文章准备了很长时间,真的是没时间写.今天就来完善吧!在我之前的Linq对WCF序列中的解决方案(参考http://www.cnblogs.com/viter/archive/2008/03/27/1126203.html),我说过这个方案并不很
好,一直在寻找更好的解决方案.
那我们就以一个"商品采购"来作为开篇吧.这个"商品采购"的解决方案将会在这个系列中一直存在.废话少说,来看需求说明.
客户在登录网上采购系统后,将会进行采购操作,涉及"采购入库,采购结算",为了让大家更容易理解,这个采购将会很粗糙(简约而不简单),呵呵
.OK,来看,数据库将会产生二张表,采购订单头(PurchaseOrderHeader),采购订单细目(PurchaseOrderDetail);这个数据库使用了
微软的示例数据库(AdventureWorks).
首先我们建立一个解决方案: PurchaseDemo
添加一个类库,接着在类库里面添加Linq to sql类,并取名为Purchase,系统将会自动帮我们重命名为:PurchaseDataContext,
好了,现在切换到设计视图,什么都没有,没关系,把建好的表拖进来吧!
细心的朋友可能会发现,这两张表都有一个字段叫做(timestamp),类型和名称一样的,后面我们再介绍是干啥用的.现在没必要,只要你注意到它的存在就可以了.
选择PurchaseOrderHeader(把自增列和RowGuid及timestamp列都设置为自动生成),就是把Auto Generated value 设为true,PurchaseOrderDetail表也如法炮制.

好,现在来看最重要的,不要选中任何表,在dbml的设计页面上右击选择属性,把Sericlization Mode值改为:Unidirectional.

好了,现在保存文件,打开dbml的代码看看每个类的头顶多了什么东西?

类中的属性又多了什么东西,天呀,漂亮的帽子(DataContact和DataMember).很好,这就是我们想要的,只要
我们把Linq上下文的序列化模式改为单向,它将会自动的支持WCF的序列化,真的就这么简单?对对对,真的就是这么简单!
现在你的Linq to sql中的类已经可以支持WCF直接序列化到客户端了,但是请注意,有一点小小的遗憾.就是自从Linq上下文改为序列化单向后,所有的关系都只能在服务器使用,客户端是一点都没有的,唉~~,难道天要绝我?不要!
突然被电击了一下,灵光突现.Partial关键字,对,就是它了.马上动手,为PurchaseOrderDetail和PurchaseOrderDetail添加一个部分类,
并添加两类之间的关系,并手动加上DataMember标记为可支持WCF序列化,
以后不管DBML文件怎么改动,这个关系都是可以不变的.解决!当然,如果你现在要用这两个类直接进行数据操作的话
,不好意思,是会出错的.这一篇只是介绍"Linq对WCF的序列化支持",关于数据操作,将会在下一篇继续...
如果有说得不对的地方,欢迎拍砖~.
点击这里下载源代码(内含sql文件).
http://files.cnblogs.com/viter/PurchaseDemo.rar