在上一篇,我们说到了通过设置Linq上下文的Serialization Mode属性为Unidirectional,使Linq自动支持WCF的序列化。
在这一篇我想说一下如何设置Linq实体类各种属性使WCF可以正确的调用Linq完成数据操作,并通过一些异常来说明为何要那样去做,并且在这一次,我会加入一些调料,比如工厂模式。
好吧,让我们马上开始!项目还是使用上一篇所建立的采购项目,如果有不明白的朋友,请下载上一篇的源代码。
说到做到,首先,
在上一篇,我们说到了通过设置Linq上下文的Serialization Mode属性为Unidirectional,使Linq自动支持WCF的序列化。
在这一篇我想说一下如何设置Linq实体类各种属性使WCF可以正确的调用Linq完成数据操作,并通过一些异常来说明为何要那样去做,并且在这一次,我会加入一些调料,比如工厂模式。
好吧,让我们马上开始!项目还是使用上一篇所建立的采购项目,如果有不明白的朋友,请下载上一篇的源代码。
说到做到,首先,
1、我们要建立一个WCF的项目,取名为:PurchaseOrderService,这样一来,这个WCF的类库会为我们自动添加两个.cs文件,我一般将其删除。再添加一个接口
命名为:IPurchaseOrder,
重新引用命名空间
using System.ServiceModel;和添加PurchaseDemo的引用;
在IPurchaseOrder这个接口里,暂时只有一个方法:
bool UpdatePurchaseOrder(List<PurchaseOrderHeader> purchaseOrderHeaderList);
再为IPurchaseOrder添加一个服务实现类,为简单起见,我直接写在同一个文件中,没有任何实现,直接返回false。
2、为了达到松耦合的目,我们加入一个工厂类,但加入之前我们需要建立数据访问层的实现类和接口,好吧,马上来做
添加一个IDAL类库,IPurchaseOrderDAL接口,PurchaseOrderDAL类,看名字就知道,PurchaseOrderDAL类必定实现了IPurchaseOrderDAL接口
(实际上在应用中,接口类库和实现类应该在不同的类库中)。
并添加对PurchaseDemo类库的引用.OK,
这个新建的IDAL接口也是简单到极点,只有一个方法,和WCF服务一模一样。
3、接下来该做点什么呢?
当然是建立数据操作工厂类,新建一个类库,取名为:DALFactory,里面有一个方法
好的,编译整个解决方案,没有发生任何异常。
4、上回说到,这篇里将会介绍一个数据操作通用的方法,很多朋友也很关注这个方法,为了让大家有一个直观的印象,我先贴图
,再慢慢细说
为了这个方法,我们还得添加一个类库,取名为:Util,并添加一个Common<T,F>类。代码有点长,我就直接贴代码了。
Code
namespace Util
{
/// <summary>
/// 定义通用的数据库操作方法,包括增、删、改
/// </summary>
/// <typeparam name="T"></typeparam>
public class Common<T,F>
{
/// 通用的主从表更新方法
/// </summary>
/// <param name="items">包含主表记录的集合</param>
/// <param name="detailTableName">从表的名称</param>
/// <param name="table"></param>
/// <returns></returns>
public static ExcuteResult GenericUpdate(List<T> items, string detailTableName, PurchaseTable table)
{
PurchaseDataContext dbContext = new PurchaseDataContext();
ExcuteResult flag = new ExcuteResult();
flag.ReturnFlag = ExcuteStatus.Fail;
try
{
ITable headerTable = dbContext.GetTable(typeof(T));
//添加头信息
headerTable.AttachAll(items.Where(h => (h as BaseEntity).CurrentStatus == EntityStatus.Update || (h as BaseEntity).CurrentStatus == EntityStatus.Delete), true);
headerTable.InsertAllOnSubmit(items.Where(h => (h as BaseEntity).CurrentStatus == EntityStatus.New));
headerTable.DeleteAllOnSubmit(items.Where(h => (h as BaseEntity).CurrentStatus == EntityStatus.Delete));
if (detailTableName != null)
{
//添加明细
foreach (T item in items)
{
IEnumerable<F> details = null;
// item : 主表
// detailType : 从表类型
// detailTable : 从表
// d : lamda 表达式中的从表
Type detailType = Assembly.Load("PurchaseDemo").CreateInstance("PurchaseDemo." + detailTableName).GetType();
System.Data.Linq.ITable detailTable = dbContext.GetTable(detailType);
PropertyInfo field = item.GetType().GetProperty(detailTableName);
details = GetDetail(table, item, field);
detailTable.AttachAll(details.Where
(d => (d as BaseEntity).CurrentStatus == EntityStatus.Update
|| (d as BaseEntity).CurrentStatus == EntityStatus.Delete
|| (item as BaseEntity).CurrentStatus == EntityStatus.Delete
|| (item as BaseEntity).CurrentStatus == EntityStatus.Update));
detailTable.InsertAllOnSubmit(details.Where
(d => (d as BaseEntity).CurrentStatus == EntityStatus.New
&& (d as BaseEntity).CurrentStatus != EntityStatus.Delete));
detailTable.DeleteAllOnSubmit(details.Where(
d => (d as BaseEntity).CurrentStatus == EntityStatus.Delete || (item as BaseEntity).CurrentStatus == EntityStatus.Delete));
}
}
dbContext.SubmitChanges();
flag.ReturnFlag = ExcuteStatus.Success;
}
catch (Exception ex)
{
throw ex;
}
return flag;
}
/// <summary>
/// 根据表的种类,返回一条主表记录的相关从表记录
/// </summary>
/// <param name="table">从表类型</param>
/// <param name="item">主表记录</param>
/// <param name="field">从表对应的从表属性值</param>
/// <returns></returns>
private static IEnumerable<F> GetDetail(PurchaseTable table, T item, PropertyInfo field)
{
IEnumerable<F> details = null;
switch (table)
{
case PurchaseTable.PurchaseOrderDetail:
EntitySet<PurchaseOrderDetail> PurchaseOrderDeatail_EntitySet = (EntitySet<PurchaseOrderDetail>)field.GetValue(item, null);
details = PurchaseOrderDeatail_EntitySet.Cast<F>();
break;
default:
break;
}
return details;
}
}
}
在这个类中,我们会看到,有一些以前没有看到的类型出现了,包括BaseEntity类,ReturnFlag类,PurchaseTable枚举,ExcuteStatus枚举.这四个是什么东西呢?
在之前我们一直没有提到过为什么我们的服务类和数据操作类只会有一个方法,而正是这四个类型使得了我们的服务和数据操作类能够
如此的干净,这个Common<T,F>有点复杂,如果有需要,这个系列完毕后回头重点介绍一下。
1>BaseEntity类:维护了我们所要进行更新的数据的状态(update,delete,new)
2>ReturnFlag类:维护了数据操作后要返回的状态,主要是为了支持WF(workflow),后面我们的这个项目还会加入工作流,所以
提前做点准备。
3>PurchaseTable枚举,定义了一组Linq上下文中的实体名映射
这些都是必不可少的。好了,下面继续
5、接着把上面提到的四种类型添加到PurchaseDemo命名空间中。
为了使用这四个新建的类型支持WCF,我们还需要做什么?当然是加上序列化标记
直接贴代码。
Code
namespace PurchaseDemo
{
/// <summary>
/// 标识实体当前状态
/// </summary>
public enum EntityStatus
{
None = 0,
New = 1,
Update = 2,
Delete = 3,
}
/// <summary>
/// 定义当前数据的状态
/// </summary>
[DataContract]
public class BaseEntity
{
[DataMember]
public EntityStatus CurrentStatus { set; get; }
}
/// <summary>
/// 数据操作执行的状态
/// </summary>
public enum ExcuteStatus
{
Fail=1,//失败
Success=2//成功
}
/// <summary>
/// 返回数据操作执行的状态
/// </summary>
[DataContract]
public class ExcuteResult
{
[DataMember]
public ExcuteStatus ReturnFlag { set; get; }
}
/// <summary>
/// 定义了一组Linq上下文中的实体名映射,Common类中使用
/// </summary>
public enum PurchaseTable
{
PurchaseOrderDetail = 0
}
}
然后,还记得上回我们建立的部分类吗?好的,让它们继承自BaseEntity类,这样它们就有了数据操作的状态
到现在为止,我们没有做对WCF的调用。接下来介绍调用的顺序:图片比较粗糙,
按照上面的流程,我们来调用一下。
首先是WCF通过工厂调用数据操作类,
Code
/// <summary>
/// WCF服务实现类
/// </summary>
public class PurchaseOrder : IPurchaseOrder
{
public bool UpdatePurchaseOrder(List<PurchaseOrderHeader> purchaseOrderHeaderList)
{
//通过工厂调用数据操作方法
IDAL.IPurchaseOrderDAL purchaseOrderDAL = DALFactory.PurchaseOrderFactory.CreaterIPurchaseOrderDAL();
return purchaseOrderDAL.UpdatePurchaseOrder(purchaseOrderHeaderList);
}
}
PurchaseOrderDAL调用Common<T,F>的GenericUpdate()方法的设置。
Code
/// 数据操作实现类,实现了接口
/// </summary>
public class PurchaseOrderDAL : IPurchaseOrderDAL
{
public bool UpdatePurchaseOrder(List<PurchaseOrderHeader> purchaseOrderHeaderList)
{
ExcuteResult result = new ExcuteResult();
List<PurchaseOrderDetail> detailList = new List<PurchaseOrderDetail>();
//因为涉及两张表的操作,在这里使用事务
try
{
using (TransactionScope scope = new TransactionScope())
{
#region 添加订单头
//设置行最后修改时间
foreach (var item in purchaseOrderHeaderList)
{
item.ModifiedDate = DateTime.Now;
}
result = Common<PurchaseOrderHeader, PurchaseOrderDetail>.GenericUpdate(purchaseOrderHeaderList, "PurchaseOrderDetail", PurchaseTable.PurchaseOrderDetail);
#endregion
#region //添加订单细目
foreach (var headeritem in purchaseOrderHeaderList)
{
foreach (var detailitem in headeritem.PurchaseOrderDetails)
{
//订单头添加完成后,将会产生一个新的PurchaseOrderHeaderId
detailitem.PurchaseOrderHeaderId = headeritem.PurchaseOrderHeaderId;
//设置行最后修改时间
detailitem.ModifiedDate = DateTime.Now;
detailList.Add(detailitem);
}
}
#endregion
//更新从表
//无从表,所以第二个参数设置为空,如果第二个为空,第三个参数将会自动忽略
result = Common<PurchaseOrderDetail, PurchaseOrderDetail>.GenericUpdate(detailList, null, PurchaseTable.PurchaseOrderDetail);
//提交事务
scope.Complete();
}
}
catch (Exception ex)
{
throw;
}
//返回执行结果
if (result.ReturnFlag == ExcuteStatus.Success)
return true;
else
return false;
}
}
6、接下来建立一个控制台应用程序作为WCF的服务主机,配置好并运行,有关配置方法,参见"张逸"兄弟的博客,
7、到现在为止,我们的程序已初具“分布式应用程序”的雏形了,好好看一下吧
8、好像一切都很顺利,有点不可思议,好吧,实践才是硬道理。先弄个客户端来调用一下服务。
9、不好意思,抛出一个异常
因为我们没有设置:行的上次修改时间,而这个列又没有设置为数据库自动生成列,自然会有异常了,而且这个列也应该是在服务器生成的。
Code
using (TransactionScope scope = new TransactionScope())
{
#region 添加订单头
//设置行最后修改时间
foreach (var item in purchaseOrderHeaderList)
{
item.ModifiedDate = DateTime.Now;
}
result = Common<PurchaseOrderHeader, PurchaseOrderDetail>.GenericUpdate(purchaseOrderHeaderList, "PurchaseOrderDetail", PurchaseTable.PurchaseOrderDetail);
#endregion
#region //添加订单细目
foreach (var headeritem in purchaseOrderHeaderList)
{
foreach (var detailitem in headeritem.PurchaseOrderDetails)
{
//订单头添加完成后,将会产生一个新的PurchaseOrderHeaderId
detailitem.PurchaseOrderHeaderId = headeritem.PurchaseOrderHeaderId;
//设置行最后修改时间
detailitem.ModifiedDate = DateTime.Now;
detailList.Add(detailitem);
}
}
#endregion
//更新从表
//无从表,所以第二个参数设置为空,如果第二个为空,第三个参数将会自动忽略
result = Common<PurchaseOrderDetail, PurchaseOrderDetail>.GenericUpdate(detailList, null, PurchaseTable.PurchaseOrderDetail);
//提交事务
scope.Complete();
这次就先写到这里吧,感觉有点长了,真怕变成又长又臭了。
不足:
这次只涉及到了添加,如果是修改或删除将会有更多的异常,欢迎转载,但请注明出处--lostcode博客(http://www.cnblogs.com/viter/)!
下一篇将介绍修改和删除数据。谢谢大家的关注!
点击下面下载服务端和客户端源代码.
https://files.cnblogs.com/viter/WCF.rar
说得不对的地方,欢迎拍砖!