cub-e.net

just coding...

Github ve Nuget Projelerim Hakkinda

Dynamics CRM 2013, 2015 ve 2016 icin yillardir gelistirdigim CubeXrmFramework projesini artik arsiv haline getirdim ve yeni gelistirmeler yapmayacagim. Bu proje NuGet'de 1.000 kereden fazla indirildi ve hala aktif bir sekilde indirilebilir: https://www.nuget.org/packages/Cube.XRM.Framework 

Yerine Cube.XRM.Framework ile ve  Dynamics 365 ekosistemi uzerinde devam edecegim. Tabii ki bu yeni proje de NuGet uzerinden indirilebilir: https://www.nuget.org/packages/Cube.XRM.Framework.D365/

Github projelerimin tamamina bu adresten erisabilirsiniz: https://github.com/bkanlica

Plug-in Yapici Metodlari

Bir plug-in için Microsoft Dynamics CRM’de opsiyonel olarak kullanabileceğiniz yapici metod(constructor) türleri mevcuttur. Hic parametre vermeden yapici metod cagirabileceginiz gibi bir ya da iki parametre vererek de cagirabilirsiniz.

SamplePlugin isimli plug-in için 3 cesit yapici metod ornegi asagidaki gibidir.

public SamplePlugin()

public SamplePlugin(string unsecure)


public SamplePlugin(string unsecure, string secure)


Yapici metodun ilk parametresi public yani unsecure bilgi yigini içermelidir. Ikinci parametre ise non-public (secure) bilgi yigini içermelidir. Buradan da anlayabileceğiniz uzere secure string encrypted yani sifrelenmis veri unsecure ise unencrypted yani sifrelenmemis değer içermelidir. Office Outlook client da calisan bir plug-in yazdiysaniz bilmelisiniz ki secure string offline yani cevrimdisi modda calismayacaktir.

Bu bilgileri bir plug-in’e Plugin Registration Tool vasitasiyla bir step’in kaydi sirasinda sisteme iletiyoruz. Bu mesajlar için ayrilmis 2 alan bulanmaktadır.

Step içerisinde bu ayarlamalari yaptıktan sonra yazmis olduğumuz degerlere kod içerisinden asagidaki gibi ulaşabilirsiniz.

private readonly string _unsecureString;

private readonly string _secureString;

 

public AdvancedPlugin(string unsecureString, string secureString)

{

    if (String.IsNullOrWhiteSpace(unsecureString) ||

          String.IsNullOrWhiteSpace(secureString))

    {

        throw new InvalidOperationException

             ("Unsecure and secure strings are required
               by the Advanced Plug-in, but not provided."
);

    }

 

    _unsecureString = unsecureString;

    _secureString = secureString;

}

Dynamics CRM IFD (Windows server 2012 R2 ADFS 3.0) ile CRM for Outlook Baglanma Problemi

Eger sizde benim gibi Dynamics CRM kurulumunuz ile ADFS 3.0'i ayni server uzerinde kullaniyorsaniz Outlook Client Configuration Wizard ile yapilandirma sirasinda authentication probleminden dolayi bir hata alabilirsiniz.
Log dosyasini incelediginizde asagidaki gib bir hata mesaji olacaktir.

"Error connecting to URL: https://org.contoso.com/XRMServices/2011/Discovery.svc Exception: Microsoft.Crm.CrmException: Authentication failed"

Bu hata ile ilgili nette buldugun bircok cozeum yolunu denesem de sonuc vermedi. Bende Fiddler ile arka tarafatki iletisimde neler oldugunu izlemeye karar verdim. Fiddler ile gordum ki Configuration Wizard'in gitmeye calistigi bir adreste 503 hatasi almaktayim, Adres ise su formattaydi: 

"https://adfs.contoso.com/adfs/services/trust/mex"

Evet sorun ADFS'deydi. CRM web arabiriminde sorunsuzca gorevini yerine getiren ADFS Outlook Client'da ise hataya neden olmaktaydi. Bunun uzerine hemen ADFS Servera gittim ve arastirmalara basladim.
ADFS Serverda Event Viewer'da  sorunun kaynagini yakaladim:

Event ID:      102
Description:
There was an error in enabling endpoints of Federation Service. Fix configuration errors using PowerShell cmdlets and restart the Federation Service. 
 
