2009-03-09 5 views
1

응용 프로그램을 BDE에서 ADO로 변환 중입니다.어떻게해야합니까?

BDE에서 쿼리가 열려 있고 "Sql.Clear"를 호출하면 자동으로 데이터 집합을 닫습니다.

그러나 TADOQuery에서는 "닫힌 데이터 집합에서 작업을 수행 할 수 없습니다."라는 예외가 발생합니다.

많은 레거시 코드가 이전 BDE 동작에 의존하므로 아래 예제와 같이 런타임 오류가 많습니다.

내 TADOCustomQuery 클래스의 Sql.Clear 메서드를 재정의하여 ".Close"명령을 포함하려고합니다. 어떻게해야합니까?

".Clear"메서드는 TWideStrings 형식의 SQL 속성에 있습니다. 내 진짜 질문은 : 어떻게 내가 TideOQuery의 자손에 TWideStrings.Clear 메서드를 재정의 할 수 있습니까?

I는 SQL 속성에 대한이와 이미 사용자 정의 TADOQuery 구성 요소를 가지고 : 여기

property SQL: TWideStrings read GetSQL write SetSQL; 

내가 데 문제를 설명하는 몇 가지 코드 : 물론

procedure TForm1.btnBDEDemoClick(Sender: TObject); 
var 
    qryBDE: TQuery; 
begin 
    //Both queries complete with no problem 
    qryBDE := TQuery.Create(nil); 
    try 
    with qryBDE do begin 
     DatabaseName := 'Test'; //BDE Alias 
     Sql.Clear; 
     Sql.Add('SELECT SYSDATE AS CURDAT FROM DUAL'); 
     Open; 
     ShowMessage('the current date is: ' + FieldByName('CURDAT').AsString); 

     Sql.Clear; //<<<<<NO ERRORS, WORKS FINE 
     Sql.Add('SELECT SYSDATE-1 AS YESDAT FROM DUAL'); 
     Open; 
     ShowMessage('And yesterday was: ' + FieldByName('YESDAT').AsString); 
    end; //with qryBDE 
    finally 
    FreeAndNil(qryBDE); 
    end; //try-finally 
end; 

procedure TForm1.btnADODemoClick(Sender: TObject); 
const 
    c_ConnString = 'Provider=OraOLEDB.Oracle.1;Password=*;'+ 
    'Persist Security Info=True;User ID=*;Data Source=*'; 
var 
    adoConn: TADOConnection; 
    qryADO: TADOQuery; 
begin 
    //First query completes, but the second one FAILS 
    adoConn := TADOConnection.Create(nil); 
    qryADO := TADOQuery.Create(nil); 
    try 
    adoConn.ConnectionString := c_ConnString; 
    adoConn.Connected := True; 
    with qryADO do begin 
     Connection := adoConn; 
     Sql.Clear; 
     Sql.Add('SELECT SYSDATE AS CURDAT FROM DUAL'); 
     Open; 
     ShowMessage('the current date is: ' + FieldByName('CURDAT').AsString); 

     Sql.Clear;//<<<<<<<<===========ERROR AT THIS LINE 
     Sql.Add('SELECT SYSDATE-1 AS YESDAT FROM DUAL'); 
     Open; 
     ShowMessage('And yesterday was: ' + FieldByName('YESDAT').AsString); 
    end; //with qryADO 
    finally 
    FreeAndNil(qryADO); 
    FreeAndNil(adoConn); 
    end; //try-finally 
end; 

답변

5

문제는 당신이 맑은을 실행할 때 데이터 집합이 열려 있다는 것입니다. ADODataset의 경우 기본 ADO 데이터 집합을 업데이트하기 위해 속성이 유선되며 데이터 집합을 열어 변경하면 예외가 발생합니다.

데이터 세트를 정리하기 전에 닫기 만하면됩니다. 그러면 데이터 세트가 모두 올바르게 작동합니다.

with qryADO do 
    begin  
    Connection := adoConn;  
    Sql.Clear;  
    Sql.Add('SELECT SYSDATE AS CURDAT FROM DUAL');  
    Open;  
    ShowMessage('the current date is: ' + FieldByName('CURDAT').AsString); 
    qryADO.close; // <=== line added to close the database first. 
    Sql.Clear;  
    Sql.Add('SELECT SYSDATE-1 AS YESDAT FROM DUAL');  
    Open;  
    ShowMessage('And yesterday was: ' + FieldByName('YESDAT').AsString);  
    end; //with qryADO 

EDIT 다음과 같은 SQLCLEAR라는 새로운 형태의 방법을 만들 수있는 대안으로 :

function TYourFormOrDataModule.SqlClear; 
begin 
    qryAdo.Close; 
    qryAdo.Sql.Clear; 
    qryBde.Sql.Clear; 
end; 

를 한 후 검색을 수행하고 "SQL.Clear"에 대한 대체 "SqlClear".그러나 나는 더 일관성이 있고 장기간 유지하기가 훨씬 쉬울 것이므로 원래의 대답에서 닫기를 수행하는 방법을 선호합니다. gExperts와 같은 도구를 사용하여 Sql.Clear의 모든 인스턴스를 찾고 몇 백 개의 인스턴스가 있더라도 qryAdo.Close를 삽입하십시오.

+0

네, 저도 압니다 ...하지만이 줄을 코드의 기존 위치 100 개에 추가하지 않으려합니다. – JosephStyons

2

, Clear 메서드를 덮어 쓰는 TAdoQuery의 하위 클래스를 만들 수 있습니다. 그러나 나는 그것이 나쁜 습관이라고 생각한다.

모든 쿼리를 변경하는 것이 좋습니다. 어쩌면 어떤 일 이겠지만 결국에는 지불해야합니다. 당신이 도움에 TAdoQuery의 예를 보면

