These columns don’t currently have unique values

Eylül 3, 2008

Uzun zamandır bloğa birşey yazamıyorum.Nedeni yok evde oyun oynuyorum insanın kendisine de biraz zaman ayırması gerektiğine inanıyorum :)

Neyse gelelim hataya:

Bu hata ile karşılaşmanın sebebi şudur,bir dataset döndürüceksiniz ve dataset içindeki bir tabloda uniqu alan belirlediniz.Örneğin ÜrünID unique varsayalım.Execute ettiğiniz query bir den fazla uniqe kayıt içeren değer döndürüyor demektir.Yani aynısından 2 tane 123 gelirse bu hatayı alırsınız.

Not:Ben bu hatayı Datatable’i programmatik olarak oluştururken almıştım.

Bir hatayı daha çözdük sayın seyirciler.Yok mu daha


String or binary data would be truncated. The statement has been terminated.

Temmuz 29, 2008

Eğer bir asp.net uygulamasında bu hatayı alıyorsanız,bunun nedeni insert/update etmek istediğiniz bir  alana, belirlediğinizden daha uzun bir veri girmeye çalışıyorsunuz demektir.Yani nvarchar(20) lik alana 25 karakterlik bir metin girmeye çalıştığınız da bu hatayı alırsınız.

Bir sorunu daha çözdük sayın seyirciler yok mu daha :D


Data Access Layer(DAL) yazmak

Temmuz 25, 2008

Sevgili dostum Mehmet Aydın Ünlü’nün yazmış olduğu makale oldukça güzel ve açıklayıcı.Kullanışlı bir veri erişim modeli nasıl oluşturulur?

Burdan erişebilirsiniz.


ASP.Net Component Paketi

Temmuz 25, 2008

Coolite component paketi içinde birçok component var ve gerçektende çok güzel kullanmanızı tavsiye ederim.Ücretsizdir.


ASP.NET QueryString Güvenliği

Temmuz 25, 2008

QueryString’leri birçoğumuz yoğun olarak kullanmaktayız.Günümüzde sistemlerde oluşan güvenlik açıklarından bir tanesi de yanlış ve kontrol edilmeyen querystring’lerden oluşmaktadır.Peki bunu nasıl engelleyebiliriz?

Düşünüyorum:

1-Bir sayfaya gelecek bütün querystring’leri almalıyım.

2-Bu gelen querystring collection’u analiz etmeliyim.

3-Kötü bir durum var ise engellemeliyim.

Algoritma tamam.Uygulamaya geçelim.İlk once sayfaya gönderdiğimiz querystring’leri nasıl toplayabiliriz?.

Querystring’ler .net’in NameValueCollection sınıfından türemektedir.Söyle bir querystring’imiz olduğunu varsayarsak:

update.aspx?ID=45&Mod=A&qu_id=45567 //tamamen salladım

Burdaki querystring değerlerini direk sql içine gömüyorsak;

Select *from urunler where id=”+Request.QueryString[ID]+”;

gibiSqlInjection yeme şansımız oldukça yüksektir veya aldığımız querystring’i direk yazdırıyor isek;

Response.Write(Request.QueryString[ID].ToString());

Bu seferde XXS(Cross-site Scripting) yeme şansımız yüksektir.Devam edelim.

private bool IsQueryStringsSafety(NameValueCollection qry)

{

string[] badWords ={ “DELETE”, “delete”, “‘”, “select”,”DELETE FROM”,”delete from”,”UPDATE”,”update”,”UPDATE FROM”,”update from”,”select *from”,”SELECT *FROM”,”<”,”>”,”OR”,”AND”,”OR=”,”=”,”‘OR=’”,”‘OR’”};

for (int i = 0; i <= qry.AllKeys.Length – 1; i++)

{

for (int a = 0; a <= badWords.Length – 1; a++)

{

if (qry[i] == badWords[a])

{

return false;

}

}

}

return true;

}

Yukarda yazdığım fonksiyon belirlenen değerler ile gelen querystring’leri karşılaştırarak kötü bir kod’un olup olmadığını bize bool türünden döndürmektedir.badWords’ü ben string dizi olarak yaptım isteğe gore xml’de tutulabilir,vt’de tutulabilir v.s.

using System.Collections.Specialized; //eklemeyi unutmayalım.

