Web Servis - sunucudan dataset göndermek

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
ertank
Kıdemli Üye
Mesajlar: 1657
Kayıt: 12 Eyl 2015 12:45

Web Servis - sunucudan dataset göndermek

Mesaj gönderen ertank »

Merhaba,

Web Servis konusuna çok yeniyim. Consume etme (client) kısmı ile ilgili sorunum yok (en azından şimdilik) ancak sunucu tarafında bulduğum tüm örnekler string veya integer gibi tek değer veya "TEmployee" gibi adı, soyadı, maaşı gibi tek kayıt gönderen örnekler.

Benim yapmak istediğim ise bir Query sonucunu (DataSet) karşı tarafa göndermek. Tabi bunu yaparken kayıtlar arasında tek tek dolaşıp her bir alanı XML haline çevirmeye çalışmadan çözüm üretmek istiyorum. Göndermem gereken veriler TDateTime, string, integer, float, boolean veriler içerebilir. TDateTime içinde milisaniye bilgisi dahil olabilir.

Nasıl yapılabileceği ile ilgili açıklama veya benim bulamadığım hazır bir örnek linki çok işime yarayacaktır.

Client ve Server tarafının ikisini de Delphi 10 ile ben hazırlayacağım.

Forum içinde referans olarak bulunması açısından:
Client kısmı için bulabildiğim en iyi örnek linki: http://edn.embarcadero.com/article/28631

Teşekkürler.
ikra
Üye
Mesajlar: 900
Kayıt: 28 Nis 2005 01:26
Konum: Simdilik Topragin Üstü

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen ikra »

Eger stream gönderebiliyorsaniz datasetten dönen veriyi tclientdataset ile stream'a kaydedip gönderebilir akabinde karsi tarafta yine tclientdataset ile gelen stream'dan verileri okuyabilirsiniz.
kıdemsiz üye
ertank
Kıdemli Üye
Mesajlar: 1657
Kayıt: 12 Eyl 2015 12:45

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen ertank »

Çok basit bir örnek istesem mümkün olabilir mi? Zira, Delphi yapabiliyor ise stream gönderebilirim. Ancak, nasıl yapılacağı ile ilgili fikrim yok maalesef.
ikra
Üye
Mesajlar: 900
Kayıt: 28 Nis 2005 01:26
Konum: Simdilik Topragin Üstü

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen ikra »

Ben hic webservisler ile calismadim. Soketler ile calistim.
O nedenle cümleme "eger stream gönderebiliyorsaniz" seklinde basladim. Webservisde socket islemleri yapiyorsaniz

Kod: Tümünü seç

socket.sendbuf
metodunu kullanmaniz gerekir. Stream büyük olursa server tarafinda donma yasanabilir. Burada devreye thread ve pooling girer.
Webservis ile hic ugrasmadigim icin veri gönderimi ve alimi konusunda bilgiye sahip degilim. Yanlis bir yönlendirme yapip sizi cikmaza sürüklemek istemem.
kıdemsiz üye
Kullanıcı avatarı
vkamadan
Kıdemli Üye
Mesajlar: 1935
Kayıt: 17 Mar 2004 03:52
Konum: Adapazarı
İletişim:

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen vkamadan »

Merhabalar ,
Şahsen web servsilerde geriye dataset döndürmem gereken durumlarda JSON formatında dönüyorum. verilen dataset i JSON olarak geri veren fonksiyon olarak aşağıdaki yapıyı kullanabilirsiniz.

Kod: Tümünü seç

