正如 Ken 所说,没有示例代码很难为您提供帮助。所以我无法直接回答你的问题,但是通过一些提示你可能能够自己弄清楚。所以我要试着教你钓鱼。;)
我确实对 Delphi 3 和 6 中的 TClientDataSet 有相当多的经验。我怀疑事情已经发生了很大的变化。另外,我将更多地关注技术,以帮助您朝着正确的方向发展。
首先,确保使用 Debug DCU 进行编译,以便在 VCL 代码中获得断点。不要害怕阅读并单步执行该代码——这是一种很好的学习方式。此外,您还可以看到 Borland/CodeGear/Embarcadero 在其丑陋的荣耀中所犯的错误。
我假设您已经启用了 Stop on Exceptions(因此您知道自己特别遇到了 AV 错误)。
做你的测试。当您获得您的 AV 时,您可能会出现在 DbClient.pas(或者可能是较低级别的单元之一)中。
通过调用堆栈寻找一个位置,您在try..except块的try部分中,该块要么吞下异常,要么短路到另一个处理程序,然后吞下它。
注意: DbClient.pas 中充斥着以下错误代码的大量实例:
except //swallowing or squashing exceptions is
end; //very dangerous and should be avoided.
except
//short-circuiting to Application.HandleException (usually informs the user)
//but is not much better. The point is the rest of the program up the
//call-stack remains blissfully unaware that something went wrong.
Application.HandleException(Self);
Action := raAbort;
end;
旁注:仅仅因为 Borland 做到了,并不意味着它是好的做法。你和其他许多人已经并将为此而焦头烂额。
一旦你发现了这一点,你就成功了一半。您现在知道为什么您没有被告知 AV。
现在,在构成该调用堆栈一部分的代码中的某处将成为您希望如何处理错误的线索。您还可以在 VCL 代码中稍早放置一个断点,以便您可以单步执行以查看导致异常的事件序列。由于您特别提到了 AV(访问冲突),因此请查找引用nil的对象。
D5 DbClient.pas 的以下代码片段中可能导致问题的一个示例
FOnReconcileError(DataSet, E, UpdateKind, Action);
finally
E.Free;
end;
except
Application.HandleException(Self);
Action := raAbort;
end;
从上面可以看出,某些情况需要您实现 OnReconcileError 事件处理程序。没有它,dll就不会被回调到上面的代码片段中,许多错误都被悄悄地踢到地毯下,让你摸不着头脑。
我对 TClientDataSet 的经验是,您必须做一些额外的事情才能“正确”使用它。这在文档中并不是特别明显。虽然一旦你弄清楚了你应该做的所有点点滴滴,如何做的例子就更好了。
不幸的是,我对所有这些事情的记忆有点模糊。所以这是一个非常粗略的指南,可能缺少很多。
- 考虑使用 OnReconcileError 事件处理程序。
- 考虑实现一个 UpdateProvider 对象。
- 您提到将其用作内存数据集,因此您可能正确使用了 CreateDataSet。但是,如果我没记错的话,您有 2 个用于指定字段的选项,并且一些更精细的属性有点挑剔。
- 无关提示:注意字符串字段的长度,TClientDataSet 过去(并且可能仍然)以类似于短字符串的方式占用内存。
编辑
根据问题中的其他信息并使用上述技术,您将收到从以下方法引发的 EDatabaseError。
procedure TIntegerField.SetAsString(const Value: string);
var
E: Integer;
L: Longint;
begin
if Value = '' then Clear else
begin
Val(Value, L, E);
if E <> 0 then DatabaseErrorFmt(SInvalidIntegerValue, [Value, DisplayName]);
SetAsInteger(L);
end;
end;
在调用堆栈的上层,您将获得以下方法,该方法提示如何解决问题。
procedure TField.SetEditText(const Value: string);
begin
if Assigned(FOnSetText) then FOnSetText(Self, Value) else SetText(Value);
end;
此外,如果您一直跟踪调用堆栈直到KeyPress,您会注意到根本不涉及任何数据集或网格代码。
您尝试在 OnPost 错误中处理错误的尝试无效的原因是您没有接近尝试发布您的记录。您收到字段验证错误,处理该错误的地方是在现场。
回到提示如何解决错误 - 您必须为该字段实现 OnSetText 事件。
但是,我建议不要这样做。开箱即用的行为是完全可以接受的!用户收到一条错误消息,说明他们做错了什么,他们有机会修复它并重试。如果他们改变主意,他们可以点击 Escape 并取消编辑。