Additional Data 
Exception details: 
System.ServiceModel.AddressAlreadyInUseException: There is already a listener on IP endpoint 0.0.0.0:808. This could happen if there is another application already listening on this endpoint or if you have multiple service endpoints in your service host with the same IP endpoint but with incompatible binding configurations. ---> System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted
 

Yani ADFS Server 808 nolu bir portu kullanmaktaydi. 808 Portu ayni zamanda Asynchronous Service tarafinda da kullanilmakta.
ProtocolPortDescriptionExplanation

TCP

808

CRM server role communication

The Asynchronous Service and Web Application Server services communicate to the Sandbox Processing Service through this channel. The default port is 808, but can be changed in the Windows registry by adding the DWORD registry value TcpPort in the key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\.

Yeri gelmisken bahsedeyim CRM tarafindan kullanilan diger portalarin listesi de soyle:
ProtocolPortDescriptionExplanation

TCP

80

HTTP

Default web application port. This port may be different as it can be changed during Microsoft Dynamics CRM Server Setup. For new websites, the default port number is 5555.

TCP

135

MSRPC

RPC endpoint resolution.

TCP

139

NETBIOS-SSN

NETBIOS session service.

TCP

443

HTTPS

Default secure HTTP port. The port number may differ from the default port. This secure network transport must be manually configured. Although this port is not required to run Microsoft Dynamics CRM, we strongly recommend it. For information about how to configure HTTPS for CRM, see “Make Microsoft Dynamics CRM client-to-server network communications more secure” in Post-installation and configuration guidelines for Microsoft Dynamics CRM in the Installing Guide.

TCP

445

Microsoft-DS

Active Directory service required for Active Directory access and authentication.

UDP

123

NTP

Network Time Protocol.

UDP

137

NETBIOS-NS

NETBIOS name service.

UDP

138

NETBIOS-dgm

NETBIOS datagram service.

UDP

445

Microsoft-DS

Active Directory service required for Active Directory access and authentication.

UDP

1025

Blackjack

DCOM, used as an RPC listener.

Listenin tamamina bu adresten ulasabilirsiniz. https://technet.microsoft.com/en-us/library/hh699823.aspx

Sorunumuza geri donersek 808 nolu portu degistirmem gerekiyordu. Iste asagidaki komut da tam bu ise yariyor:
Set-ADFSProperties –nettcpport 809

ADFS portunu 809 olarak degistirdim ve servisi yeniden baslattim. 
Configuration Wizard'a geri dondum ve tekrar denedim sorun cozulmustu. Zaten Fiddler'da da ilgili bolumu sorunsuzca gectigini gordum. 

Umarim sizlere de faydali olur.

*************************
21/05/2015 tarihinde ek:
Yukaridaki durum update rollup 0.1 yukledikten sonra yine devam etti. Konuyu arastirinca bu sefer de 49443 portunda ayni sorunun oldugunu gordum. Konuyu inceleyince bu portu da ADFS TlsClientPort oldugunu ogrendim.
Bu portu da yukaridaki konutla baska bir porta yonlendirdim.
Set-ADFSProperties -tlsclientport 42223

firewall ve diger ayarlari yaptiktan sonra ADFS Servisi yeniden baslattim ve hersey yoluna girdi. 

Core Entity mi Dynamic Entity mi? ( 2.Bölüm Veri Yazma)

Dynamic Entity'ler ile ilgili birinci makalemiz veri sorgulama idi. İkinci makalede ise veri yazma yöntemlerini göreceğiz. Burada dynamic entity'i anlatmadan önce core entity yöntemiyle veri yazmaya değineceğim. Böylece aradaki farkları görmemiz daha kolay olacaktır.

Konuyu hatırarsak; bir web servisini add web reference diyerek projenize dahil ettiğinizde visual studio arka tarafta bir bir class mimarisi oluşturur ve siz o mimariyi kullanırsınız. Eğer core enitity kullanırsanız, bu class özelleştirme nedeniyle her firmada farklı bir hal alabilir ve kodu yeniden derlemize neden olur. İşte bundan kurtularak çalışma zamanında nesneler yaratıp bunlara değer atamayı göreceğiz.