protected void Page_Load(object sender, EventArgs e)

{

private NameValueCollection qStrings;

//Gönderilen bütün querystring’leri kolleksiyona alalım

qStrings = Request.QueryString;

if (IsQueryStringsSafety(qStrings))

{

//eğer güvenli ise

}

else

{

//yapılacak işlemler.Kullanıcı ip’si alınabilir(mesela)

}

}

Ufak ama etkili bir yöntem.

Rock C# !! :)


ASP.NET Uygulama bazında hata yakalama(Application Error Management)

Temmuz 25, 2008

Bir çoğumuz yaptığımız sistemlerde oluşan hatalardan dolayı sıkıntıya düşmüşüzdür.Gelişmiş uygulamalarda oluşan hatalar,buglar kullanıcıya gösterilmez.Bunun yerine kullanıcıya yardımcı bir sayfa gösterilir.Mesela “Sistemde beklenmeyen bir hata oluştu.Lütfen söyle yapınız,böyle yapınız” şeklinde.Oluşan hataları log’lamak gerekir.Bu log’lama geliştirici açısından önemli olup,en çok nerelerde hata yaptığını da raporlayamabilir.

Asp.net’te hata yönetimi 2 türlü yapılmaktadır.Kod içerisinde try-catch-finally blog’ları kullanarak veya uygulama statüsünde global.asax dosyasındaki Application_Error methodunu kullanarak.

Şimdi sistemimize bir hata yöneticisi yapmayı planlıyoruz? Peki bu nasıl olacak?.Nasıl bir yöntem izlemeliyiz.?

Zaten try-catch-finally bloglarını kod içerisinde kullanmamız bize oldukça faydalı olacaktır.Biz uygumalama bazında hata yönetimi yapalım.

Yapacağımız ilk iş uygulamada oluşacak hatayı bulmaktır.Global.asax dosyası içinde:

void Application_Error(object sender,EventArgs e)

{

//son oluşan hatayı yakalıyoruz.

Exception ex = Server.GetLastError().GetBaseException();

//hatayı temizliyoruz.

Server.ClearError();

}

Burda birkaç yöntemden bahsetmek istiyorum.Biz bu aldığımız hatayı ne yapacağız?.İster loglayalım,ister bize e-mail olarak gelsin.Bence e-mail olarak gelsin:)

Global.asax dosyasının başına şu library’i ekleyelim:

using System.Net.Mail;

{

//son oluşan hatayı yakalıyoruz.

Exception ex = Server.GetLastError().GetBaseException();

//hatayı temizliyoruz.

Server.ClearError();

//Mail gönderme kodlarını yazalım.

StringBuilder sb = new StringBuilder();

sb.Append(“Stack Trace:”+ex.StackTrace.Trim()+”<br>”);

sb.Append(“Error Message:”+ex.Message+”<br>”);

string MailHTML = sb.ToString();

SmtpClient smtpserver = new SmtpClient();

MailAddress mSender = new MailAddress(“GonderenAdres@mailadresi.com”);

MailAddress MailTo = new MailAddress(kimegonderilecek@mailadresi.com);

MailMessage newMail = new MailMessage(mSender, MailTo);

newMail.IsBodyHtml = true;

newMail.Subject = “Koş admin koşş!”;

newMail.Body = MailHTML;

smtpserver.Send(newMail);

//Burda da kendi yaptığımız bir sayfaya yönlendirelim.

Response.Redirect(“~/Error.aspx(veya .html)”)

}

Tabi mail’i göndermeniz için web.config dosyası içinde smtp ayarlarını yapmamız gerekiyor.

<system.net>

<mailSettings>

<smtp from=”GonderenAdres@maildaresi.com”>

<network port=”25″ host=”mailsunucuadresi” userName=”kullaniciadi” password=”sifre” defaultCredentials=”true”/>

</smtp>

</mailSettings>

</system.net>

Bu ayarları yaptıktan sonra oluşan hata direk mailimize gelicektir :) Allah aşkına zormu :D

İyi kodlamalar..


C# Kodlama Standartları(Coding Standarts for C#)

Temmuz 25, 2008

İsimlendime düzeni ve yazım stillleri

1-Metod isimleri,sabit değişkenler ve tip değişkenlerinde pascal kalıplarını kullanın:

