使用poFetchDetailsOnDemand刷新嵌套数据集 [英] Refresh Nested DataSet with poFetchDetailsOnDemand

查看:208
本文介绍了使用poFetchDetailsOnDemand刷新嵌套数据集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



这是我迄今为止所尝试的:

  DM.ClientDataSet2.Refresh; 
DM.ClientDataSet2.RefreshRecord;

我也尝试过:

  DM.ClientDataSet1.Refresh; 

但是上述方法会刷新整个Master数据集,而不仅仅是当前的记录。



现在,以下代码似乎可以做任何事情:

  DM.ClientDataSet1.RefreshRecord; 

有没有解决方法或正确的方式来做我想要的? (可能是插入程序...)



附加信息:



ClientDataSet1 =主数据集



ClientDataSet2 =详细数据集,如下:*

 对象ClientDataSet2:TClientDataSet 
Aggregates =<>
DataSetField = ClientDataSet1ADOQuery2
FetchOnDemand = False
.....
end

提供者属性:

 对象DataSetProvider1:TDataSetProvider 
DataSet = ADOQuery1
选项= [poFetchDetailsOnDemand]
UpdateMode = upWhereKeyOnly
左= 24
顶部= 104
结束


解决方案

Googling发现许多文章说,根本没有关闭和重新打开主CDS的嵌套ClientDataSets是不可能的,OP在这种情况下不想做。然而...



对于q的简短答案是肯定的,在我测试的相当简单的情况下,这很简单,如果有点长时间;找出必要的步骤花了一阵时间才能弄清楚。



代码在下面,包括说明它的工作原理的一些注释,以及一些潜在的问题,以及它们如何避免或使用它们。我只用TAdoQueries进行测试,用于提供CDS的提供者。



当我开始研究这一切时,很快就变得很明显,通常的主人
+细节设置,虽然Providers + CDS很乐意从服务器刷新主数据,但是自从自从服务器读取以来,他们根本不会刷新详细信息记录cdsMaster被打开。这可能是设计当然。



我不认为我需要发布一个DFM去代码。我只是以通常的主 - 详细方式设置AdoQueries(详细查询具有主机的PK作为参数),DataSetProvider指向主AdoQuery,指向提供者的主CDS,以及指向该提供者的详细cDS cdsMaster的DataSetField。要实验并看看发生了什么,每个数据集都有DBGrids和DBNavigator。



简而言之,下面的代码是临时过滤AdoQuery主并将CDS压缩为当前行,然后强制刷新其数据和当前主行的dtail数据。这样做,不像我尝试的任何其他,导致嵌套在cdsMaster的DataSet字段中的详细行刷新。 btw,我尝试过的另一个盲人小巷包含有和没有poFetchDetailsOnDemand设置为true,同上cdsMaster.FetchDetailsOnDemand。显然FetchDetailsOnDemand并不意味着ReFetchDetailsOnDemand!



我遇到一个或两个问题,让我的解决方案工作,这个最棘手的一个在这个SO问题描述:
刷新嵌套在DataSetField中的ClientDataSet



我已经验证了这可以与Sql Server 2000(!)后端正常工作,包括从ISqlW获取在服务器上触发的行数据更改。我还使用Sql Server的Profiler验证了刷新中的网络流量仅涉及单个主行及其详细信息。



Delphi 7 + Win7 64位,btw。

  TForm1.cdsMasterRowRefresh(MasterPK:Integer); 
begin
//以下操作将导致cdsMaster上的光标滚动
//,所以我们需要检查并设置一个标志,以避免重新入侵
如果DoingRefresh出口;
DoingRefresh:= True;

try
//将cdsMaster下载到要刷新的单行。
cdsMaster.Filter:= MasterPKName +'='+ IntToStr(MasterPK);
cdsMaster.Filtered:= True;
cdsMaster.Refresh;
Inc(cdsMasterRefreshes); //只是一个柜台来协助调试

//发布过滤器
cdsMaster.Filtered:= False;

//清除过滤器可能会导致cdsMaster光标移动,所以...
cdsMaster.Locate(MasterPKName,MasterPK,[]);
finally
DoingRefresh:= False;
结束
结束

程序TForm1.qMasterRowRefresh(MasterPK:Integer);
begin
try
//首先,将AdoQuery主控器过滤到cdsMaster当前行
qMaster.Filter:= MasterPKName +'='+ IntToStr(MasterPK);
qMaster.Filtered:= True;

//此时,Ado很乐意从服务器
qMaster.Refresh仅刷新当前的主行;

//注意:
// qDetail AdoQuery上的以下操作的原因是我发现
//在测试情况下,此数据集将不会在这一点上,在刷新操作中
//,所以我们手动更新。我手动执行的原因
//是简单地调用qDetail的刷新引发了Ado不足的键列
//信息进行更新或刷新,尽管它的查询不涉及连接
//并且基础表具有PK