Hatırlarsanız bir örnek uygulamamız vardı. Senaryo şöyle idi; bir web sitemiz olacak ve bu web sitemize insanlar ad,soyad,e-mail,ilçe ve il bilgilerini yazarak kayıt olacaklar. Fakat biz forma girilen e-mail'i kullanarak veritabanımızda bu kişinin kayıtlı olup olmadığını kontrol edeceğiz eğer yoksa müşteri adayı (lead) olarak bu kişiyi Microsoft Dynamics CRM'e kaydedeceğiz.

Kaydet düğmesinin arkasında aşağıdaki kod yer almakta.
Mail adresinin yazılıp yazılmadığına bakıyoruz. Eğer yazıldı ise
dynamicRetrieve metodu ile bu mailinde birilerinin sistemde olup olmadığına bakıyoruz.



/// <summary>
///
Kaydet Dugmesi Click Olayi
/// </summary>
///
<param name="sender"></param>
///
<param name="e"></param>
protected void btnSave_Click(object sender, EventArgs e)
{
   
if (txtMail.Text != "")
    {
       
if (!dynamicRetrieve(txtMail.Text))
        {
           
if (coreCreate())
                lblMessage.Text =
"başarıyla oluşturuldu";
           
if (dynamicCreate())
                lblMessage.Text =
"başarıyla oluşturuldu";
        }
    }
}

Birinci makalede e-mail adresinden kontrol etmeyi gördük. Kontrol sonucunda e-mail adresinin sistemde bulunmadığını ve oluşturmamız gerektiğini düşünelim.


1.Core Entity

Burada önemli 3 adım bulunmakta.
1. web servisteki lead sınıfını çağırıyoruz
        lead myLead = new lead();
2. class içerisindeki proprty'e ilgili değeri atıyoruz
        myLead.firstname = txtName.Text.ToString();
3. içersisini veri ile doldurduğumuz class'ı servise oluşturması için veriyoruz.
        MyService.Create(myLead);

/// <summary>
///
coreCreate metdodu Core Entity kavramiyla lead olusturmayi bize gosterecek
/// </summary>
private bool coreCreate()
{
   
try
   
{
       
// CRM Servis'ini cagiriyoruz
       
CrmService MyService = service();
       
//lead sınıfımız aryoruz ve üretiyoruz.
       
lead myLead = new lead();
       
       
//textbox'ın değer içerip içermediğine bakıyoruz.
       
if (txtName.Text != string.Empty)
           
//eğer değer içeriyor ise class içerisindeki ilgili attribute a değerini veriyoruz.
           
myLead.firstname = txtName.Text.ToString();
       
if (txtSurname.Text != string.Empty)
            myLead.lastname = txtSurname.Text.ToString();
       
if (txtMail.Text != string.Empty)
            myLead.emailaddress1 = txtMail.Text.ToString();
       
if (txtState.Text != string.Empty)
            myLead.address1_stateorprovince = txtState.Text.ToString();
       
if (txtCity.Text != string.Empty)
            myLead.address1_city = txtCity.Text.ToString();
       

        //Son olarak da değer atamalarımız bitince servisin Create metodu ile
        //lead imizi oluşturuyoruz.
       
MyService.Create(myLead);
       
return true;
    }
   
catch (Exception ex)
    {
        HandleException(ex);

        return false;
    }
}

2. Dynamic Entity

Eğer bu oluşturma işlemini dynamic entity özelliğini kullanarak yapmış olsaydık kod bu sefer aşağıdaki gibi olacaktı.
Ama önce ne yaptığımızı anlatalım;

1. CRM Servis'ini cagiriyoruz. Burada service(); benim yazdığım bir metod ve crm servisini oluşturuyor.

       
CrmService MyService = service();
2. DynamicEntity nesnesini yaratyoruz

        DynamicEntity leadEntity = new DynamicEntity();
3. Entity ismini veriyoruz.

       
leadEntity.Name = EntityName.lead.ToString();
4. Eger deger iceriyorsa; Property nesnemizi cagiriyoruz ve adın ve degeri veriyoruz. Boylece calisma aninda bir class'in icerisindeki bir nesneye deger vermis oluyoruz.

           
StringProperty firstname = new StringProperty();
            firstname.Name =
"firstname";
            firstname.Value = txtName.Text;