public class BenimClass

{

const int DefaultSize=100;

public void SomeClass()

{

}

}

2-Method değişkenlerinde ve lokal değişkenlerde deve tipi yazım (camel casing) kullanın.

Void BenimClass(int someNumber)

{

int number;

}

3-Arayüz tanımlamarıda “I” ön ekini kullanın.

interface IBenimArayuzum {…}

4.Bir classa ait özel değişkenlerde(private) “m_” gibi ön ekler ve pascal yazım tipini kullanın.

public class SomeClass()

{

Private int m_Number

}

5.Methodlara isim verirken yüklem-nesne ikilemesinde olmasına dikkat edin.Örneğin ShowDialog() -pencereyi göster.

6.Geriye değer döndüren methodlardaki isimlendirmelerde geriye döndürülen değer hakkında bilgi içermesine dikkat edin.Örneğin GetObjectState()-Nesne durumunu getir.(Neyin geleceğini biliyorum)

7.Anlaşılabilir değişken isimleri kullanın.Tek karakterlik değişken isimlerinden kaçının.Kelimeleri kısaltmayın(number yerine num yazmayın)

8.Generik kolleksiyonlar kullanır iken,tipler için baş etiketleri kullanın.

//Doğru

Public class LinkedList<K,T>

{…}

//Yanlış

Public class LinkedList<KeyType,DataType>

{…}

9.Namespace’leri(isim alanları) oluştururken anlamlı isimler verin,örneğin ürün veya firma isimleri..

10.Tip tanımlamalarında full isimleri kullanmayın.Örneğin;

System.Collections.ArrayList myarray=new System.Collections.ArrayList;

Bunun yerine using ile bu alan ismini tanımlayın.

11.using kelimesinin namespace’ler içinde kullanmaktan kaçının.

12.Tanımlanan bütün isim alanlarını alt alta gruplayın.

using System;

using System.Collections.Generic;

using System.Data;

using MyNameSpace;

using MyNameSpace2;

13.Paragraf başı kullanımını sürdürün.Tab’ları kullanmayın.

14.Class içinde tanımladığınız değişkenleri daima üstte tutun ve method tanımlamaları ile arasında bir boşluk bırakın.

public class BenimClass

{

int m_Number;

string m_Name;

public void Method1(){}

public void Method2(){}

}

15. “{“ işaretini daima yeni bir satıra indirin.

16.delegate tanımlamalarında aşağıdaki gibi yöntem izleyin:

delegate void SomeDelegate(string someString);

void InvokeMethod()

{

SomeDeleagete someDelegate=delete(string name)

{

MessageBox.Show(name);

}

}

17.Parametresiz delegate tanımlamalarında şu yöntemi izleyin:

delete void SomeDelegate();

//Doğru

SomeDeleagete someDelegate=delegate()

{

MessageBox.Show(name);

}

//Yanlış

SomeDeleagete someDelegate=delegate

{

MessageBox.Show(name);

}

Bu yazı IDesign tarafından yayınlanmıştır.Çevirisini ben yaptım:) Lütfen kötü olan yerleri bildirin:)

Not:Devamı gelecektir.


ASP.NET Connection String’i Şifreleme

Temmuz 24, 2008

Güvenlik şüphesiz ki uygulamalarımızdaki en önemli etmenlerden birisidir.Asp.net’te connectionstring’ler ile veritabanına bağlantı kurarız ama bu ne kadar güvenlidir?Hadi bunu şifreleyelim:

//Configürasyon dosyamızı açıyoruz ve içinden “connectionstrings” adlı sectionu alıyoruz.

Configuration config = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
ConfigurationSection configSection = config.GetSection(“connectionStrings”);

//zaten şifreli ise

if (configSection.SectionInformation.IsProtected)
{

//şifreyi kaldır
configSection.SectionInformation.UnprotectSection();
config.Save();
}
else
{

//yoksa şifrele
configSection.SectionInformation.ProtectSection(“DataProtectionConfigurationProvider”);
config.Save();
}

“DataProtectionConfigurationProvider” yazan yere asp.net’te bulunan 2 tip encryption provider’ından birini kullanabiliriz.

