您的位置:首页 > 其它

Winform开发之离线式WCF开发框架的实现介绍

2012-12-01 12:43 441 查看
在上篇随笔《Winform开发框架之框架演化》中介绍了几种Winform开发框架,其中有对于离线式WCF开发框架的介绍,离线式的WCF开发框架 ,就是结合了传统Winform开发框架的数据访问方式,又利用了WCF分布式数据获取的特点,使得数据可以离线使用,在一种业务要求集中化,又要求不影响正常业务操作的应用系统场景下比较适合。本文主要介绍如何利用我的Winform开发框架的整体思路,实现WCF开发框架的离线式的数据上传、更新的同步操作。

其实目前企业集中化管理,这种模式要求很多,如一些加盟店的情况,需要独立运行,有可以对一些总店关键数据进行提交或者下载,如客户信息等。这种情况下,就要求我们开发者提供适合应用场景的开发框架进行支持。离线式的WCF开发框架,一个特点就是基本上显示,以及保存等操作数据库的数据,都是本地的数据库,不是远端的服务器数据库,这样,就需要记录所有发生变更的数据库操作,包括写入,删除、修改等,以便在网络畅通的情况下,可以上传数据到服务器上面。



下面我们来分析下这种离线式的WCF开发框架,需要做哪些准备工作,来实现框架的支撑。

1、数据库表记录ID定义唯一性。

这个是常见分布式系统的要求了,在一些普通的Winform程序的数据库中也比较常见,之所以把它作为第一条,虽然简单,但是很必要,因为需要避免分布式的客户端和服务端的数据冲突问题,特别在多个客户端的情况下,对数据的唯一性要有好的控制性。

所以这也要求基础的框架基类,能够提供对整形、字符型的主键ID的操作兼容性,这在我的Winform开发框架中,支持是比较好的。

2、多数据库支持

在分布式的环境下,和服务端的环境不同,部署程序要求越简单越好,太复杂的话,增加客户端的使用的难度,会极大提高维护的成本,因此,一般客户端会选用适应性比较好,又免安装的数据库,如Sqlite就是一个很好的单机版数据库,还有Access也是很不错的,当然还有其他的一些数据库,不过我觉得Sqlite和Access是比较好的备选方案。服务器端的数据库,则看业务支持和响应程度来决定,可以从一些对性能支持比较好的数据库中选型,如大型一点的,可选择Oracle来做,其他的可以选择SqlServer、MySql等数据库。虽然这些数据库部署比较麻烦一点,不过反正只有一台服务器需要这种安装部署,所以工作难度及工作量不会很大。

对多数据库的支持,也要求我们的开发框架能够很好兼容,最好在数据库操作层可以通过配置方式进行切换,即使数据库变化为其他类型,也不需要改变整体的框架布局,甚至不用变化代码即可实现自由切换,如数据库框架可以设置如下。



对于上面几种数据库的支持,一般来说,需要增加不同数据库类型的BaseDAL,由于每个不同数据库都需要拥有一个BaseDAL,那么很多相同的操作代码就会发生冗余,因为大多数数据库的基础操作是一样的,只有一部分比较特别,需要进行个性化处理,因此对数据访问层进行优化设计,得到下面的设计图,如下所示。

经过框架抽象,这个BaseDAL类代码很少,基本上通用的数据库操作,已经放到了AbStractBaseDAL超级基类进行封装,即使对于一些不同数据库操作不同,我们也尽可能抽象放到上面基类了,BaseDAL只需要实现一些特殊的操作即可。



3、分布式客户端数据上传设计

由于分布式,离线式的框架设计,要求我们客户端自行记录数据的变化情况,包括新增数据、修改数据和删除数据,这样不用每次同步的时候,把所有的数据库记录都遍历一次,然后和服务器记录进行比较。这种记录方式,可以极大提高客户端数据上传的性能和快捷性。因为我们对于很多表及记录的数据库,可能每次更新的只是一小部分,这样设计,有利于我们更好地额处理客户端数据上传。

例如,下面的表,就是对于一个客户端上传记录表的设计,其中Dept_ID是用来记录不同部门的表示,基本上每个客户端,都有自己的一个部门编号,防止数据发生冲突,也方便服务器端的数据进行归类查询。



下面是一些实际业务产生的数据记录,我们记录部门ID、表名(发生变化)、对应记录的ID(GUID)、修改用户、修改时间等信息。



另外,我们还可以结合系统来记录用户登录信息、用户对记录修改的日志,以便我们对一些关键操作进行审计需要。数据库设计如下所示。



4、数据修改记录自动记录

对于上面的to_upload表,我们是把客户端修改的数据记录信息,记录到表里面去,但是这些肯定是后台自动记录的,而且这个操作是放到基类比较合适,否则每次调用,不太方便,也比较冗余。

放到基类的操作,我们需要设计一下,否则所有的表都会记录,不管需不需要,这样不可以的。

首先我们在基类BaseDAL(对Sqlite的数据库基类),增加一个变量来记录是否数据库访问基类,需要记录数据库变化信息。