Tabii burada yeri gelmişken söz etmekte fayda var CRM sadece string türünde bir değişken biçimi ile çalışmıyor. Yukarıdaki örnek "StringProperty" classından bir değişken türeterek string oluşturmaya yarıyor. Diğer değişken türleri (sınıflar) ise aşağıdaki listede yer almakta;


Sınıf Adı Microsoft Dynamics CRM Attribute Türü
CrmBooleanProperty CrmBoolean
CrmDateTimeProperty CrmDateTime
CrmDecimalProperty CrmDecimal
CrmFloatProperty CrmFloat
CrmMoneyProperty CrmMoney
CrmNumberProperty CrmNumber
CustomerProperty Customer
DynamicEntity N/A
DynamicEntityArrayProperty N/A
EntityNameReferenceProperty EntityNameReference
KeyProperty Key
LookupProperty Lookup
OwnerProperty Owner
PicklistProperty Picklist
Property N/A
StateProperty State
StatusProperty Status
StringProperty String
UniqueIdentifierProperty UniqueIdentifier
N/A Internal

Memo

Virtual



///
<summary>
///
dynamicCreate metdodu Dynamic Entity kavramiyla lead olusturmayi bize gosterecek
/// </summary>
private bool dynamicCreate()
{
    try
   
{
       
// CRM Servis'ini cagiriyoruz
       
CrmService MyService = service();
   
       
// DynamicEntity nesnesini yaratyoruz
       
DynamicEntity leadEntity = new DynamicEntity();
       
// Entity ismini veriyoruz.
       
leadEntity.Name = EntityName.lead.ToString();
       
// Property'ler icin bir Array olusturuyoruz.
       
ArrayList arrProps = new ArrayList();
   
       
// Textbox deger iceriyor mu diye kontrol ediyoruz.
       
if (txtName.Text != string.Empty)
        {
           
// Eger deger iceriyorsa; Property nesnemizi cagiriyoruz ve
            // adn ve degeri veriyoruz. Boylece calisma aninda bir class'in
            // icerisindeki bir nesneye deger vermis oluyoruz.
           
StringProperty firstname = new StringProperty();
            firstname.Name =
"firstname";
            firstname.Value = txtName.Text;
            arrProps.Add(firstname);
        }
       
if (txtSurname.Text != string.Empty)
        {
           
StringProperty lastname = new StringProperty();
            lastname.Name =
"lastname";
            lastname.Value = txtSurname.Text;
            arrProps.Add(lastname);
        }
       
if (txtMail.Text != string.Empty)
        {
           
StringProperty mail = new StringProperty();
            mail.Name =
"emailaddress1";
            mail.Value = txtMail.Text;
            arrProps.Add(mail);

        }
       
if (txtState.Text != string.Empty)
        {
           
StringProperty state = new StringProperty();
            state.Name =
"address1_stateorprovince";
            state.Value = txtState.Text;
            arrProps.Add(state);
        }
       
if (txtCity.Text != string.Empty)
        {
           
StringProperty city = new StringProperty();
            city.Name =
"address1_city";
            city.Value = txtCity.Text;
            arrProps.Add(city);
        }
       
// Property'leri bir DynamicEntity'e teslim ediyoruz.
       
leadEntity.Properties = (Property[])arrProps.ToArray(typeof(Property));
       
       
// Ve final entity'imizi olusturmasi icin servise veriyoruz.
       
MyService.Create(leadEntity);
        return true;
    }
   
catch (Exception ex)
    {
        HandleException(ex);
   
    return false;
    }
}


Uygulamamızı çalıştırdığımızda aşağıdaki ekran görüntüsünü elde edeceğiz.



Bir makale daha burada biter. Hepiniz hoşçakalın.

Barış KANLICA
Yazılım Uzmanı – Software Specialist
brsk@e-kolay.net
www.cub-e.net

 

Core Entity mi Dynamic Entity mi? ( 1.Bölüm Veri Sorgulama)

Microsoft Dynamics CRM üzerinde program geliştiriren herkes veri yazarken ve veri okurken web servisinden faydalanmamız gerektiğini bilir. (Veri okurken ek olarak fetchxml ve filteredview da kullanılabilir)
 
Peki bu işlemler sırasında web servisindeki nesnelere nasıl erişirsirsiniz?

