首页 > 开发 > 综合 > 正文

让DataAdapter实现KeyAndModifiedField更新

2024-07-21 02:26:15
字体:
来源:转载
供稿:网友

本文实验用代码请从这里下载:keyandmodifiedfieldindataadapter.rar

先在sql server 2000中建立一名为dbapp的数据库,然后用查询分析器执行sql-gendb目录下的.sql文件建立student表。

让dataadapter实现keyandmodifiedfield更新

使用dataadapter(这里我用的是sqldataadapter,后面所有dataadapter的地方均指sqldataadapter)进行数据库更新时,可以很容易的实现"只包括主键"、"在where短语中包含所有列"以及"主键和时间戳列"的并发方式,但dataadapter并没有为我们提供"主键和已修改列"的并发模式。因为该种并发模式尽管可以产生精简的update命令,但设计代价比较高。david sceppa在它的《ado.net core reference》一书中仅仅说了一句可以在dataadapter的rowupdateing事件中进行处理,却没有详细的论述。为此我尝试了以此思路实现keyandmodifiedfiled并发模式。

在代码实现过程中,主要遇到的问题包括:

1、缺乏有效的schema信息。由于更新命令中set短语包含的字段以及where短语包含的字段都是动态创建出来的,要根据datarow中修改的列进行创建,因此需要了解字段类型以及长度和其它相关信息。这些信息本应包含在table的schema信息中,但这些信息是设计时有生成器生成的(我反编译了一下dataadapter的designer设计器代码,发现微软通过了com完成的底层实现并用.net进行调用),并且无法在rowupdating事件中获取,因此如何保存足够的schema信息就成为实现的一个难题。

2、动态生成更新命令后要动态的将datarow中的数据存入不同的dataparameter中,如何动态获取不同版本的字段值并填入dataparameter中也具有一定难度。

3、由于缺少必要的schema信息,所以很难获得主键信息。

4、dataset中字段名可能与实际table的字段名不同,它们之间是通过tablemapping完成映射的。在动态生成sql命令时要根据tablemapping中的信息进行处理,不能出现字段名不相符的差错。

针对上面问题,在程序代码实现中主要采取了以下策略:

1、在向导生成dataadapter时采用开放式并发,这样dataadapter的designer会生成所有字段的current与original类型的参数,并且保存在updatecommand的parameters属性中。我的程序在执行时首先备份这些信息到自定义的paramcollection中,将来用parameter名进行检索(dataparameters支持string类型的indexer),这样就省去了了解schema信息的麻烦。但主键信息仍然无法得到很好的解决,只能手工指定。

在程序初始化时会有类似如下几条命令,就是用来保存足够的parameter信息和主键信息的。

private sqlparametercollection paramcollection;private string keyfieldname = "id";paramcollection = sqlupdatecommand1.parameters;

在后面的addparametertocommand方法中,我们只需要根据参数名检索paramcollection,就可以得到对应参数的sqltype,而不再需要schema信息了。

private void addparametertocommand(idbcommand cmd, string paraname){   sqlparameter tmpsqlparameter;   tmpsqlparameter = new sqlparameter();   tmpsqlparameter.parametername = paraname;   tmpsqlparameter.sqldbtype = this.paramcollection[paraname].sqldbtype;   tmpsqlparameter.sourceversion = this.paramcollection[paraname].sourceversion;   tmpsqlparameter.sourcecolumn = this.paramcollection[paraname].sourcecolumn;   tmpsqlparameter.size = this.paramcollection[paraname].size;   tmpsqlparameter.direction = this.paramcollection[paraname].direction;   tmpsqlparameter.precision = this.paramcollection[paraname].precision;   cmd.parameters.add(tmpsqlparameter);}

2、通过使用reflactor反编译dataadapter类,可以看到里面已经有了一个名为parameterinput的方法就是根据datarow中的数据向sqlcommand里面填写参数用的,只是为internal类型。我将其拷贝出来,放到了我的程序代码中发挥作用,不过还需要做一些小的改动。

