komponent TClientDataset jako parametr w Datasnap

0

mam problem dotyczący komunikacji pomiędzy klientem a serwerem przy uzyciu komponentów DATASNAP i Delphi 2010 Ent.
Po stronie klienta tworzę dataset (klasy Tclientdataset), którego zawartość, jako parametr metody wyeksportowanej z serwera (metoda "pobierz_sprzedaz"), zostaje przesłana do serwera. I tu wszystko jest ok , serwer prawidłowo odbiera i przetwarza otrzymane dane, oraz zwraca wynik .

Problem pojawia się, gdy po stronie klienta usiłuję zwolnić utworzony dataset metodą ".free" , bo wtedy pojawia się wyjątek "invalid pointer operation".
Nie wiem gdzie zrobiłem błąd, pozornie wszystko proste, a nie działa prawidłowo ...
Będę wdzięczny za pomoc w rozwiązaniu problemu ....

poniżej w skrócie kod programu generujący wyjątek

w skrócie kod aplikacji

 
    ClientDataSet1 := TClientDataSet.Create(self);
 
    /// tutaj nastepuje zdefiniowanie pól w Clientdataset1 i wypełnienie ich danymi   
    //// ....
    ///...
 
    proxy := TServerMethods1Client.Create
       (self.DATASNAPCONNECTION.DBXConnection);
    wynik:= proxy.pobierz_sprzedaz(ClientDataSet1);
 
    proxy.Free;
 
 /// poniższa linia generuje opisany powyżej wyjątek 
    ClientDataSet1.Free;

tak wygląda metoda "pobierz_sprzedaz" po stronie klienta:

function TServerMethods1Client.pobierz_sprzedaz(sprzedaz: TDataSet): Double;
begin
  if Fpobierz_sprzedazCommand = nil then
  begin
    Fpobierz_sprzedazCommand := FDBXConnection.CreateCommand;
    Fpobierz_sprzedazCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    Fpobierz_sprzedazCommand.Text := 'TServerMethods1.pobierz_sprzedaz';
    Fpobierz_sprzedazCommand.Prepare;
  end;
  Fpobierz_sprzedazCommand.Parameters[0].Value.SetDBXReader(TDBXDataSetReader.Create(sprzedaz, FInstanceOwner), True);
  Fpobierz_sprzedazCommand.ExecuteUpdate;
  Result := Fpobierz_sprzedazCommand.Parameters[1].Value.GetDouble;
end;
 

a tak po stronie serwera :

 
function TServerMethods1.pobierz_sprzedaz(sprzedaz: tdataset): double;
begin
  Result := 0;
  while not sprzedaz.eof do
  begin
    Result := Result + sprzedaz.FieldByName('ilosc')
      .AsFloat * sprzedaz.FieldByName('cena').AsFloat;
    sprzedaz.next;
  end;
  /// tutaj zapis do bazy i jeszcze parę innych operacji 
  /// .....
  /// .....
 
end;
0

Jedyne co mogę polecić to podczas tworzenia obiektu instrukcje wykorzystujące go umieszczaj w bloku try .. finally .. end;

Co do linii powodującej błąd - sprawdź funkcją Assigned czy obiekt jest utworzony w pamięci:

if Assigned(ClientDataSet1) then
  ClientDataSet1.Free();

Wstaw operacje wykonywane na dynamicznie tworzonych obiektach w wyżej wymieniony blok i formatuj na bieżąco kod - to pomoże Ci uniknąć większości błedów;

0

jest przypisany, moge odwołać sie do wszystkich jego własności np. "recno', "recordcount", itp. .... , odczytac zawartość pól ....
a "Try ... Finally End" lub "Try except end" nie rozwiązuja tylko chwilowo omijają problem , bo nie zwalniaja pamięci przydzielonej obiektowi , metoda jest wywołwana co 5 sek, czyli 17280 razy na dobę , więc już po kilkudziesieciu minutach powoduje zajęcie całej fizycznie dostepnej pamieci

0
grzegorz_so napisał(a)

a "Try ... Finally End" lub "Try except end" nie rozwiązuja tylko chwilowo omijają problem , bo nie zwalniaja pamięci przydzielonej obiektowi

No tak, oczywiście że nie rozwiąże problemu, poniewż bloki try .. finally .. end oraz try .. except .. end nie służą do zwalniania pamięci po obiektach; Ich przeznaczenie jest zupełnie inne:

  • blok try .. finally .. end pozwala na automatyczne wykonanie części kodu pomiędzy finally .. end w razie jakiegokolwiek błedu w bloku try .. finally,
  • blok try .. except .. end pozwala na obsłużenie błędu (części kodu pomiędzy except .. end), który wystąpił pomiędzy słowami try .. except;
    Tak więc obiekty trzeba ręcznie usuwać za każdym raziem, gdy się je tworzy; Z pomocą przychodzą nam wyżej wymienione bloki by uniknąć niekontrolowanych wycieków pamięci;

Dlatego zalecana jest taka konstrukcja podczas dynamicznego tworzenia obiektów:

try
  objFoo := TFooClass(TObject);

  try
    { OPERACJE DOKONYWANE PRZY UŻYCIU OBIEKTU }
  finally
    objFoo.Free();
  end;
except
  { WYCHWYCENIE BŁĘDU DOTYCZĄCEGO TWORZONEGO OBIEKTU }
end;

metoda jest wywołwana co 5 sek, czyli 17280 razy na dobę , więc już po kilkudziesieciu minutach powoduje zajęcie całej fizycznie dostepnej pamieci

Jeśli nie zwalniałeś pamięci po tworzonych obiektach i tworzyłeś nowe "na ich miejscu" to powodowałeś potężny MemoryLeak...

Problem na pewno nie leży w samym istnieniu obiektu, bo nie dostałbyś błędu Invalid pointer operation tylko AccessViolation;

Napisz sobie prostą aplikację, w której utworzysz obiekt, wykorzystasz kilka metod (z tych, które chcesz wykorzystać w swoim programie) po czym zwolnisz pamięć po obiekcie; Jeśli żaden błąd nie wyskoczy to znaczy, że to nie klasa jest winna (być może klasa ma gdzieś bug); Jeśli jednak dostaniesz jakiś błąd tym bardziej będziesz pewny, że to nie Ty błędnie coś zaprogramowałeś; Nie wykluczam, że klasa sama w sobie ma gdzieś jakiś mankament - zdażyło mi się to już kilka razy korzystając z gotowych API;

Jednak nic więcej nie mogę podpowiedzieć - musiałbym widzieć cały kod programu i prześledzić każdą linię, w której wykorzystujesz ów obiekt; Polecam także prześledzić te linijki debugerem (linijka po linijce) i sprawdzić wszystkie zmienne pod kątem poprawności ich zawartości;

1 użytkowników online, w tym zalogowanych: 0, gości: 1