Peki CRM üzerinde çalışacak bir uygulama geliştirdiniz ve bu uygulama web servislerini kullanarak CRM üzerine veri yazan bir uygulama olsun. Amacınız bu uygulamayı CRM kullanan firmalara satarak çok para kazanmak. Ama her müşterideki özelleştirme (customization) birbirinden farklı olacağından web servisleri de birbirinden farklı olacak. Çünkü standart yöntemde web servisindeki bir sınıf türetilerek, türetilen sınıftaki nesnelere erişilir. Her firmada nesneler de birbirinden farklı olabilir.  Sonucunda uygulamanızı her müşteri için derleyecek misiniz?

Bu çok saçma ve çok yorucu olurdu. Bu durumda bize kod zamanında değil çalışma zamanında nesneleri yönetmemizi sağlayacak bir yapıya ihtiyacımız var. Yani çalışma zamanında nesneler yaratıp bunlara değer atamalıyız.

İşte CRM içerisinde bu düşünülmüş, Core Entity yani standart nesne yönetimini yerine Dynamic Entity modeli ile nesneleri çalışma zamanında oluşturabilir ve yönetebiliriz. Dynamic Entity yöntemiyle veri sorgulayabilir ve veri yazabiliriz. Sırasıyla bu ikisini göreceğiz.

Konuyu örnek uygulamamızda irdeleyelim. Örnek uygulamamız için şöyle bir senaryo düşünelim. Bir web sitemiz olacak ve bu web sitemize insanlar ad,soyad,e-mail,ilçe ve il bilgilerini yazarak kayıt olacaklar. Fakat biz forma girilen e-mail'i kullanarak veritabanımızda bu kişinin kayıtlı olup olmadığını kontrol edeceğiz eğer yoksa müşteri adayı (lead) olarak bu kişiyi CRM'e kaydedeceğiz.




Kaydet düğmesinin arkasında aşağıdaki kod yer almakta.
Mail adresinin yazılıp yazılmadığına bakıyoruz. Eğer yazıldı ise
dynamicRetrieve metodu ile bu mailinde birilerinin sistemde olup olmadığına bakıyoruz.

/// <summary>
///
Kaydet Dugmesi Click Olayi
/// </summary>
///
<param name="sender"></param>
///
<param name="e"></param>
protected void btnSave_Click(object sender, EventArgs e)
{
   
if (txtMail.Text != "")
    {
       
if (!dynamicRetrieve(txtMail.Text))
        {
           
if (coreCreate())
                lblMessage.Text =
"başarıyla oluşturuldu";
           
if (dynamicCreate())
                lblMessage.Text =
"başarıyla oluşturuldu";
        }
    }

}

Dynamic Entity yönetemiyle veri sorgulamayı göreceğiz ilk önce. (Core entity ve diğer veri sorgulama yöntemleri ile ilgili olarak 3 makalelik bir yazı dizisi yayınlamıştım hatırlarsanız o yüzden bu makalede bu konulara girmeyeceğim.)

İlk önce kodu inceleyelim.


1. query nesnemizi oluşturuyoruz

        QueryByAttribute query = new QueryByAttribute();

2. hangi entity üzerinde sorgulama yapacağımızı söylüyoruz.

        query.EntityName =
EntityName.lead.ToString();

3. sorgulama sonucunda hangi alanları geri istiyoruz bunu söylüyoruz

       
ColumnSet cs = new ColumnSet();
        cs.Attributes =
new string[] { "firstname","lastname","address1_city" };
        query.ColumnSet = cs;

 4. koşulumuzu veriyoruz.
        query.Attributes =
new string[] { "emailaddress1" };
        query.Values =
new object[] { mail };

        Aslında buraya kadar ( " select firstname","lastname","address1_city from filteredlead where emailaddress1 = '"+ mail +"' ") dedik. Ama servis kullanarak bunu demek daha zahmetli.

5. servisimizi çağırıyoruz. Burada service(); benim oluşturduğum bir metod. CRM 3.0 ve CRM 4.0 arasında servise bağlanmakta farklılıklar var. Bunu daha önceki bir makalemde anlattığım için burada anlatmıyorum.

       
CrmService MyService = service();

6. işte can alıcı nokta burası geriye core entity değil dynamic entity istediğimizi söylüyoruz.
        retrieved.ReturnDynamicEntities =
true;

7. gelen sınıfı DynamicEntity türüne çeviriyoruz.
           