qDetail.Parameters.ParamByName(MasterPKName).Value:= MasterPK;
qDetail.Close;
qDetail.Open;

//现在从服务器重新读取主数据和细节行,我们可以更新
// cdsMaster
cdsMasterRowRefresh(MasterPK);
finally
//现在我们可以清除过滤器
qMaster.Filtered:= False;
qMaster.Locate(MasterPKName,MasterPK,[]);
//显然,如果qMaster被首先过滤,我们需要在
结束后再恢复;
结束

程序TForm1.RefreshcdsMasterAndDetails;
var
MasterPK:整数;
begin
如果cdsMaster.ChangeCount> 0 then
raise Exception.Create(Format('cdsMaster has%d change(s)pending。',[cdsMaster.ChangeCount]));
MasterPK:= cdsMaster.FieldByName(MasterPKName).AsInteger;

cdsDetail.DisableControls;
cdsMaster.DisableControls;
qDetail.DisableControls;
qMaster.DisableControls;

try
try
qMasterRowRefresh(MasterPK);
除了
//根据味道添加异常处理
//我在调试/测试期间没有遇到任何问题:
raise;
结束
finally
qMaster.EnableControls;
qDetail.EnableControls;
cdsMaster.EnableControls;
cdsDetail.EnableControls;
结束
结束

程序TForm1.cdsMasterAfterScroll(DataSet:TDataSet);
begin
RefreshcdsMasterAndDetails;
结束

程序TForm1.cdsMasterAfterPost(DataSet:TDataSet);
//注意:除了cdsMasterAfterScroll之外,调用RefreshcdsMasterAndDetails的原因是
//,因为RefreshcdsMasterAndDetails只刷新当前
// cdsMaster行的master + detail AdoQueries。因此,在当前cdsMaster行或其详细信息
//已更新的情况下,该行在我们离开之前需要刷新处理。
begin
cdsMaster.ApplyUpdates(-1);
RefreshcdsMasterAndDetails;
结束

程序TForm1.btnRefreshClick(Sender:TObject);
begin
RefreshcdsMasterAndDetails;
结束

程序TForm1.cdsDetailAfterPost(DataSet:TDataSet);
begin
cdsMaster.ApplyUpdates(-1);
结束


Is there a way to refresh only the Detail DataSet without reloading all master dataset?

this is what I've tried so far:

DM.ClientDataSet2.Refresh;      
DM.ClientDataSet2.RefreshRecord;

I have also tried:

DM.ClientDataSet1.Refresh;

But the method above refreshes the entire Master dataset, not just the current record.

Now, the following code seems to do anything:

DM.ClientDataSet1.RefreshRecord;

Is there a workaround or a proper way to do what I want? (maybe an interposer...)

Additional Info:

ClientDataSet1 = Master Dataset

ClientDataSet2 = Detail DataSet , is the following: *

object ClientDataSet2: TClientDataSet
    Aggregates = <>
    DataSetField = ClientDataSet1ADOQuery2
    FetchOnDemand = False
    .....
end

Provider properties:

object DataSetProvider1: TDataSetProvider
    DataSet = ADOQuery1
    Options = [poFetchDetailsOnDemand]
    UpdateMode = upWhereKeyOnly
    Left = 24
    Top = 104
  end

解决方案

Googling finds numerous articles that say that it isn't possible at all with nested ClientDataSets without closing and re-opening the master CDS, which the OP doesn't want to do in this case. However ...

The short answer to the q is yes, in the reasonably simple case I've tested, and it's quite straightforward, if a bit long-winded; getting the necessary steps right took a while to figure out.

The code is below and includes comments explaining how it works and a few potential problems and how it avoids or works around them. I have only tested it with TAdoQueries feeding the CDSs' Provider.

When I started looking into all this, it soon became apparent that with the usual master + detail set-up, although Providers + CDSs are happy to refresh the master data from the server, they simply will not refresh the detail records once they've been read from the server for the first time since the cdsMaster was opened. This may be by design of course.

I don't think I need to post a DFM to go with the code. I simply have AdoQueries set up in the usual master-detail way (with the detail query having the master's PK as a parameter), a DataSetProvider pointed at the master AdoQuery, a master CDS pointed at the provider, and a detail cDS pointed at the DataSetField of the cdsMaster. To experiment and see what's going on, there are DBGrids and DBNavigators for each of these datasets.

In brief, the way the code below works is to temporarily filter the AdoQuery master and the CDS masterdown to the current row and then force a refresh of their data and the dtail data for the current master row. Doing it this way, unlike any other I tried, results in the detail rows nested in the cdsMaster's DataSet field getting refreshed.