* RSAProtectedConfigurationProvider : Asp.net’in default olarak atadığı sağlayıcıdır ve  RSA(Açık anahtarlı şifreleme yöntemi.Bakınız.Wiki-RSA)
* DPAPIProtectedConfigurationProvider : Windows Veri Şifreleme Yöntemini (DPAPI) kullanır.(Bakınız.Wiki-DPAPI)

Not:Eğer bu işlemi dinamik olarak kod içerisinden yaparsanız; web uygulaması kendini resetleyecektir.Tüm sessionlar ve application nesneleriniz sonlanabilir.


ASP.NET Veri Erişim Sağlayıcıları(Data Providers) Nelerdir?

Temmuz 23, 2008

ADO.NET iki adet bağlantı nesnesi içerir.Connection-based ve content-based:

Connection-Based Objects:Veri sağlayıcı nesnelerden oluşur.Bağlantı(Connection),Komut(Command),DataAdapter(DataUyarlayıcı),DataReader(DataOkuyucu).Bu nesneler SQL ifadelerini çalıştıran veriyi bağlayan,veri kümelerini dolduran nesnelerdir.

Content Based Objects:Bu nesneler ise veri için gerekli olan paketlerdir.DataSet(Veri Kümesi),DataColumn(VeriKolonu),DataRow(VeriSatırı),DataRelation(Datailişkisi) ve birkaç nesneyi daha destekler.

Bildiğimiz üzere bu nesneleri kullanabilmemiz için bazı “namespace” lere ihtiyacımız vardır.Yani isimalanlarına şimdi ise bunları görelim..

System.Data:Anahtar veri kümelerini,ilişkiler,tablolar,görünümler gibi bir çok bağlantı nesnesini içerir.

System.Data.Common:Bu tür veri sağlayıcılar kendi sınıfında uzmanlaşmıştır.Arayüz olarak System.Data ve ADO.NET sınıflarını kullanır.

System.Data.OleDb:OleDb veri sağlayıcılarına bağlanmak için kullanılan sınıftır.OleDbCommand, OleDbConnection, OleDbDataAdapter’kullanır.

System.Data.SQLSlient:Microsoft SQL Server’a bağlanmak için kullandığımız SQLDbCommand,SqlDbConnection, SqlDBDataAdapter sınıflarını içerir. Bu sınıflar, SQL sunucuya TDS arayüzü kullanmak için optimize edilir.

System.Data.OracleClient:Oracle veritabanına bağlanmak için kullanılan sınıftır.OracleCommad,OracleConnection ve OracleDataAdapter’ı kapsar.

System.Data.Odbc:ODBC türü veritabanlarına bağlanmak için kullanılan sınıftır.OdbcCommand, OdbcConnection ve OdbcDataAdapter’ı içerir.

Bağlantı Cümleleri

(Connection Strings)

Bağlantı nesnelerini oluşturduğumuzda,tek ihtiyacımız olan bunu tetikleyecek bağlantı cümlesidir.Bağlantı cümlelerimiz bize şu avantajları sağlar:

Serverda veritabanımız nerde?

Veritabanımızda ne kullanmak istiyoruz?

Nasıl veritabanı bizi tanıyabilir?

Gibi işlemleri “connection strings” ile sağlarız.Bir örnek verecek olursak:

string connectionString = “Data Source=localhost;Initial Catalog=Sales;” +

“user id=sa;password=;Provider=MSDAORA”;

Sırasıyla gidicek olursak veritabanımız “Localhostta”,”Sales” isimli ve kullanıcı adı olarak “sa” şifreyi de boş geçtik,Sağlayıcı ise “MSDAORA” dır.

Haydi bir örnek uygulama yapalım:

İlk önce web.config dosyamızda connection string olarak şunu tanımlıyoruz..

<configuration xmlns=”http://schemas.microsoft.com/.NetConfiguration/v2.0″>

<connectionStrings>

<add name=”Northwind” connectionString=

“Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI”/>

</connectionStrings>

</configuration>

Bu işlemden sonra artık bağlantımızı test etmeye geldi.

// Bağlantı nesnemizi oluşturalım.

string connectionString =

WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;

SqlConnection con = new SqlConnection(connectionString);

try

{

// Bağlantıyı açmayı deniyelim.

con.Open();

lblInfo.Text = “<b>Server Version:</b> ” + con.ServerVersion;

lblInfo.Text += “<br /><b>Connection Is:</b> ” + con.State.ToString();

}