:

ADOQuery := TADOQuery.Create(Self); 
ADOQuery.Connection := ADOConn; 
ADOQuery.SQL.Add(SQLStr); 

{ Update the parameter that was parsed from the SQL query: AnId } 
Param := ADOQuery.Parameters.ParamByName('AnId'); 
Param.DataType := ftInteger; 
Param.Value := 1; 

{ Set the query to Prepared - will improve performance } 
ADOQuery.Prepared := true; 

try 
    ADOQuery.Active := True; 
except 
    on e: EADOError do 
    begin 
    MessageDlg('Error while doing query', mtError, 
       [mbOK], 0); 

    Exit; 
    end; 
end; 

당신은 좀 더 다르게 BDE 버전보다 참조하십시오.

따라서 ExecuteSQL 함수 (BDE의 첫 번째 함수)를 만든 다음 ADO에서 사용할 수 있도록 다시 작성하는 것이 가장 좋습니다.

+0

글쎄, 난 아마 여기에 밀도가 높습니다. 그러나 Clear 메서드는 TWideStrings 형식의 SQL 속성에 있습니다. 어떻게 TADOQuery의 자손에서 TWideStrings.Clear 메서드를 재정의 할 수 있습니까? 아마도 그게 내 진짜 질문이었을거야 .... – JosephStyons

+0

죄송합니다, 질문을 잘못 읽었습니다. 아니, 클리어 할 수 없습니다. 그런데 다시 오류 메시지를 표시해서는 안되며 다른 것이 잘못되었습니다. –

2

어떤 델파이 버전을 사용하고 있습니까? 나는 이것을 복제하려했지만 델파이 2009에서 정상적으로 작동합니다 - 오류가보고되지 않고 예상 한 데이터를 반환합니다.

감사 돈

+0

나는 동일한 문제를 나타내는 Delphi 2007과 Delphi 5를 사용하고 있습니다. D2009에 같은 문제가 없다는 사실이 흥미 롭습니다. – JosephStyons

6

이것은 BDE 자체의 기능이 아닙니다. 델파이와 함께 제공되는 당신이 설명되어있는 문제가 TQuery.SQL의 SetQuery 방법에 구현되는 것을 볼 수있는 소스를 보면 :

procedure TQuery.SetQuery(Value: TStrings); 
begin 
    if SQL.Text <> Value.Text then 
    begin 
    Disconnect; 
    SQL.BeginUpdate; 
    try 
     SQL.Assign(Value); 
    finally 
     SQL.EndUpdate; 
    end; 
    end; 
end; 

이 TADOQuery의 SetQuery 간단하지만 :

procedure TADOQuery.SetSQL(const Value: TWideStrings); 
begin 
    FSQL.Assign(Value); 
end; 

왜 볼랜드/Codegear는 그것을 구현하지 않기로 결정했습니다. 사용자 지정 ADOQuery에서 TQuery의 SetQuery를 구현하면 원하는 동작을 제공해야합니다.

+0

당신 말이 맞아요, 그 코드도 보았습니다. 그러나, 내 Custom ADO SetQuery를 수정하는 것만으로도 TQuery가 도움이되지 않습니다 ("Disconnect"대신 "Close"를 제외하고 정확히 같은 코드를 사용했습니다). – JosephStyons

1

대신 다음과 같이 할 것입니다. Don이 말한 것처럼 동작이 실제로 고정되어 있다면 D2009를 가진 사람과 확인하십시오. 그에게 (또는 D2009를 가지고있는 다른 사람에게) 테스트 케이스를 보냅니다. D2009의 동작이 고정되어 있으면 문제가 간단합니다.

프로젝트 디렉토리에 ADODB.pas를 복사하십시오. 원하는 동작 (예 : SetSQL 메소드 변경)을 갖도록 파일을 수정하십시오. 다시 컴파일하십시오. 그것은 작동해야합니다. 이렇게하면 이전의 사용자 지정된 ADODB.pas를 프로젝트에서 제거 할 수있는 D2009 로의 최종 업그레이드 시간을 갖게됩니다.

HTH.

2

업데이트 나는 자동으로 우리의 모든 소스 코드를 업데이트 할 수있는 작은 유틸리티를 작성하여 skamradt의 솔루션을 구현했습니다. 그것은이처럼 일 :

1 - 재귀 적으로 우리의 프로젝트 폴더에있는 모든 .PAS 파일의 목록을

2 - 해당 파일의 모든 절차를 적용 :

procedure ApplyChange(filename: string); 
const 
    c_FindThis = 'SQL.CLEAR'; 
var 
    inputFile, outputFile: TStringList; 
    i, postn, offset: integer; 
    newline: string; 
begin 
    inputFile := TStringList.Create; 
    outputFile := TStringList.Create; 
    offset := 0; 
    try 
    inputFile.LoadFromFile(filename); 
    outputFile.Assign(inputFile); //default: they are the same 

    for i := 0 to inputFile.Count - 1 do begin 
     { 
     whenever you find a "Sql.Clear", place a new line before it, 
     which consists of everything up to the "Sql.Clear" (which may 
     just be whitespace), plus the "Close" command. 
     //} 
     postn := Pos(c_FindThis,Uppercase(inputFile[i])); 
     if (0 < postn) then begin 
     newline := Copy(inputFile[i],1,postn-1) + 'Close;'; 
     outputFile.Insert(i+offset,newline); 
     Inc(offset); 
     end; 
    end; 

    //overwrite the existing file with the revised one 
    outputFile.SaveToFile(filename); 
    finally 
    FreeAndNil(inputFile); 
    FreeAndNil(outputFile); 
    end; //try-finally 
end; 
관련 문제