Btw, the other blind alleys I tried included with and without poFetchDetailsOnDemand set to true, ditto cdsMaster.FetchDetailsOnDemand. Evidently "FetchDetailsOnDemand" doesn't mean ReFetchDetailsOnDemand!

I ran into a problem or two getting my "solution" working, the stickiest one being described in this SO question: Refreshing a ClientDataSet nested in a DataSetField

I've verified that this works correctly with a Sql Server 2000(!) back-end, including picking up row data changes fired at the server from ISqlW. I've also verified, using Sql Server's Profiler, that the network traffic in a refresh only involves the single master row and its details.

Delphi 7 + Win7 64-bit, btw.

procedure TForm1.cdsMasterRowRefresh(MasterPK : Integer);
begin
  //  The following operations will cause the cursor on the cdsMaster to scroll
  //  so we need to check and set a flag to avoid re-entrancy
  if DoingRefresh then Exit;
  DoingRefresh := True;

  try
    //  Filter the cdsMaster down to the single row which is to be refreshed.
    cdsMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
    cdsMaster.Filtered := True;
    cdsMaster.Refresh;
    Inc(cdsMasterRefreshes);  //  just a counter to assist debugging

    //  release the filter
    cdsMaster.Filtered := False;

    // clearing the filter may cause the cdsMaster cursor to move, so ...
    cdsMaster.Locate(MasterPKName, MasterPK, []);
  finally
    DoingRefresh := False;
  end;
end;

procedure TForm1.qMasterRowRefresh(MasterPK : Integer);
begin
  try
    //  First, filter the AdoQuery master down to the cdsMaster current row
    qMaster.Filter := MasterPKName + ' = ' + IntToStr(MasterPK);
    qMaster.Filtered := True;

    //  At this point Ado is happy to refresh only the current master row from the server
    qMaster.Refresh;

    // NOTE:
    //  The reason for the following operations on the qDetail AdoQuery is that I noticed
    //  during testing situations where this dataset would not be up-to-date at this point
    //  in the refreshing operations, so we update it manually.  The reason I do it manually
    //  is that simply calling qDetail's Refresh provoked the Ado "Insufficient key column
    //  information for updating or refreshing" despite its query not involving a join
    //  and the underlying table having a PK

    qDetail.Parameters.ParamByName(MasterPKName).Value := MasterPK;
    qDetail.Close;
    qDetail.Open;

    //  With the master and detail rows now re-read from the server, we can update
    //  the cdsMaster
    cdsMasterRowRefresh(MasterPK);
  finally
    //  Now, we can clear the filter
    qMaster.Filtered := False;
    qMaster.Locate(MasterPKName, MasterPK, []);
    // Obviously, if qMaster were filtered in the first place, we'd need to reinstate that later on
  end;
end;

procedure TForm1.RefreshcdsMasterAndDetails;
var
  MasterPK : Integer;
begin
  if cdsMaster.ChangeCount > 0 then
    raise Exception.Create(Format('cdsMaster has %d change(s) pending.', [cdsMaster.ChangeCount]));
  MasterPK := cdsMaster.FieldByName(MasterPKName).AsInteger;

  cdsDetail.DisableControls;
  cdsMaster.DisableControls;
  qDetail.DisableControls;
  qMaster.DisableControls;

  try
    try
      qMasterRowRefresh(MasterPK);
    except
      //  Add exception handling here according to taste
      //  I haven't encountered any during debugging/testing so:
      raise;
    end;
  finally
    qMaster.EnableControls;
    qDetail.EnableControls;
    cdsMaster.EnableControls;
    cdsDetail.EnableControls;
  end;
end;

procedure TForm1.cdsMasterAfterScroll(DataSet: TDataSet);
begin
  RefreshcdsMasterAndDetails;
end;

procedure TForm1.cdsMasterAfterPost(DataSet: TDataSet);
//  NOTE:  The reason that this, in addition to cdsMasterAfterScroll, calls RefreshcdsMasterAndDetails is
//         because RefreshcdsMasterAndDetails only refreshes the master + detail AdoQueries for the current
//         cdsMaster row.  Therefore in the case where the current cdsMaster row or its detail(s)
//         have been updated, this row needs the refresh treatment before we leave it.
begin
  cdsMaster.ApplyUpdates(-1);
  RefreshcdsMasterAndDetails;
end;

procedure TForm1.btnRefreshClick(Sender: TObject);
begin
  RefreshcdsMasterAndDetails;
end;

procedure TForm1.cdsDetailAfterPost(DataSet: TDataSet);
begin
  cdsMaster.ApplyUpdates(-1);
end;

这篇关于使用poFetchDetailsOnDemand刷新嵌套数据集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