catch (Exception err)

{

// Hata alırsak

lblInfo.Text = “Error reading the database. “;

lblInfo.Text += err.Message;

}

finally

{

con.Close();

lblInfo.Text += “<br /><b>Now Connection Is:</b> “;

lblInfo.Text += con.State.ToString();

}

Bunu yaptıktan sonra sayfamızı çalıştıralım.Karşımıza şöyle bir sayfa çıkarsa sorun yoktur.

Server Version:”Sizin server versiyonunuz”

Connection Is: Open

Now Connection Is:Closed


ASP.NET GoogleMaps Kontrolü

Temmuz 23, 2008

GoogleMaps Google’in uydu üzerinden elde ettiği fotoğrafları internet ortamında kullanmasıdır.Bunun için kendine bir API(Application Programming Interface) oluşturmuştur.Geliştiriciler bu api’yi kullanarak istedikleri yeri kolay bir şekilde görüntüleyebilmektedir.Bu kadar google’ reklamı yeter :) Makaleme başlıyorum :)

Makale’nin ana konusuna girmeden önce şu linkteki bilgileri okumanızı öneririm.Zira makale içinde kullanacağımız javascript kodlarının ne anlama geldiğini bilmemiz gerekiyor.

http://code.google.com/apis/maps/documentation/reference.html

Burada google’in bize sunduğu api’nin tüm açıklaması yapılmıştır.Ayrıca demo gallery ve examples bölümlerinde örnekler verilmiştir.Ayrıca googlemaps’i kullanabilmemiz için google’dan bir “apikey” elde etmemiz gerekiyor.Onuda buradan elde edebilirsiniz.

http://code.google.com/apis/maps/signup.html

Javascript bilginizin biraz olması gerekiyor.Çünkü tamamıyla client-side olarak çalışan bu api’de çok işimize yaracaktır.Ama biz bunu server-side’a dönüştüreceğiz.(Nasıl yani?)

Google’ın dokümantasyonu inceledikten sonra tamamiyle client-side olan bu api’yi asp.net içerisinde nasıl kullanacağım’ı araştırmaya başladım.Aslında zor olan bir şey yok javascript kodlarını sayfaya koyunca harita yükleniyor ve istediğim sonucu alıyorum.

function load() {

if (GBrowserIsCompatible()) {

map = new GMap2(document.getElementById(“map”));

map.addControl(new GSmallMapControl());

map.addControl(new GMapTypeControl());

map.addControl(new GOverviewMapControl());

map.addControl(new GScaleControl());

map.setCenter(new GLatLng(38.4, 31.5), 5);

}

}

Bu fonksiyonu body tagının onLoad event’ına(<body onLoad=”Load()”) yazınca harita yükleniyor.Bu ne anlama geliyor? Şu anlama geliyor:

if (GBrowserIsCompatible())

{

}

Bu kod browser’ın harita için uyumluluğu kontrol eder.Harita yüklebilir bir browser ise yükler.(Daha fazla bilgi için dökümanlara bakınız.)

map = new GMap2(document.getElementById(“map”));

Bu kod sayfa üzerinde belirlenen bir element’in id’sine göre yükleneceği yeri belirler.Bu bir div olabilir tablo olabilir.

map.addControl(new GSmallMapControl());

map.addControl(new GMapTypeControl());

map.addControl(new GOverviewMapControl());

map.addControl(new GScaleControl());

map.setCenter(new GLatLng(38.4, 31.5), 5);

Bu kodlar ise haritanın kontrolleridir.Harita üzerine istenilen kontrolleri yükler.Ayrıca map.SetCenter ise yükleme anında haritanın hangi koordinatlarına gidileceğini belirler.Yani yukarıdaki koordinatlar ile yüklerseniz Türkiye haritasından başlar..

Buraya kadar tamam ama hala tatmin olabilmiş değilim.Tamamiyle cilent-side çalışmak gözümü korkutmaya başlamıştı.Benim istediğim şuydu,C# ile bu haritayı parmağımda oynatabilmeliydim..

Araştırmaya başladım..Bir çok makale ve örnekten sonra www.codeproject.com da bir örneğe rastladım.Tam istediğim gibiydi.C# ile her şeyi yapabilme özgürlüğü veriyordu..