function DataToJSON(TableName: string;
  Dataset: TDataset): string;

  function CleanJSON( pRawData : String ) : String ;
     var
       intTemp : String ;
    begin
      intTemp :=  StringReplace( pRawData , '\' , '\\' , [rfReplaceAll] ) ;
      intTemp :=  StringReplace( intTemp , '"' , '\"' , [rfReplaceAll] ) ;
      intTemp :=  StringReplace( intTemp , #13#10 , ' ' , [rfReplaceAll] ) ;
      intTemp :=  StringReplace( intTemp , #9 , ' ' , [rfReplaceAll] ) ;
      intTemp :=  StringReplace( intTemp , #13 , '\r' , [rfReplaceAll] ) ;
      intTemp :=  StringReplace( intTemp , #10 , '\n' , [rfReplaceAll] ) ;

      Result:= intTemp;
    end;

  var
  Data                    : string;
  JSONEncodingFormat      : string;
  I                       : Integer;
  FieldValue              : string;
begin

    Dataset.First;
    while not Dataset.Eof do
      begin
          Data := Data + '{';
          for I := 0 to Dataset.FieldCount - 1 do
          begin
            FieldValue := Dataset.Fields[I].AsString;
            Data := Data + '"' + Dataset.Fields[I].FieldName + '":"' + CleanJSON( FieldValue ) + '"';

            if I < Dataset.FieldCount - 1 then
            Data := Data + ',';

          end;
          Data := Data + '},';
          Dataset.Next;
      end;
    Delete(Data, Length(Data), 1);

    JSONEncodingFormat := '{<Tablename>:[<Data>]}';
    Result := StringReplace(JSONEncodingFormat, '<TableName>', '"'+TableName+'"', [rfIgnoreCase]);
    Result := StringReplace(Result, '<Data>', Data, [rfIgnoreCase]);
end;
Volkan KAMADAN
www.polisoft.com.tr
Kullanıcı avatarı
kimimben
Üye
Mesajlar: 129
Kayıt: 28 Oca 2016 04:41
Konum: İstanbul

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen kimimben »

ertank yazdı: Client kısmı için bulabildiğim en iyi örnek linki: http://edn.embarcadero.com/article/28631
Bulduğunuz örnekte ki web servisin sunucu tarafı .net ile geliştirilmiştir.
Geriye Dataset döndürmektedir.
Dataset sınıfını incelediğimizde IXmlSerializable,ISerializable sınıflarını implemente ettiğini görmüş oluyoruz.

Yine bulduğunuz dokümanda Delphi tarafında;

Kod: Tümünü seç

 rtnZipDSResult = class(TRemotable)
rtnZipDSResult sınıfının TRemotable sınıfından miras aldığını görüyoruz.

Anahtar kelimeler : IXmlSerializable,ISerializable,TRemotable
ertank
Kıdemli Üye
Mesajlar: 1657
Kayıt: 12 Eyl 2015 12:45

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen ertank »

Biraz uğraştıktan sonra aşağıdaki gibi bir çözüm ile işimi gördüm sayılır. Anladığım kadarıyla platform bağımsız bir sistem olduğu için daha çok XML formatında dosya gönderiliyor. Dosya olarak göndermek mümkün olmadığı için XML dosyanın tamamı bir string değişken içine kaydedilerek iletiliyor.

Kod her ne kadar uzun gibi gözükse de aslında özünde basit. Birçok noktada hata kontrolü gerektiği için ve açıklama satırlarından dolayı normalden uzun oluyor. Server tarafında bilgisayar başında normalde bir kullanıcı bulunmuyor. Hata mesajlarını karşı tarafa özellikle iletmek gerekiyor ki bir sorun çıktığında ne olduğunu tespit etmek daha kolay olsun.

Kod: Tümünü seç

{ Invokable implementation File for TDeneme which implements IDeneme }

unit DenemeImpl;

interface

// Aşağıdaki "uses" ile başlayan satır zaten File-New-Other-WebServices-SOAP Server Application seçeneği ile otomatik oluşturuluyor.
// Ekstra eklenen unitelerin açıklamaları mevcut
// Ben Interface adına "Deneme" dedim. Otomatik olarak DenemeIntf.pas ve DenemeImpl.pas dosyaları oluşturuldu.
// Bu dosya DenemeImpl.pas dosyasıdır.
uses Soap.InvokeRegistry, System.Types, Soap.XSBuiltIns, DenemeIntf, 
     System.SysUtils,  // StrPCopy için kullanılıyor
     Uni, // Uni TUniQuery için kullanılıyor
     DataSnap.Provider, // TDataProvider için kullanılıyor
     DataSnap.DBClient,  // TClientDataSet için kullanılıyor
     Windows; // GetTempFileName için kullanılıyor

type

  { TDeneme }
  TDeneme = class(TInvokableClass, IDeneme)
  public
    function echoEnum(const Value: TEnumTest): TEnumTest; stdcall;
    function echoDoubleArray(const Value: TDoubleArray): TDoubleArray; stdcall;
    function echoMyEmployee(const Value: TMyEmployee): TMyEmployee; stdcall;
    function echoDouble(const Value: Double): Double; stdcall;
    function Adresler: string; stdcall; // Tablo içeriğini göndermek için kullandığım fonksiyon.
    // Bu fonksiyon ayrıca "DenemeIntf.pas" dosyasına da "IDeneme" interface içine bu fonksiyon manuel eklenmeli.
  end;

implementation

uses uData; // uData = TDM oluyor. İçinde database bağlantısını yapan TUniConnection var. 

// [...]  Aradaki otomatik oluşturulan fonksiyonları buraya kopyalamadım. Sadece kendi eklediğ fonksiyonu gösteriyorum.

function TDeneme.Adresler: string; stdcall;
var
  LProvider : TDataSetProvider;  // Query sonucunu TClientDataSet içine doldurmada aracılık yapıyor
  LClient   : TClientDataSet;  // Query sonucu bu nesne içine dolduruluyor.
  q : TUniQuery; // Bunun yerine AdoQuery veya AdoTable vs. kullanılabilir. Ben UniDAC kullanmayı tercih ediyorum.
  F:TextFile; // XML dosyasını diskten okuyup string değişkene aktarmak için
  s:string;
  lpTempFileName: Array[0..MAX_PATH] of char;  // GetTempFileName için
begin
  // Aynı anda birden fazla bağlantı talebi gelirse diye fonksiyon içinde yeni nesne oluşturuluyor.
  q := TUniQuery.Create(nil);  
  try
    q.Connection := DM.UniConnection1;
    q.SQL.Add('select * from ADRES');
    try
      q.Prepare;
      q.Open;
      // Yine birden çok bağlantı gelirse diye kod ile nesne oluşturuluyor.
      LProvider:=TDataSetProvider.Create(nil);
      try
        LProvider.DataSet := q;
        // Yine birden çok bağlantı gelirse diye kod ile nesne oluşturuluyor.
        LClient:=TClientDataSet.Create(nil);
        try
          LClient.SetProvider(LProvider);
          LClient.Active:=True;
          try
            // Geçici dosya adı al.
            // Bu dosya diskte 0 byte uzunluğunda oluşturulur.
            // Aşağıdaki LClient.SaveToFile prosedürü dosya varsa üzerine yazacağı için sorun yok.
            GetTempFileName('.', nil, 0, lpTempFileName);
            s := lpTempFileName;
            Delete(s, 1, 2); // ".\"
            // s değişkeni başka amaçlar içinde kullanılıyor.
            // Dosya adının son halini orjinal değişken içinde sakla
            StrPCopy(lpTempFileName, s);
          except
            // Kullanıcının Temp dizinine veya EXE dosyanın bulunduğu dizine yazma yetkisi yok
            s := EmptyStr;
            // Hata oluştur ki karşı tarafa da (WebServisi consume eden tarafa) sorun olduğu bildirilmiş olsun.
            raise;
          end;

          // Query sonucunu XML dosyası olarak sakla
          LClient.SaveToFile(s, dfXMLUTF8);
        finally
          LClient.Free;
        end;
      finally
        LProvider.Free;
      end;
    except
      // Query çalıştırılamadı.
      on E:Exception do begin
        raise Exception.Create('Query çalıştırılamadı. Orjinal hata mesajı:' + E.Message);
      end;
    end;

    // XML dosyasını direk değişken içine alamadığım için önce diske kaydediyorum
    // Sonra diskten değişken içine okuyorum.
    if s <> EmptyStr then begin
      if FileExists(s) then begin
        AssignFile(F, s);
        try
          Reset(F);
          Result := EmptyStr;  // Result değişkeni string cinsinden. Maksimum 2GB kapasitesi var.
          while not Eof(F) do begin
            ReadLn(F, s);
            Result := Result + s;
          end;
          CloseFile(F);  // Dosyayı kapatmayı unutma
          // Geçici dosyayı silmeyi unutma
          if not DeleteFile(lpTempFileName) then begin
            // Kod buraya kadar geldi ise geçici dosyayı diskte oluşturabildik demektir.
            // Dolayısıyla silme ile ilgili sorun yaşamamamız lazım.
            // Eğer geçici dosya silinemez ise yığınla dosya birikir.
            // GetTempFileName bir sonraki çalışmada geçici dosya adı bulmakta zorlanır.
            // Windows dizin içeriğini okumada yavaşlar.
            // vs. vs. vs.
            // Dolayısıyla hata mesajı çevir.
            raise Exception.Create('Geçici dosya silinemiyor. Dosya adı:' + lpTempFileName);
          end;
        except
          on E:Exception do begin
            raise Exception.Create('Geçici dosya okunamıyor. Dosya Adı:' + lpTempFileName + #13 + 'Orjinal hata mesajı:' + E.Message);
          end;
        end;
      end else begin
        raise Exception.Create('Geçici dosya bulunamıyor. Dosya adı:' + s);
      end;
    end else begin
      raise Exception.Create('s değişkeni boş. İçinde geçici dosya adı olması gerekiyordu');
    end;
  finally
    q.Free;  // Nesneyi manuel oluşturunca manuel Free etmek gerekiyor
  end;
end;
Kullanıcı avatarı
kimimben
Üye
Mesajlar: 129
Kayıt: 28 Oca 2016 04:41
Konum: İstanbul

Re: Web Servis - sunucudan dataset göndermek

Mesaj gönderen kimimben »

ertank yazdı:

Kod: Tümünü seç

LClient.SaveToFile(s, dfXMLUTF8);
İsterseniz diske kaydetmeden de doğrudan,SaveToStream methodu ile veriyi TMemoryStream'a aktarıp ordan da okuyabilirsiniz. :idea:
Cevapla