在问题标题中,我与一位同事争论如何使用计算字段。 据我所知,计算字段是在运行时创建的,就像问题answer中的FrançoisAdding a calculated field to a Query at run time一样。在同一个问题上,还有另一个来自sabri.arslan的answer,建议将现有字段更改为计算字段(代码如下)
var
initing:boolean;
procedure TSampleForm.dsSampleAfterOpen(
DataSet: TDataSet);
var
i:integer;
dmp:tfield;
begin
if not initing then
try
initing:=true;
dataset.active:=false;
dataset.FieldDefs.Update;
for i:=0 to dataset.FieldDefs.Count-1 do
begin
dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self);
dmp.FieldName:=DataSet.FieldDefs.Items[i].DisplayName;
dmp.DataSet:=dataset;
if (dmp.fieldname='txtState') or (dmp.FieldName='txtOldState') then
begin
dmp.Calculated:=true;
dmp.DisplayWidth:=255;
dmp.size:=255;
end;
end;
dataset.active:=true;
finally
initing:=false;
end;
end;
procedure TSampleForm.dsSampleAfterClose(
DataSet: TDataSet);
var
i:integer;
dmp:TField;
begin
if not initing then
begin
for i:=DataSet.FieldCount-1 downto 0 do
begin
dmp:=pointer(DataSet.Fields.Fields[i]);
DataSet.Fields.Fields[i].DataSet:=nil;
freeandnil(dmp);
end;
DataSet.FieldDefs.Clear;
end;
end;
procedure TSampleForm.dsSampleCalcFields(
DataSet: TDataSet);
var
tmpdurum,tmpOldDurum:integer;
begin
if not initing then
begin
tmpDurum := dataset.FieldByName( 'state' ).AsInteger;
tmpOldDurum:= dataset.FieldByName( 'oldstate' ).AsInteger;
dataset.FieldByName( 'txtState' ).AsString := State2Text(tmpDurum);
dataset.FieldByName( 'txtOldState' ).AsString := State2Text(tmpOldDurum);
end;
end;
procedure TSampleForm.btnOpenClick(Sender: TObject);
begin
if dsSample.Active then
dsSample.Close;
dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1';
dsSample.Open;
end;
我认为此更改会导致指定TField的未知行为。在运行时将数据集字段更改为计算字段是安全的吗?这会产生什么样的问题?
LE:这是一个问题。其目的是演示在运行时在数据集上添加计算字段的良好实践。并且,是的,在运行时添加计算字段是糟糕的设计。
LE2:这只是“不要这样做”的一个例子。作为一个论点,我问在这样做之后讨论领域的行为是什么。该领域将如何行动?
答案 0 :(得分:3)
不,这不是一个好习惯。代码复杂的简单事实表明应该避免这种做法。有人已经提到了KISS原则,我同意这一点。
特别是,数据集必须打开两次的简单事实足以让我不喜欢这种做法。
此外,将字段的性质从数据更改为计算将改变数据集在其内部记录表示中组织字段的方式(数据集调用的内容) 记录缓冲区)。这种表示可能与一个数据集实现到另一个数据集实现非常不同。由于该问题未确定特定数据集,因此行为的变化(通常):
然而,根据其目的和特征,某个数据集的实现会产生一些其他后果。
答案 1 :(得分:1)
有没有人注意到这段代码?
procedure TSampleForm.btnOpenClick(Sender: TObject);
begin
if dsSample.Active then
dsSample.Close;
dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1';
dsSample.Open;
end;
他没有将数据库字段更改为计算字段...他正在将不存在的字段更改为计算字段。他知道字段类型......它将成为一个字符串...所以它是一个大问题......不......这是一个黑客......是的...你可以在SQL中做同样的事情Cast ...事实上我从来没有真正看到使用计算字段的理由......我通常可以在SQL中更容易地做同样的事情。
我在更多地挖掘了为什么不这样做之后添加了更多的信息...
sabri.arslan代码...从FieldList创建Fields ...也存在缺少设置密钥和处理heirchy字段的问题。
dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self);
然后我们有弗朗索瓦......
if you call the none hacked code you get this...
for I := 0 to MyQuery.FieldDefList.Count - 1 do
with MyQuery.FieldDefList[I] do
if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and
not ((faHiddenCol in Attributes) and not MyQuery.FIeldDefs.HiddenFields) then
CreateField(Self, nil, MyQuery.FieldDefList.Strings[I]);
嗯...缺少SetKeyFields会导致不可预知的行为吗?此外,如果您的ObjectView属性设置为True ...您的数据集对于层次结构字段将无法正常运行...它看起来更安全
只是将Hack调用到CreateFields而不是使用他的代码...除了你必须肯定你的数据集组件永远不会调用这个代码...
CreateField调用CreateFieldComponent,你得到你的TField的结果:= FieldClassType.Create(Owner)
取自TFieldDef的Borland帮助 “字段定义具有相应的TField对象,但并非所有TField对象都具有相应的字段定义。例如,计算字段没有字段定义对象。”
所以我问你......你是否积极通过动态创建CalculatedField来引入未知行为? 您是否肯定尚未创建字段或稍后不会创建字段? (sabri.arslan代码中有一个错误,因为他打开/打开后......他正在覆盖原来的TFields ......,我不明白为什么我们需要为已经打开的数据集重新创建TField)
那么当数据集调用CreateFields时会发生什么(BDE和ADO在InternalOpen上执行此操作 并检查以确保它们在字段中没有值...所有数据集组件都执行此操作 办法?他们不必)。 您已经创建的字段会发生什么......是否会被覆盖?我没有看到任何代码 在TDataset或TFieldDef中检查是否已经为相应的TFieldDef创建了TField而不是 检查DefaultFields(如果Fields有值)。
答案 2 :(得分:0)
TField要么映射到数据库列,要么不映射到数据库列,并且是通过计算得出的。这应该在设计时设置。任何试图在运行时改变这种情况的尝试都会让IMO设计不好,而且你正在为自己设置很多潜在的麻烦。