DynamicEntity entity = (DynamicEntity)responsed.BusinessEntityCollection.BusinessEntities[0];

8. işte ikinci can alıcı nokta burası. gelen DynamicEntity içinden property leri birer birer çıkartıyoruz.
Gelen property inin Value attribute 'u içerisinde değer bulunmakta bunu set ediyoruz.
           
StringProperty property = (StringProperty)entity.Properties[i];
            firstname = property.Value;

Kodun tamamı aşağıdadır.

/// <summary>
///
dynamicRetrieve metdodu Dynamic Entity kavramiyla veri cekmeyi bize gosterecek
/// </summary>
private bool dynamicRetrieve(string mail)
{
   
try
   
{
        //query nesnemizi oluşturuyoruz
        QueryByAttribute query = new QueryByAttribute();

        //hangi entity üzerinde sorgulama yapacağımızı söylüyoruz.
        query.EntityName =
EntityName.lead.ToString();

        //sorgulama sonucunda hangi alanları geri istiyoruz bunu söylüyoruz
       
ColumnSet cs = new ColumnSet();
        cs.Attributes =
new string[] { "firstname","lastname","address1_city" };
        query.ColumnSet = cs;

        //koşulumuzu veriyoruz.
        query.Attributes =
new string[] { "emailaddress1" };
        query.Values =
new object[] { mail };

        //aslında buraya kadar ( " select firstname","lastname","address1_city from filteredlead where emailaddress1 = '"+ mail +"' ") dedik.

        //
servisimizi çağırıyoruz.
       
CrmService MyService = service();
       
RetrieveMultipleRequest retrieved = new RetrieveMultipleRequest();

        //işte can alıcı nokta burası geriye core entity değil dynamic entity istediğimizi söylüyoruz.
        retrieved.ReturnDynamicEntities =
true;
        retrieved.Query = query;
       
RetrieveMultipleResponse responsed = (RetrieveMultipleResponse)MyService.Execute(retrieved);
       
if (responsed != null && responsed.BusinessEntityCollection.BusinessEntities.Length > 0)
        {

   
        //gelen sınıfı DynamicEntity türüne çeviriyoruz.
           
DynamicEntity entity = (DynamicEntity)responsed.BusinessEntityCollection.BusinessEntities[0];
           
string firstname = "";
           
string lastname = "";
           
string city = "";
           
for (int i = 0; i < entity.Properties.Length; i++)
            {
               
if (entity.Properties[i].Name.ToLower() == "firstname")
                {
           
        //işte ikinci can alıcı nokta burası. gelen DynamicEntity içinden property leri birer birer çıkartıyoruz.
                    //gelen property inin Value attribute 'u içerisinde değer bulunmakta bunu set ediyoruz.
                   
StringProperty property = (StringProperty)entity.Properties[i];
                    firstname = property.Value;
                }
               
if (entity.Properties[i].Name.ToLower() == "lastname")
                {
                   
StringProperty property = (StringProperty)entity.Properties[i];
                    lastname = property.Value;
                }
               
if (entity.Properties[i].Name.ToLower() == "address1_city")
                {
                   
StringProperty property = (StringProperty)entity.Properties[i];
                    city = property.Value;
                }
            }
           
string result = "<table border=1><tr>";
            result +=
"<td>Ad</td>";
            result +=
"<td>Soyad</td>";
            result +=
"<td>Şehir</td>";
            result +=
"</tr>";
            result +=
"<tr>";
            result +=
"<td>" + firstname + "</td>";
           
result += "<td>" + lastname + "</td>";
            result +=
"<td>" + city + "</td>";
            result +=
"</tr></table>";
            lblMessage.Text =
"eşleşen kayıtlar bulundu";
            lblMessage.Text += result;
           
return true;
        }
       
return false;
    }
   
catch (Exception ex)
    {
        HandleException(ex);
       
return false;
    }
}

Daha önceden sitemize kayıt olmuş test1 kişisinin sitemize tekrar kayıt olmaya çalıştığını düşünelim uygulama eşleşen bir kayıt bulacağından görüntü şöyle olacak.




Bu makaleye de burada noktayı koyalım. Unutmayın sırada veri yazma ile ilgili bir makale var.

Barış KANLICA
Yazılım Uzmanı – Software Specialist
brsk@e-kolay.net
www.cub-e.net