3、主键信息只能自己手工指定,由于在程序中没有获取数据库的schema信息,所以只能手工指定。如果需要了解schema信息,也可以自己设计程序实现。david sceppa在《ado.net core reference》一书提供的工具中给了一个dataadapter builder工具,是用vb.net写的,里面实现的读取数据库表的schema信息功能,可供参考。代码可以从书配套光盘cd下载(http://www.wenyuan.com.cn/soft_show.asp?softid=34)。

4、在rowupdating事件中,我们可以通过sqlrowupdatingeventargs得到所需的datarow和tablemapping以及相关的statementtype信息。然后利用这些信息实现动态生成更新命令,最后填入所需参数并执行。于是,我们便实现了keyandmodified方式更新数据。

private void dastudent_rowupdating(object sender, system.data.sqlclient.sqlrowupdatingeventargs args){   //-- 在这段程序中我们只拦截update命令   if(args.statementtype != statementtype.update)      return;   string strmsg;   strmsg = "beginning update.../r/n";   strmsg += "/r/n----------------------------/r/n";            sqlcommand cmd = generateupdatecommand(args.row, args.tablemapping, true);   cmd.connection = args.command.connection;   cmd.transaction = args.command.transaction;   args.command = cmd;   string p = parameterinput(args.command.parameters, args.statementtype, args.row, args.tablemapping);   strmsg += "command text:/r/n/r/n";   strmsg += args.command.commandtext + "/r/n/r/n----------------------------/r/n/r/n";   strmsg += p;   this.txtmessages.text = strmsg;}private sqlcommand generateupdatecommand(datarow row, datatablemapping mappings, bool refreshrowafterupdate){   sqlcommand cmd = new sqlcommand();   string paraname="";   string tablename = mappings.datasettable;   stringbuilder commandtextbuilder = new stringbuilder();   stringbuilder modifiedfieldsbuilder = new stringbuilder();   stringbuilder whereclausebuilder = new stringbuilder();   stringbuilder tablefieldsbuilder = new stringbuilder();   commandtextbuilder.append("update " + delimit(tablename));   foreach(datacolumnmapping map in mappings.columnmappings)   {      // 判断该列是否发生修改      if(!row[map.datasetcolumn, datarowversion.current].equals(row[map.datasetcolumn, datarowversion.original]))      {         if (modifiedfieldsbuilder.tostring() != "")          {            modifiedfieldsbuilder.append(", ");         }                        paraname = "@" + map.sourcecolumn + "current";         modifiedfieldsbuilder.append(delimit(map.sourcecolumn) + " = " + paraname);         addparametertocommand(cmd, paraname);      }   }   commandtextbuilder.append(" set " + modifiedfieldsbuilder.tostring());   // 添加主键约束   paraname = "@" + this.keyfieldname + "original";   whereclausebuilder.append(delimit(this.keyfieldname) + " = " + paraname);   addparametertocommand(cmd, paraname);   foreach(datacolumnmapping map in mappings.columnmappings)   {      // 判断该列是否发生修改      if(!row[map.datasetcolumn, datarowversion.current].equals(row[map.datasetcolumn, datarowversion.original]))      {         if (whereclausebuilder.tostring() != "")          {            whereclausebuilder.append(" and ");         }         paraname = "@" + map.sourcecolumn + "original";         whereclausebuilder.append(delimit(map.sourcecolumn) + " = " + paraname);         addparametertocommand(cmd, paraname);      }   }   commandtextbuilder.append(" where " + whereclausebuilder.tostring());            if (refreshrowafterupdate)    {      foreach(datacolumnmapping map in mappings.columnmappings)      {         if (tablefieldsbuilder.tostring() != "")          {            tablefieldsbuilder.append(", ");         }         tablefieldsbuilder.append(delimit(map.sourcecolumn));      }      tablefieldsbuilder.append(" from " + tablename + " where " +           delimit(this.keyfieldname) + " = @" + this.keyfieldname + "original");      commandtextbuilder.append("; /r/n/r/nselect " +             tablefieldsbuilder.tostring());   }   cmd.commandtext = commandtextbuilder.tostring();   return cmd;}

通过这种方式更新数据可以减少update命令的复杂度,尤其是在网络带宽受到限制的时候,能够减少命令长度,提高通讯效率。但程序编写比较麻烦。在上面的程序中仅仅实现了update命令的keyandmodifiedfield更新,更完整的代码可留给读者自己去设计。贴张图上来:

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表