http://www.codeproject.com/KB/webforms/Google-Maps-User-Control.aspx

Bunu ben yapamazmıydım? İlk aklıma gelen şey buydu.C# olarak sorun yoktu fakat javascript olarak sorun vardı:) C# ile javascript’i nasıl birleştirmeliydim?

Örneği 1-2 gün boyunca incedim.Örnekte sadece bir googlemaps class’ı (cGoogleMap.cs) bir tane webservis(GService.cs) ve bir tanede user control bulunmakta.(GoogleMapControl.ascx)

Örneği indirip incelerseniz user control’de söyle bir ibare yer alıyor:

<asp:ScriptManager ID=”ScriptManager1″ runat=”server”>

<Services>

<asp:ServiceReference Path=”~/GService.asmx” />

<asp:ServiceReference Path=”~/GoogleMapForASPNet.ascx” />

</Services>

</asp:ScriptManager>

Örneği yazan kişi scriptmanager’a yazmış olduğu servisi referans olarak gösteriyor ve C# classından elde ettiği bilgileri servis aracılığyla javascript’ten çağırıyor.

Can alıcı noktalardan bahsetmek istiyorum.GoogleMapsControl.ascx’te önemli 2 tane fonksiyon bulunmakta:

function fGetGoogleObject(result, userContext)

function fGetGoogleObjectOptimized(result, userContext)

1.Fonksiyon sayfanın ilk yüklendiği anda çalıştığı fonksiyondur.

2.Fonksiyon ise postback işleminde çağırılan fonksiyondur.

İkisininde yaptığı işlem hemen hemen aynıdır.(Kodlara baktığınızda göreceksiniz.).Aralarındaki fark ise ilk yüklenen nesne ile sonradan yüklenen nesne arasındaki değişimleri bulup update etmektir.Bunuda webservis classında görebilirsiniz..

[WebMethod(EnableSession = true)]

public GoogleObject GetGoogleObject()

{

GoogleObject objGoogle = (GoogleObject)System.Web.HttpContext.Current.Session["GOOGLE_MAP_OBJECT"];

System.Web.HttpContext.Current.Session["GOOGLE_MAP_OBJECT_OLD"] = new GoogleObject(objGoogle);

return objGoogle;

}

Bu kod webservis içindeki kodtur.Session’dan GOOGLE_MAP_OBJECT’i çekiyor.Haydaa bu session nerden geldi şimdi dediğinizi der gibiyim :)

Bu session ise GoogleMapControl.ascx’in kod bölümüne gittinizde sayfanın page_load event’ında ki koda dikkatlice bakın:

if (!IsPostBack)

{

Session["GOOGLE_MAP_OBJECT"] = GoogleMapObject;

}

else

{

GoogleMapObject = (GoogleObject)Session["GOOGLE_MAP_OBJECT"];

}

Aynı sayfa içinde

#region Properties

GoogleObject _googlemapobject = new GoogleObject();

public GoogleObject GoogleMapObject

{

get { return _googlemapobject; }

set { _googlemapobject = value; }

}

#endregion

ile var olan GoogleMaps class’na ulaşılıyor.

GoogleMaps classından fazla bahsetmek istemiyorum.Zaten bildiğimiz c# kodları ile yazılmış.

Şimdiye kadar anlattıklarımı toparlarsam nasıl bir yol izlemişiz bakalım:

JavasctiptàWebServisàSessionàUygulama Class’ı

GoogleMaps ile ilgili bütün property’ler(özellikler) bir class yardımıyla alınıyor.Bu class session’a yükleniyor.Session’dan web servis aracılığıyla javascript’ten çağrılıyor.

Örneği incelerken kendime soruyorum.Bu adam postback ile ilk load’u birbirinden nasıl ayırmış yaw.? GoogleMapsControl sayfasında şu javascript kodları gözüme çarpıyor.

function endRequestHandler(sender, args)

{

GService.GetOptimizedGoogleObject(fGetGoogleObjectOptimized);

}

function pageLoad()

{

if(!Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack())

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequestHandler);

}

Artık bundan sonra user control’ü sayfanıza sürükleyip kodları yazmaya başlayabilirsiniz.