Bugün daha önce yazmış olduğum Setup & ConnectionString yazısına ek yapacağız. Söz konusu ek; mdfDataFile ve ldfLogFile dosyalarını VeritabanıDatabase sunucusuna eklemekAttach.
İlk öğrenmemiz gereken bu işi yapan DDL kodları; (şu yazıda bunu nasıl yapabileceğiniz görebilirsiniz)
CREATE DATABASE [ornek] ON (FILENAME = N'E:\DataBases\ornek.mdf'), (FILENAME = N'E:\DataBases\ornek_log.ldf') FOR ATTACH
Daha sonra bu kodları kurulumsetup sırasında (OnAfterInstall) Ado.Net komutları ile çalıştırmakExecute etmek.
//DDL kodlarını oluşturalım StringBuilder sb = new StringBuilder(); sb.Append("CREATE DATABASE [Ornek] ON\r\n"); sb.AppendFormat("(FILENAME = N'{0}'),\r\n", thisAss.Location.Replace( thisAss.GetName().Name + ".exe", "ornek.mdf") ); sb.AppendFormat("(FILENAME = N'{0}')\r\n", thisAss.Location.Replace( thisAss.GetName().Name + ".exe", "ornek_log.ldf") ); sb.Append("FOR ATTACH\r\n"); //oluşan DDL kodlarını ADO.NET ile çalıştıralım using (SqlConnection con = new SqlConnection(value.Replace("ornek", "Master"))) { using (SqlCommand com = new SqlCommand( sb.ToString(), con)) { con.Open(); com.ExecuteNonQuery(); } }
Geçenlerde bir takım verileridata WebForm' unda görüntülememView ve filtrelememFilter|Search gerekti (zaman da dar bu arada).
Bakın ne yaptım;
<asp:QueryExtender ID="QueryExtender1" runat="server" TargetControlID="EntityDataSource1"> <asp:SearchExpression DataFields="ProductName" SearchType="StartsWith"> <asp:ControlParameter ControlID="productname" /> </asp:SearchExpression> </asp:QueryExtender>
protected void Button1_Click(object sender, EventArgs e) { QueryExtender1.DataBind(); }
Alternatif olarak; TextBox kontrolünün Autopostback özelliğini True olarak ayarlanırsa Button kontrolüne ihtiyaç kalmadan da filtrelemeyi tetiklemek mümkün olur.
Biraz açıklama: QueryExtender, Framework 4.0 bileşenidir. LINQ ifadeleri için geliştirilmiş Expressionlaraİfade sahiptir. Örneğimizde SearchExpression kullandık. Nasıl kullandık? DataFields özelliği ile filtre uygulanacak alanı belirledik.(Daha fazla alan virgül ile belirtilebilir) Bu özellik klasik Sql sorgusunda where ifadesinden sonra kullanılan fieldAlan tanımlaması gibi düşünebilir. SearchType özelliği ise like ifadesinin karşılığıdır. StartsWith 'parametre%', EndsWith '%parametre' ve Contains '%parametre%' gibi çalışır. Son olarak filtreleme için kullanılacak parametre kaynağını belirtmek. Bu bir kontrol olabileceği gibi session, cookie gidi kaynaklar da olabilir.
Projeyi indirmek için tıklayın.
Repeater ile verileri listemek yada sıralamaksort mümkün. Peki sıralamasını manuel nasıl yaparız?
Bunu gerçekleştirmek için taşıma yapmak istediğimiz tablomuza RowNumber adlı bir kolonfield ekliyoruz. Bu kolondaki değer verinin sıra numarasını temsil edecek. Taşıma işlemi ise bu numaraları değiştirmekten ibaret olacaktır.
Repeater içine sıralama olayını gerçekleştirmek için iki adet ImageButton kontrolü ekliyoruz. CommandArgument özelliğineproperty taşınacak satırın Id değerini atıyoruzset. CommandName özelliğine atanan up ve down değerleri ile de taşıma yönünü bildiriyoruz.
<td style="width: 24px; text-align: center;"> <asp:ImageButton runat="server" ID="moveup" ImageUrl="~/Images/up.png" ToolTip="Yukarı Taşı" CommandArgument='<%# Eval("Id")%>' CommandName="up" /> </td> <td style="width: 24px; text-align: center;"> <asp:ImageButton runat="server" ID="movedown" ImageUrl="~/Images/down.png" ToolTip="Aşağı Taşı" CommandArgument='<%# Eval("Id")%>' CommandName="down" /> </td>
Repeater kontrolünün ItemCommand olayıevent na aşağıdaki kodları yazarak taşıma işlemini gerçekleştiriyoruz.
using (NorthwindDataContext db = new NorthwindDataContext()) { //taşınacak verinin Id bilgisi alınıyor Guid Id = new Guid(e.CommandArgument.ToString()); //taşınacak veri OrderedProduct moveproduct = db. OrderedProducts. FirstOrDefault(p => p.Id == Id); //yeni numara hesapla int RowNumber = e.CommandName == "up" ? moveproduct.RowNumber - 1 : //yukarı ise moveproduct.RowNumber + 1; //aşağı ise //yeri değişecek veri OrderedProduct targetproduct = db. OrderedProducts. FirstOrDefault(p => p.RowNumber == RowNumber); if (e.CommandName == "up") { //yukarı taşı moveproduct.RowNumber -= 1; targetproduct.RowNumber += 1; } else if (e.CommandName == "down") { //aşağo taşı moveproduct.RowNumber += 1; targetproduct.RowNumber -= 1; } //değişiklikleri yap db.SubmitChanges(); } //tekrar listele BindData();
Daha sonra ise, ilk satırdaki Yukarı Ok u ve en alt satırdaki Aşağı Ok ImageButton'larını basit bir script kodu ile kaldırıyoruz. Bu kod sayesinde sunucuserver tarafındaki ilk kayıt mı son kayıt mı kontrolünü yapmaktan yırtıyoruz :)
<script src="Scripts/jquery-1.4.1-vsdoc.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $("table tbody tr:first td:eq(3)").empty(); $("table tbody tr:last td:eq(4)").empty(); }); </script>
Repeater kontrolü renderHtml koduna dönüşmek edildiğinde aşağıdaki görüntü oluşacaktır. Kullanıcı yukarı aşağı ok tuşları ile satırları manuel olarak sıralayabilecek.
Projeyi indir.
Excel yada benzeri bir kaynaktan elde ettiğimiz yaklaşık bir milyon kayıt içeren bir DataTable olduğunu düşünelim. Bu kayıtları veritabanında ki x bir tabloya nasıl eklerizinsert?
Tek tek Insert yapmak mı? Bildiğin intihar olur. DataAdapter ile UpdateGüncelleme yapmak mı? İnanılmaz zahmetli ve gereksiz.(Çalışacağından emin bile değilim)
Biri Bulk Insert mü dedi, işte aradığımız cevap. Bulk Insert, yüklü miktarda ki verileri herhangi bir tablotableya performanslı bir şekilde eklemek için veri katmanında kullanabileceğimiz bir komuttur. Ado.Net teknolojisi SqlBulkCopy (kısaca sbc diyelim) nesnesi ile bu komutu kolaylıkla kullanmamıza olanak verir. Tabi kullanımı bu kadar kısıtlamamak gerekir. Sbc ile iki farklı sunucu yada aynı sunucu içinde iki farklı tablo da kopyalanabilir.
Bir örnek ile açıklayalım; Excel dosyasındaki verileri DataTable nesnesine dolduracağız. Peşinde sbc ile DataTable içindeki verileri örnek bir tabloya ekleyeceğiz.
Örnek Excel dosyası
Hedef tablo
Excel dosyasındaki alanlar ile tablo alanfieldlarını bilerek farklı isimlendirdim. Sbc nesnesinin bir özelliği de bu gibi durumlarda alanları adresleyebilmesidirmapping.
//Excel -> DataTable string SourceConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\data.xls;Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=1\";"; DataTable SourceDT = null; using (OleDbConnection SourceConn = new OleDbConnection(SourceConnStr)) { using (OleDbDataAdapter SourceDAP = new OleDbDataAdapter( "select * from [Contact]", SourceConn)) { SourceDT = new DataTable("Contact"); SourceDAP.Fill(SourceDT); } } //DataTable -> Sql using (SqlConnection DestinationConn = new SqlConnection( "Data Source=.;Initial Catalog=Ordek;Integrated Security=True")) { DestinationConn.Open(); using (SqlBulkCopy SBC = new SqlBulkCopy(DestinationConn)) { //Verilerin kopyalanacağı Tablo Adı SBC.DestinationTableName = "Kisiler"; //Kaynak ile hedef alanlarının adları farklı ise //alanları adreslemek gerekir SBC.ColumnMappings.Add("ContactID", "ID"); SBC.ColumnMappings.Add("FirstName", "Ad"); SBC.ColumnMappings.Add("LastName", "Soyad"); SBC.ColumnMappings.Add("EmailAddress", "Email"); SBC.ColumnMappings.Add("Phone", "Telefon"); //son olarak WriteToServer metotu ile kaynak hedefe kopyalanır SBC.WriteToServer(SourceDT); //DataTable dışında DataRow koleksiyonu yada DataReader nesnesi de //parametre olarak verilebilir. } }
Örnek proje.
Repeater, veri kontrolleri içinde en sevdiğimdir. Onu esnek hale getiren basit yapısı da ben en cezbeden özelliğidir. Basit olduğundan dolayı görevi sadece veri göstermek. Peki sıralama yapmak istersek ne yapmalıyız? Aslında cevap basit; HeaderTemplate içine LinkButton kontrolü yerleştirip, sıralama bilgilerini de ViewState içinde tutacağız.
Burada tablotable başlıklarına birer LinkButton ve Image kontrolü koyuyoruz. LinkButton sıralama komutunu işlerken, Image kontrolleri de sıralama yapılan satırları işaret edecek.
<HeaderTemplate> <table> <tr class="head"> <th style="width: 65%"> <asp:LinkButton ID="h1" runat="server" Text="Product Name" CommandName="name" CommandArgument="ProductName" /> <asp:Image ID="ProductName" runat="server" ImageAlign="AbsMiddle" /> </th> <th style="width: 20%"> <asp:LinkButton ID="h2" runat="server" Text="Unit Price" CommandName="price" CommandArgument="UnitPrice" /> <asp:Image ID="UnitPrice" runat="server" ImageAlign="AbsMiddle" /> </th> <th style="width: 15%"> <asp:LinkButton ID="h3" runat="server" Text="Unit Stok" CommandName="stock" CommandArgument="UnitsInStock" /> <asp:Image ID="UnitsInStock" runat="server" ImageAlign="AbsMiddle" /> </th> </tr> </HeaderTemplate>
Sıralama sırasından iki adet bilgiye ihtiyaç var. Sıralanacak veri kolonu ve sıralama yönü. Bu bilgileri ViewState içinde saklıyoruz. Kullanım kolaylığı olması için Propertyözellik olarak tanımlıyoruz.
public string SortOrder { get { if (ViewState["SortOrder"] == null) ViewState["SortOrder"] = "Asc"; return ViewState["SortOrder"].ToString(); } set { ViewState["SortOrder"] = value; } } public string SortColumn { get { if (ViewState["SortColumn"] == null) ViewState["SortColumn"] = "ProductName"; return ViewState["SortColumn"].ToString(); } set { ViewState["SortColumn"] = value; } }
Sıralama komutu için yazılacak Ado.Net kodları.
"Kolon adı parametre olmadığı için String nesnedinin Format metotunu kullandım."
void GetData() { SqlConnection con = new SqlConnection( "Data Source=.;Initial Catalog=Northwind;Integrated Security=True"); string query = string.Format( "Select ProductName,UnitPrice,UnitsInStock From Products Order by {0} {1}", SortColumn, SortOrder); SqlCommand com = new SqlCommand( query, con); con.Open(); rpt1.DataSource = com.ExecuteReader(); rpt1.DataBind(); con.Close(); }
Uygulama sonunda elde edeceğimiz veri listesi.
Kalan detaylara projeyi indirip bakabilirsiniz.
Öyle bir tablotable olsun ki sınırsız kategori ve alt kategori ekleyebileyim. Neden bahsettiğimi anladınız sanırım :) Halk arasında Sınırsız Ağaç Yapısı denilen tablo çeşidi.
Örnek tablo:
Yapı şöyle olacak; Kategori, hiyerarşinin en üstünde ise alt kategori değeri 0sıfır değilse üst kategorinin id değeri yer alacak. Örneğin; Kitap, kendi kategorisinde en tepede yani alt kategori değeri 0. Dünya Klasikleri ise Kitap kategorisinin altında olduğu için alt kategori değeri Kitap kategorisinin CategoryID değeri olacak yani 1 gibi ...
Azıcık kod yazalım. Kategori tablosunu DataTable nesnesine doldurarak başlayalım. Bu hamle işimizi bi hayli kolaylaştıracaktır. Daha sonra recursiveyinenelen bir metot yardımıyla kategori ve alt kategori bitinceye kadar dönelim. Son olarak ta elde ettiğimiz değerleri Treeview controlülüne nodedüğüm olakarak ekleyelim.
"Eğer karışık geldi ise biraz recursive çalışın."
DataTable dt = new DataTable(); protected void Page_Load(object sender, EventArgs e) { SqlConnection con = new SqlConnection( "Data Source=.;Initial Catalog=Ordek;Integrated Security=True"); SqlDataAdapter dap = new SqlDataAdapter( "select * from Category order by SubCategoryID", con); dap.Fill(dt); populateData(TreeView1.Nodes[0], getrows("0")); } DataRowCollection getrows(string id) { DataView dw = new DataView(dt); dw.RowFilter = "SubCategoryID=" + id; return dw.ToTable().Rows; } void populateData(TreeNode node, DataRowCollection col) { foreach (DataRow row in col) { TreeNode n = new TreeNode(row[1].ToString()); node.ChildNodes.Add(n); populateData(n, getrows(row[0].ToString())); } }
Sonuç;
Ben veri kaynağı olarak Xml kullanması severim de herkes sevmek yada bilmek zorunda değil. Xml ile yaptığım anket uygulamasını Sql ve Ado.Net kullanarak tekrar yaptım.
Veritabanı şeması aynen aşağıdaki gibi;
Uygulamada kullanılan nesneclass diyagramı;
Uygulamada kullanılan örnek kod;
public void OyVer(int AnketSoruID) { using (SqlConnection con = new SqlConnection( "Data Source=.;Initial Catalog=calismalar;Integrated Security=True;")) { using (SqlCommand com = new SqlCommand( "update AnketSorular set AnketOy += 1 where AnketSoruID = @AnketSoruID", con)) { com.Parameters.AddWithValue("@AnketSoruID", AnketSoruID); con.Open(); com.ExecuteNonQuery(); } } }
Uygulama kaynak kodları da burada. Sql script ise burada.
Northwind sql eğitimlerinde sıkça kullandığım (basit yapısınsan dolayı) bir veri tabanı örneğidir. Aynı zamanda eskidir de :) En sık başvuru yaptığım tablolardan biri de Categories tablosudur. Bu tabloda kategorileri resimleri ile yer almaktadır. Yer almasına alıyor da, alırken ilk 78 ve son 78 byte kısmında ole nesne bilgisi olduğu için kimi zaman sorunlar yaşanıyor. Her ne kadar DataGridView kontrolü bunu otomatik gösterse de, manuel olarak PictureBox gibi bir kontrolde göstermek istersek şu şekilde kullanmak gerekiyor.
private void Form1_Load(object sender, EventArgs e) { foreach (Category cat in CategoryProcess.GetCategories()) listBox1.Items.Add(cat); } private void button1_Click(object sender, EventArgs e) { Category cat = (Category)listBox1.SelectedItem; byte[] resim = CategoryProcess.GetPicture(cat.CategoryID); MemoryStream mem = new MemoryStream(); mem.Write(resim, 78, resim.Length - 78); pictureBox1.Image = Image.FromStream(mem); for (int i = 0; i < 78; i++) textBox1.Text += ((char)resim[i]).ToString(); }
Daha önceleri MARS (Multiple Active Result Sets) uygulamalarımı Treeview yada Listbox kontrollerini kullanarak yapıyordum. Listview içinde sıralama ile uğraşırken bir örnekte bu kontrolle yapayım dedim.
//görünüm ayarlaniyor listView1.View = View.Details; listView1.FullRowSelect = true; //3 adet kolon ekleniyor listView1.Columns.Add("Product Name", 300); listView1.Columns.Add("Unit Price", 150); listView1.Columns.Add("Units In Stock", 100);
//Northwind veritabanından kategoriler ve Ürünlerler çekiliyor SqlConnection con = new SqlConnection("Data Source=. ;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True;"); SqlCommand comCat = new SqlCommand("Select CategcrylD,CategoryName from Categories order by CategoryName", con); SqlCommand comPro = new SqlCommand("Select ProductName,UnitPrice,UnitslnStock from Products where CategorylD = @id", con); comPro.Parameters.Add("@id", SqlDbType.Int); SqlDataReader rdPro = null; con.Open(); SqlDataReader rdCat = comCat.ExecuteReader();
//verileri ekleniyor while (rdCat.Read()) { //Kategori adı grup olarak ekleniyor ListViewGroup group = new ListViewGroup(); group.Header = rdCat.GetString(1); listView1.Groups.Add(group); comPro.Parameters[0].Value = rdCat[0]; rdPro = comPro.ExecuteReader(); int rowlndex = 0; while (rdPro.Read()) { //ürünler ekleniyor ListViewItem item = new ListViewItem(); item.Text = rdPro.GetString(0); item.SubItems.Add(rdPro[1].ToString()); item.SubItems.Add(rdPro[2].ToString()); //ürünün ait olduğu kategori seçiliyor item.Group = group; //alternatif görünüm için arkaplan rengi mod ile ayarlanıyor item.BackColor = (rowlndex++ % 2) == 0 ? Color.Beige : Color.Wheat; listView1.Items.Add(item); } rdPro.Close(); } rdCat.Close(); con.Close();
Şöyle bir düşündüğümüzde veritabınında bulunan herhangi bir tablonun verilerini Xml formatında dosyaya yazmanın kaç yolu vardır acaba? Sabahları genelde işe erken gelirim, 1.5 saat kadar :). Oturdum bir kaç örnek yaptım, paylaşıyorum tüm dünya ile. (Özgüvenin tavan yaptığı an) Aklıma gelen ilk yöntem DataTable nesnesini kullanmak. Bu nesneyi sadece yazmak için kullanmak kaynak tüketimi açısından çok iyi bir seçim olmaz fakat kullanım kolaylığı açısında tercih edilebilir.
Diğer bir yöntem ise henüz Veri KatmanındaDataLayer iken oracıkta Xml formatına dönüştürüp XmlTextWriter ile cayır cayır yazmak.
Son yöntem ise daha önce şu yazıda kullandığım teknik ile (attributenitelik yerine bu sefer nodedüğüm kullandım) XDocument ile manuel olarak Xml dosyasını oluşturmak. Sanırım en zoru bu. Yanlış anlaşılmasın benim için kolay da hani ilk yapan için diyorum :)
Uzun zamandır Strong Type DataSet kullanmadığımdan basit bir uygulama yazmak belki bir kaç yenilik varsa da görmek istedim. Dataset içine basit birkaç veri girebileceğim DataTable ekledim. Ms Sql Server ile uğraşmamak için veri kaynağı olarak Xml' i uygun gördüm. Daha sonra aklıma keşke içi dolu bir Xml dosyası olasa da test işlemlerini hemen yapsam. Visual Studio 2008 kullanıyorsanız eğer Xml Schema Explorer penceresini bilirsiniz. O pencerede DataTable' a sağ tıklarsanız tam aradığım şey ile karşılaşırsınız. Generate Sample XML Sonuç;
CloseConnection ne işe yarar neden kullanılır? Command nesnesinin executeReader metotu ile kullanılan enumnumaralandırıcı olarakta bilinir. Varsayılan değeri Default fakat ihtiyaç gereği 6 adet daha kullanılan değeri mevcuttur ki CloseConnection bunlardan biridir. Command nesnesine bağlı olan Connection nesnesi DataReader nesnesi kapatılana kadar açık kalır. Neden kalsın ki? Örnek bir kullanım ile açıklayalım; Kazancımıza bakalım; - DataReader ile okumanın DataAdapter ile okumaktan daha hızlı olduğunu herkes bilir. + 1 (Connection nesnesi kapansaydı DataReader nesnesi null olarak gelecekti. Açık bırakırsak ayrı bir dert) - SqlCommand nesnesini Program nesnesine çekersekte uygulama System.Data.SqlClient' a bağımlı kalacaktı bu da iyi bir tasarım olmayacaktı. + 2
Bildiğiniz gibi BindingSource nesnesi .Net Framework 2.0 versiyonu ile hayatımıza girdi. Girmesiyle Data nesneleri arasında ilişki kurmak daha da kolay oldu.
Şöyle ki;
Öğrencilerimin en sık sorduğu sorulardan biri de, veritabanında depolanmış resimleri web sayfasında nasıl gösterildiğidir. Internette bu konu hakkında zilyon tane yazı olmasına karşın bir tane yazma gereği hissettim.
Öncelikle anlaşılması gereken konu, html sayfalarında resim göstermenin ilk koşulu resmin fiziksel yolunun kontrole yada etikettage aktarılmasıdır. Örneğin;
Diğer taraftan baktığımızda ki bu taraf veritabanı tarafıdır, resim gibi içeriklerin hangi tip veri alanında saklanacağıdır. C# ile yazılım geliştiryorsanız resim yada bir dosya serileştiğinde (yani veritabına yazıldığında) byte dizisinden (byte[]) ibaret olacaktır. Bu durumda en uygun alan tipi varbinarybyte[] olsa iyi olur. :)
Kaçınılması gereken davranışlar.
yada
hiç olmadı :)
Peki yapılması gereken nedir? Veritabınından okunan byte dizisini resim formatında dışarı aktaracak (response) bir dosya (aspx,ashx) oluşturmak ve oluşan dosyanın yolunu resmi gösterecek kontrol yada etikete atamaktır.
Örnek tabloda gösterildiği gibi resim UrunResim alanında varbinarybyte[] olarak saklanıyor.
Resmi oluşturacak resim.aspx sayfasına gönderilen parametre değeri ile veritabanında basit bir sorgu yapılarak ve sonucu byte dizisi olarak elde edilir.
Son olarak ise sayfa çıktısının resim olacağını işaret eder ve çıktıyı BinaryWriter metotu ile oluştururuz.