AsStreams FireDAC got nil value from MemoryStream - arrays

i was trying to insert a lot of info into my database Access and i am stuck on this exemple:
(TableContact.FieldByName('foto') as TBlobField).SaveToStream(AQuery.Params.ParamByName('pFoto').AsStreams[I];
Always my AsStreams[I] is nil
Trying to get it solved i tryed to do an loop adding my Streams into an Array and then insert them into my AsStreams[I], but it was the same error (nil).
Exemple:
for I := 0 to Streams.Count - 1 do
(AQuery.Params.ParamByName('pFoto').AsStreams[I] as TBlobField).LoadFromStream(Streams[I]);
Anyone could help me?
Full code downhere
procedure TPrincipalController.SetDadosMemTableToBanco;
var
I: Integer;
AQuery: TFDQuery;
function GetSQLMemTableToBanco: String;
begin
Result :=
'INSERT INTO PESSOA (nome, telefone, email, foto, observacoes) ' + sLineBreak +
' VALUES (:pNome, :pTelefone, :pEmail, :pFoto, :pObservacoes); ';
end;
function GetSQLMemTableUpdateToBanco: String;
begin
Result :=
'UPDATE PESSOA ' + sLineBreak +
' SET nome = :pNome, ' + sLineBreak +
' telefone = :pTelefone, ' + sLineBreak +
' email = :pEmail, ' + sLineBreak +
' foto = :pFoto, ' + sLineBreak +
' observacoes = :pObservacoes ' + sLineBreak +
' WHERE id = :pID; ';
end;
procedure SetArraySizeOnSQLChange;
begin
AQuery.Params.ArraySize := FTableContatos.RecordCount;
end;
begin
try
AQuery := TFDQuery.Create(nil);
try
AQuery.Connection := TConexao.GetConexao;
TableContatos.First;
for I := 0 to TableContatos.RecordCount - 1 do
begin
AQuery.SQL.Text := GetSQLMemTableToBanco;
SetArraySizeOnSQLChange;
if ExisteRegistro(TableContatos.FieldByName('id').AsInteger) then
begin
AQuery.SQL.Text := GetSQLMemTableUpdateToBanco;
SetArraySizeOnSQLChange;
AQuery.ParamByName('pID').AsIntegers[I] := TableContatos.FieldByName('id').AsInteger;
end;
AQuery.ParamByName('pNome').AsStrings[I] := TableContatos.FieldByName('nome').AsString;
AQuery.ParamByName('pTelefone').AsStrings[I] := TableContatos.FieldByName('telefone').AsString;
AQuery.ParamByName('pEmail').AsStrings[I] := TableContatos.FieldByName('email').AsString;
AQuery.ParamByName('pObservacoes').AsStrings[I] := TableContatos.FieldByName('observacoes').AsString;
(TableContatos.FieldByName('foto') as TBlobField).SaveToStream(AQuery.Params.ParamByName('pFoto').AsStreams[I]);
TableContatos.Next;
end;
AQuery.Execute(TableContatos.RecordCount, 0);
finally
AQuery.Free;
end;
except
on E: Exception do
ShowMessageFmt('Erro ao incluir dados no banco. MSG -> %s', [E.Message]);
end;
end;

Solved using only Update on SQL and the attribution to the Field i needed to use like this:
AStream := TableContatos.CreateBlobStream(TableContatos.FieldByName('foto'), TBlobStreamMode.bmRead);
try
AQuery.ParamByName('pFoto').LoadFromStream(AStream, ftBlob, I);
finally
AStream.Free;
end;

Related

Inno setup Windows Registry not catchable

:-)
I already did already a little bit programming in Inno setup, but now I think I am not able to see the forest for the trees. :-)
I want to copy some customization files in the section "Copyfiles" into the installation path of the files which is located in the registry. For this I call "GetInstalledPath" and want to get the registry information. BUt the message box in the Copyfiles section with input message "Test" is afterwards empty and also the files are not copied into the right place. I could swear it worked in an earlier version, probably I changed sth or is there sth to take care about it?
Thank you very much for having a look!
Best wishes and stay healthy!
JH
function GetInstallPath(Param: String): String;
begin
if Param = 'File1' then
Result := 'C:\Program Files\FolderFile1'
else if Param = 'InstPath32' then
begin
if RegGetSubkeyNames(HKLM32, 'Software\Programname', RegKeys) then
begin
if RegQueryStringValue(HKLM32,'Software\Programname' + '\'+ 'Versionnumber'+ 'Paths','InstallDirectory', Program_PATH2) then
Result := Program_PATH2
else
end
end
else if Param = 'InstPath64' then
begin
if RegGetSubkeyNames(HKLM64, 'Software\Programname', RegKeys) then
begin
if RegQueryStringValue(HKLM64,'Software\Programname' + '\'+ 'Versionnumber+ 'Paths','InstallDirectory', Program_PATH3) then
Result := Program_PATH3
else
MsgBox('InstPath64 ' + Program_PATH3, mbInformation, MB_OK);
end
end
end;
procedure WaitUntilInstalled();
var
I: integer;
count: integer;
begin
count := 0;
I := 0
while (NOT FileExists(ExpandConstant('{tmp}\filename.txt'))) AND (count<90) DO
begin
count := count + 1;
Sleep(1000);
end;
end;
procedure CopyFiles(Param: String) ;
var
SrcDir: String;
DestDir: String;
ResultCode: Integer;
Command: String;
begin
if Param = 'Example32' then
begin
SrcDir := '{tmp}\Customization';
DestDir := GetInstallPath('InstPath32');
Exec('md',ExpandConstant(DestDir),'',SW_SHOW, ewWaitUntilTerminated, ResultCode);
Command := '"' + ExpandConstant(SrcDir) + '" "' + ExpandConstant(DestDir) + '"' + ' /y /E /I';
Exec('xcopy', Command, '',SW_SHOW, ewWaitUntilTerminated, ResultCode);
end
else if Param = 'Example64' then
begin
SrcDir := '{tmp}\Customization';
DestDir := GetInstallPath('InstPath64');
MsgBox('Test' + DestDir, mbInformation, MB_OK)
Exec('md',ExpandConstant(DestDir),'',SW_SHOW, ewWaitUntilTerminated, ResultCode);
Command := '"' + ExpandConstant(SrcDir) + '" "' + ExpandConstant(DestDir) + '"' + ' /y /E /I';
Exec('xcopy', Command, '',SW_SHOW, ewWaitUntilTerminated, ResultCode);
end
end;

How to assign values to an array of objects in Delphi

I have an array of TBoek and and a loop that is supposed to assign values to each of the elements of the array. What happens instead is that the array ends up with the exact same values in each index. Perhaps my order of processing is incorrect or I'm incorrectly assigning values to the array, but either way I cannot for the life of me figure it out.
procedure TBoek.MaakArray;
var
i: integer;
sTitel, sOuteur, sISBN, sUitgewer, sPrys, sI: string;
boek: TBoek;
begin
boek := TBoek.Create;
for i := 0 to 9 do
begin
{$REGION 'Parse JSON om eienskappe van boek te kry'}
sI := IntToStr(i);
sTitel := JSONFile.GetValue<string>('items[' + sI + '].volumeInfo.title');
try
sOuteur := JSONFile.GetValue<string>
('items[' + sI + '].volumeInfo.authors[0]');
except
sOuteur := '<none>'
end;
try
sISBN := JSONFile.GetValue<string>
('items[' + sI + '].volumeInfo.industryIdentifiers[1].identifier');
except
sISBN := '<none>'
end;
try
sUitgewer := JSONFile.GetValue<string>
('items[' + sI + '].volumeInfo.publisher');
except
sUitgewer := '<none>'
end;
try
sPrys := JSONFile.GetValue<string>
('items[' + sI + '].saleInfo.listPrice.amount');
except
sPrys := '0';
end;
{$ENDREGION}
arrBoeke[i] := boek;
with arrBoeke[i] do
begin
SetTitel(sTitel);
SetOuteur(sOuteur);
SetISBN(sISBN);
SetUitgewer(sUitgewer);
SetPrys(sPrys);
end;
end;///end of for loop
end;
The Set functions all follow this format:
procedure TBoek.SetTitel(BoekTitel: string);
begin
fTitel := BoekTitel;
end;
This is the GetString function:
function TBoek.GetString: string;
begin
Result := GetTitel + #13#10 + GetOuteur + #13#10 + GetISBN + #13#10 +
GetUitgewer + #13#10 + GetPrys + #13#10 + #13#10;
end;
And the GetTitel,GetOuteur etc. functions all follow the same format:
function TBoek.GetTitel: string;
begin
Result := fTitel;
end;
What I want is to call:
for I := 0 to 9 do
begin
ShowMessage(arrBoeke[i].GetString);
end;
and access the values in the array one at a time, instead each value is the same.

Eof not triggering

I have a function where I get data from a DB, my test data set returns 6500 rows (I extracted the formatted SQL statement from the SQLText Variable and ran it as a test), but then when I run the following code Eof never triggers and I have seen over 100k rows imported.
ADOQuery := TADOQuery.Create(nil);
ADOQuery.ConnectionString := CONNECT_STRING;
// Build SQL Query
SQLText := Format( 'Select Temp.Serial, Temp.QCSample , Temp.Scrap , Temp.StationID , Temp.Defect , Temp.AddData , Temp2.Serial as Parent_Serial ' +
'from TAB_ELEMENT as Temp ' +
'left join TAB_ELEMENT as Temp2 on Temp.Parent_Id = Temp2.Element_Id ' +
'where Temp.Batch_ID = %d and Temp.StationID = 0 ',[iSearchID]);
ADOQuery.SQL.Clear; // Clear query of garbage values
ADOQuery.SQL.Text := SQLText; // Add query text to query module
ADOQuery.Open;
// Handle Results
iIndexPos := 0;
tDataImport.BeginUpdate;
while not ADOQuery.Eof do
begin
tDataImport.Items[iIndexPos].Serial := ADOQuery.FieldByName('Serial').AsString;
tDataImport.Items[iIndexPos].QCStatus := ADOQuery.FieldByName('QCSample').AsBoolean;
tDataImport.Items[iIndexPos].Scrap := ADOQuery.FieldByName('Scrap').AsInteger;
tDataImport.Items[iIndexPos].StationID := ADOQuery.FieldByName('StationID').AsInteger;
tDataImport.Items[iIndexPos].Defect := ADOQuery.FieldByName('Defect').AsBoolean;
tDataImport.Items[iIndexPos].AddData := ADOQuery.FieldByName('AddData').AsString;
tDataImport.Items[iIndexPos].ParentSerial := ADOQuery.FieldByName('Parent_Serial').AsString;
inc(iIndexPos);
end;
So in summery running this query with these parameters I expect 6500 rows, when I run this it never ends even after 100k+ rows are processed.
Open() places the cursor on the first record and sets Eof accordingly. You are not calling Next() to advance the cursor to the next record and update Eof, so you are processing the same record over and over:
ADOQuery.Open;
while not ADOQuery.Eof do
begin
//...
ADOQuery.Next; // <-- add this!
end;
On a side note, you should be using a parameterized query instead of a formatted SQL query string. It is safer, faster, and more efficient on the DB:
ADOQuery := TADOQuery.Create(nil);
ADOQuery.ConnectionString := CONNECT_STRING;
ADOQuery.SQL.Text := 'Select Temp.Serial, Temp.QCSample , Temp.Scrap , Temp.StationID , Temp.Defect , Temp.AddData , Temp2.Serial as Parent_Serial ' +
'from TAB_ELEMENT as Temp ' +
'left join TAB_ELEMENT as Temp2 on Temp.Parent_Id = Temp2.Element_Id ' +
'where Temp.Batch_ID = :iSearchID and Temp.StationID = 0 ';
with ADOQuery.Parameters.ParamByName('iSearchID') do
begin
DataType := ftInteger;
Value := iSearchID;
end;
ADOQuery.Open;
try
iIndexPos := 0;
tDataImport.BeginUpdate;
try
while not ADOQuery.Eof do
begin
tDataImport.Items[iIndexPos].Serial := ADOQuery.FieldByName('Serial').AsString;
tDataImport.Items[iIndexPos].QCStatus := ADOQuery.FieldByName('QCSample').AsBoolean;
tDataImport.Items[iIndexPos].Scrap := ADOQuery.FieldByName('Scrap').AsInteger;
tDataImport.Items[iIndexPos].StationID := ADOQuery.FieldByName('StationID').AsInteger;
tDataImport.Items[iIndexPos].Defect := ADOQuery.FieldByName('Defect').AsBoolean;
tDataImport.Items[iIndexPos].AddData := ADOQuery.FieldByName('AddData').AsString;
tDataImport.Items[iIndexPos].ParentSerial := ADOQuery.FieldByName('Parent_Serial').AsString;
inc(iIndexPos);
ADOQuery.Next;
finally
tDataImport.EndUpdate;
end;
end;
finally
ADOQuery.Close;
end;

Get Params of a QueryString and their datatypes

Let's say you have an delphi interface that has a TEdit where an user types and SQL Query
SELECT * FROM X WHERE X.A = :A and X.B = :B and X.C= :C
Is there a way to get the types of the :A,:B,:C Params? I managed to get the name of the Params. using this code
procedure TfrmA.Button1Click(Sender: TObject);
var
queryInput, queryOutput , aux : string;
pozitie : Integer;
param : string;
Ch,Chx : Char;
begin
queryInput := TEdit.Text;
queryOutput := StringReplace(queryInput,':','#',[rfReplaceAll, rfIgnoreCase]);
aux := Copy(queryOutput,AnsiPos('#',queryOutput),200);
while Length(aux ) > 0 do begin
if AnsiPos('#',aux ) = 0 then break;
for Ch in aux do
begin
if Ch = ' ' then begin
param := param + ' ';
break
end else param := param + Ch;
end;
ShowMessage(param);
test := Copy(test,2,200);
test := Copy(test,AnsiPos('#',test),200);
end;
end;
Param string will containt now : #A #B #C
Is there anyway I can find out the datatype of the params?
If you are able to use a Adodataset you might use this to get the needed informations.
uses TypInfo;
procedure TForm5.Button1Click(Sender: TObject);
var
i:Integer;
begin
Ads.CommandText := 'Update Adressen set geboren=:birthdate where name=:n and ID=:i';
Memo1.Lines.Clear;
for I := 0 to Ads.Parameters.Count - 1 do
begin
Memo1.Lines.Add(Ads.Parameters[i].Name + ' : ' + GetEnumName(TypeInfo(TDataType), Integer(Ads.Parameters[i].DataType)) + ' : '+ IntToStr(Ads.Parameters[i].Size) )
end;
end;
The Output of the example will be:
birthdate : ftDateTime : 16
n : ftWideString : 40
i : ftInteger : 4

Initiating a SOAP Array on Delphi

I am trying to initialize or create an array of a soap call:
Array_Of_ProductIdentifierClinicalType = array of ProductIdentifierClinicalType;
This is the way that I am trying to initialize it:
Product[0].IdentifierType := Array_Of_ProductIdentifierClinicalType.Create();
When I run the application I get this error: Access Violation at address...
The question would be: How to initialize this soap call??
Thank you for your time!!!!
You can do a WSDL import on: http://axelcarreras.dyndns.biz:3434/WSAlchemy.wsdl
procedure TFrmMain.Button16Click(Sender: TObject);
Var
ExcludeExpiradas: String;
Serv: AlchemyServiceSoap;
req: AlchemyClinical;
element: AlchemyClinicalRequest;
Prescribed: PrescribedType;
//Prescribing: Prescribing
Prescribing: PrescribedType;
alc: AlchemyIdentifierType;
D: TXSDate;
Counter: Integer;
ProductStr: AlchemyIdentifierClinicalType;
begin
With DM do
begin
ExcludeExpiradas := ' and (' + chr(39) + DateToStr(Date) + chr(39) + ' < (FECHARECETA + 180)) ';
CDSRx_Procesadas.Close;
CDSRx_Procesadas.CommandText := 'SELECT * ' +
' FROM RX_PROCESADAS WHERE ' +
' (NUMERORECETA IS NOT NULL AND CANTIDAD_DISPONIBLE > 0)' +
ExcludeExpiradas +
' and NumeroCliente = ' + CDSPacientesNumeroCliente.asString +
' Order by NumeroReceta';
//ShowMessage(CDSRx_Procesadas.CommandText);
CDSRx_Procesadas.Open;
ProductStr := AlchemyIdentifierClinicalType.Create;
With ProductStr do
begin
Identifier := 1;
end;
element := AlchemyClinicalRequest.Create;
//Prescribed := PrescribedType.Create;
With element do
begin
With Prescribed do
begin
Counter := 0;
while not CDSRx_Procesadas.eof do
begin
Product := Array_Of_ProductIdentifierClinicalType.Create();
With Product[0] do
begin
IdentifierType := ProductIdentifierTypeEnum.NDC9;
Identifier := Copy(DM.CDSInventarioNDC.Value, 1, 9);
end;
Counter := Counter + 1;
CDSRx_Procesadas.Next;
end;
end;
With Prescribing do
begin
Counter := 0;
Product[0].IdentifierType := ProductIdentifierTypeEnum.AlchemyProductID;
Product[0].Identifier := Copy(DM.CDSInventarioNDC.Value, 1, 9);
Counter := Counter + 1;
end;
With PatientDemographics do
begin
while not CDSAlergies.Eof do
begin
Allergy.AllergySubstanceClass[0].Identifier := CDSAlergiesNOALERGIA.Value;
CDSAlergies.Next;
end;
if CDSPacientesSEXO.Value = 1 then
Gender := GenderTypeEnum.Male
else
Gender := GenderTypeEnum.Female;
D := TXSDate.Create;
D.AsDate := CDSPacientesFECHANACIMIENTO.AsDateTime;
DateOfBirth := D;
end;
With RequestedOperations do
begin
DrugToDrug := True;
//DuplicateTherapy
Allergy := True;
With WarningLabels do
begin
Request := True;
LanguageCode := 'en-US';
MaxLines := 5;
CharsPerLine := 24;
end;
With DoseScreening do
begin
Request := True;
end;
AdverseReactions.Request := True;
end;
IgnorePrescribed := False;
IncludeConsumerNotes := True;
IncludeProfessionalNotes := True;
end;
end;
end;*
Assuming that this line of code from the question is accurate:
Array_Of_ProductIdentifierClinicalType = array of ProductIdentifierClinicalType;
then the problem lies here:
Product := Array_Of_ProductIdentifierClinicalType.Create();
This is a dynamic array constructor. It creates a dynamic array of length equal to the number of parameters to the constructor. And then assigns each element of the array, in turn, to the parameters passed.
Consider an example using TBytes = array of Byte.
Bytes := TBytes.Create(1, 2, 3);
This initialises Bytes to be an array of length 3 and having values 1, 2 and 3.
Now, let's look at your code again. This initialises Product to be an array of length 0. So when you access Product[0] that results in a runtime error because the array index is out of bounds.
To solve the problem you will need to make sure that the array is initialised to have sufficient elements. One option is certainly to use a dynamic array constructor. Another is to use SetLength. I suspect that your understanding of Delphi's dynamic arrays is poor. I suggest that you consult the documentation.

Resources