protected bool IsLogToUpoad = false; //表示是否记录变化


对于具体业务对象的数据访问,我的Winform开发框架都有提供一个对应的类来进行操作。

/// <summary>
/// 药品信息
/// </summary>
public class DrugDetail : BaseDAL<DrugDetailInfo>, IDrugDetail
{
#region 对象实例及构造函数

public static DrugDetail Instance
{
get
{
return new DrugDetail();
}
}
public DrugDetail() : base("M_DrugDetail","ID")
{
this.IsLogToUpoad = true;
this.sortField = "EditTime";
this.isDescending = true;
}

#endregion
..........................................


为了要实现自动记录数据库变化信息,我们需要在BaseDAL里面对插入、修改、删除的操作进行特别的处理,重载基类的操作,增加相应的处理即可,如下代码所示。

private void AddToUpload(string id, string targetTable, System.Data.Common.DbTransaction trans, int uploadType)
{
AppConfig config = new AppConfig();

ToUploadInfo info = new ToUploadInfo();
info.EditTime = DateTime.Now;
info.RecordId = id;
info.TableName = targetTable;
info.UploadType = uploadType;
info.Dept_ID = config.AppConfigGet("Dept_ID"); ;
info.User_ID = config.AppConfigGet("User_ID");
Hashtable uploadHash = GetHashByObject(info);
base.Insert(uploadHash, "SS_ToUpload", trans);
}

public override bool PrivateUpdate(object id, Hashtable recordField, string targetTable, DbTransaction trans)
{
bool result = base.PrivateUpdate(id, recordField, targetTable, trans);
if (result && IsLogToUpoad)
{
AddToUpload(recordField["ID"].ToString(), targetTable, trans, 1);
}
return result;
}

public override bool Insert(System.Collections.Hashtable recordField, string targetTable, System.Data.Common.DbTransaction trans)
{
bool result = base.Insert(recordField, targetTable, trans);
if (result && IsLogToUpoad)
{
AddToUpload(recordField["ID"].ToString(), targetTable, trans, 0);
}
return result;
}


由于是上面的PrivateUpdate和Inser方法,是所有派生的更新、插入接口的最原始的函数,所有其他相关函数都会调用这两个的基础函数, 这样就基本实现了数据库记录的变化记录了。

5、分布式客户端数据和服务器端的同步

为了和服务器实现同步,需要实现变化记录的上传和服务器修改数据的下载两个方向的工作。

变化记录的上传从操作,就是遍历to_upload里面的记录,把它更新到服务器上即可。

/// <summary>
/// 把本地变化的数据记录,同步到服务器上
/// </summary>
/// <returns></returns>
public bool SyncAll()
{
List<ToUploadInfo> toList = BLLFactory<ToUpload>.Instance.GetAll();
int i = 1;
int total = toList.Count;
bool success = false;

foreach (ToUploadInfo toInfo in toList)
{
switch(toInfo.TableName.ToLower())
{


case "m_drugusedetail":
success = DealDrugUseDetail(toInfo);
if (!success) return false;
break;


.......................................
//其他操作,利用服务器代理对象,实现各个表的数据上传
}

#region 显示进度等处理
string tips = string.Format("正在同步表 {0}...", toInfo.TableName);
int step = 0;
if (total > 0)
{
step = Convert.ToInt32((100.0 / (1.0 * total)) * i);
}
if (OnDataDealed != null)
{
OnDataDealed(step, tips);
}
i++;

if (toInfo == null || string.IsNullOrEmpty(toInfo.TableName))
{
continue;
}
#endregion
}

#region 同步系统关键数据
if (success)
{
//部门
SynAllDept();

//已上传数据表同步
DealUploaded();

SyncBasicData();
}

#endregion

return true;
}


为了实现数据上传操作,我们把逻辑封装在一个函数里面,这样方便管理,也方便阅读。

private bool DealDrugUseDetail(ToUploadInfo toInfo)
{
bool success = false;
DrugUseDetailInfo objInfo = BLLFactory<DrugUseDetail>.Instance.FindByID(toInfo.RecordId);
if (objInfo != null && objInfo.Dept_ID == Portal.gc.LoginInfo.Dept_ID)
{
new DrugUseDetailServiceClient().Using(client =>
{
success = client.InsertUpdate(objInfo, objInfo.ID);
});

if (success)
{
RemoveToUploadInfo(toInfo);
}
}
return success;
}


数据同步的下载操作,其实也不难,就是把数据对应的记录下载下来进行判断。

private void DealInHospital()
{
List<InHospitalInfo> list = new List<InHospitalInfo>();
new InHospitalServiceClient().Using(client =>
{
list = client.Find(conditionPilotDept);
});

int i = 1;
int total = list.Count;
foreach (InHospitalInfo info in list)
{
BLLFactory<InHospital>.Instance.InsertUpdate(info, info.ID);
ShowProgress(total, i++, "住院信息");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: