https://www.buraksenyurt.com/Burak Selim Şenyurt - Asp.Net2016-08-08T07:27:38+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://www.buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://www.buraksenyurt.com/post/TFI-109-IIS-Uzerindeki-Uygulamalarc4b1-Kod-Yoluyla-OgrenmekTFİ 109 - IIS Üzerindeki Uygulamaları Kod Yoluyla Öğrenmek2014-08-27T13:52:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p>
<p>Diyelim ki sunucudaki <strong>IIS </strong>üzerinde konuşlandırdığınız Web uygulamalarının bir listesini almak istiyorsunuz. Bunun elbette pek çok yolu olduğunu biliyorsunuz. Bir Powershell script' i belki de işinizi görür. Ancak belki de siz bunu kendi geliştireceğiniz windows forms uygulamasında bu listeyi kullanmak istiyorsunuz. Ne yaparsınız? Kod yardımıyla <strong>IIS </strong>üzerindeki <strong>Application</strong>' ları, <strong>Site</strong>' ları öğrenebilir misiniz?</p>
<p>Aslında hep elinizin altında olan<em> (Windows\System32\inetsrv\Microsoft.Web.Administration.dll)</em> ve hatta isterseniz <strong>NuGet Package Manager </strong>ile de indirebileceğiniz <strong>Microsoft.Web.Administration</strong> kütüphanesini kullanarak bu işi gerçekleştirmeniz oldukça kolay. Nasıl mı? İşte böyle.</p>
<p><img src="http://www.buraksenyurt.com/pics/2014%2f8%2ftfi109.png" alt="" /></p>
<p>Başka neler mi yapabilirsiniz? Örneğin bir Application Pool' u Recylce edebilirsiniz. Ya da bir Web Site' ı Stop-Start. Hatta yeni bir Web Site bile açabilirsiniz. Araştırmaya değer değil mi?</p>
<p>Başka bir ipucunda görüşmek dileğiyle.</p>2014-08-27T13:52:00+00:00bsenyurthttps://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=b0a0f719-7be3-43f8-96a5-6635b286bc471https://www.buraksenyurt.com/trackback.axd?id=b0a0f719-7be3-43f8-96a5-6635b286bc47https://www.buraksenyurt.com/post/TFI-109-IIS-Uzerindeki-Uygulamalarc4b1-Kod-Yoluyla-Ogrenmek#commenthttps://www.buraksenyurt.com/syndication.axd?post=b0a0f719-7be3-43f8-96a5-6635b286bc47https://www.buraksenyurt.com/post/AspNete28093Custom-Configuration-Section-GelistirmekAsp.Net–Custom Configuration Section Geliştirmek2014-05-19T16:50:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/Configuration.png"><img style="float: right; margin: 4px 0px; display: inline;" title="Configuration" src="/pics/Configuration_thumb.png" alt="Configuration" width="200" height="197" align="right" /></a>Merhaba Arkadaşlar,</p>
<p>Konfigurasyon tabanlı geliştirme modeli, uygulama kodlarına girilmeden çalışma zamanına yönelik değişiklikler yapabilmemizi sağlar. Bu sayede pek çok programın kodsal müdahale yapmadan davranışları değiştirilebilir. .<strong>Net</strong> dünyasında baktığımızda da, <strong>App.Config</strong>, <strong>Web.config</strong> gibi dosyalar içerisinde <strong>Framework’</strong> ün geneline yönelik pek çok konfigurasyon ayarı bulunduğu görülür. <strong>appSettings</strong>, <strong>connectionStrings</strong>, <strong>httpHandler</strong> vb…</p>
<blockquote>
<p>Eskilerden : <a href="https://www.buraksenyurt.com/post/Asp-Net-2-0-ile-Configuration-Management-(Konfigurasyon-Yonetimi)-bsenyurt-com-dan" target="_blank">Asp.Net 2.0 ile Configuration Management (Konfigurasyon Yönetimi)</a></p>
</blockquote>
<p>Söz konusu konfigurasyon içerikleri aslında <strong>XML</strong> tabanlı bir dosya şemasının parçalarıdır ve doğal olarak <strong>element</strong> ile <strong>attribute</strong>’ lardan oluşmaktadır. Konfigurasyon dosyalarının daha iyi yönetilebilmesi için <strong>Asp.Net 2.0</strong> ile birlikte <strong>Configuration API</strong> alt yapısı geliştirilmiştir. Bu kütüphane sayesinde konfigurasyon içerisindeki <strong>elementlere</strong> <strong>sınıf</strong> <strong>bazında</strong> erişmek ve yönetebilmek mümkündür. Pek tabi <strong>XML</strong> elementlerinin sahip oldukları <strong>nitelikler,</strong> sınıfların <strong>özellikleri<em>(Property)</em></strong> olarak ele alınmaktadır.</p>
<p>Peki konfigurasyon dosyası içerisine kendi özel kısımlarımızı<em>(section) </em>ilave etmek ve hatta bunları çalışma zamanında<em>(Runtime)</em> kullanmak istersek, nasıl bir yol izlememiz gerekir? <img class="wlEmoticon wlEmoticon-thinkingsmile" style="border-style: none;" src="/pics/wlEmoticon-thinkingsmile_3.png" alt="Thinking smile" /> </p>
<h1>Konfigurasyon Yapısı</h1>
<p>Aklımıza ilk gelen belirli tip türetmeleri veya arayüz<em>(interface)</em> uyarlamaları ile bu işin halledilebilecek olmasıdır. Aslında olayı çözümlemek için var olan konfigurasyon parçalarının örnek tip yapısını incelemek yerinde bir hareket olacaktır. Söz gelimi <strong>system.web</strong> kısımı içerisindeki <strong>compilation</strong> ve <strong>pages</strong> elementlerini incelediğimizi düşünelim.</p>
<p><a href="https://www.buraksenyurt.com/pics/ccs_1.png"><img style="margin: 4px 0px; display: inline;" title="ccs_1" src="/pics/ccs_1_thumb.png" alt="ccs_1" width="615" height="591" /></a></p>
<p>Eğer <strong>object</strong> <strong>browser</strong> üzerinden ilgili elementlerin karşılık geldiği sınıfları incelersek şu sonuçlara varırız.</p>
<ul>
<li><strong>system.web</strong> elementi <strong>SystemWebSectionGroup</strong> isimli bir sınıf ile işaret edilmekte olup <strong>ConfigurationSectionGroup</strong> türevlidir.</li>
<li><strong>compilation</strong> elementi <strong>system.web</strong> in alt elementidir ve <strong>CompilationSection</strong> sınıfı ile işaret edilmektedir. Bu sınıf ise <strong>ConfigurationSection’</strong> dan türemiştir.</li>
<li>Benzer şekilde <strong>pages</strong> elementi de <strong>ConfigurationSection</strong> türevli <strong>PageSection</strong> sınıfı ile temsil edilmektedir.</li>
<li><strong>namespaces</strong> elementine baktığımızda aynı tipten birden fazla elementi içerecek şekilde kullanılabildiği görülmektedir. Bunun için <strong>NamespaceColletion</strong> sınıfı <strong>ConfigurationElementCollection</strong> türevli olarak tasarlanmıştır.</li>
<li><strong>namespaces</strong> segmenti içerisinde yer alan <strong>add</strong> elementleri aslında <strong>NamespaceInfo</strong> tipini işaret etmekte olup yine <strong>ConfigurationElement</strong> türevlidir.</li>
<li>alt elementler genellikle üst elementlerin birer özelliği<em>(sınıf bazında düşünüldüğünde)</em> olarak karşımıza çıkmaktadır.</li>
</ul>
<p>Dolayısıyla kendi geliştireceğimiz özel Section elementleri için de bu tip bir yol izlememiz ve ilgili türetmeleri yapmamız yeterli olacaktır.</p>
<h1>Örnek Senaryo</h1>
<p>Basit bir senaryo üzerinden ilerleyebiliriz. Örneğin bir web uygulamasının <strong>web.config</strong> dosyası içerisinde aşağıdaki gibi bir konfigurasyon kısmı oluşturmak istediğimizi düşünelim.</p>
<p><a href="https://www.buraksenyurt.com/pics/ccs_3.png"><img style="margin: 4px 0px; display: inline;" title="ccs_3" src="/pics/ccs_3_thumb.png" alt="ccs_3" width="477" height="246" /></a></p>
<p><strong>serviceConnection</strong> ve altında yer alan <strong>definition</strong> elementlerinin işlevselliği çok önemli değildir aslında. Sadece bir kaç nitelik ve alt element içeren bir XML yapısı söz konusu. Bizim yapacağımız basit olarak bu konfigurasyon içeriğini türlendirmek ve çalışma zamanında yönetebilir hale getirmek. Öyleyse işe koyulalım ne duruyoruz.</p>
<h1>Sınıfların İnşa Edilmesi</h1>
<p>Boş bir Web uygulamasında aşağıdaki sınıf çizelgesinde yer alan tipleri ürettiğimizi düşünelim.</p>
<p><a href="https://www.buraksenyurt.com/pics/ccs_2.png"><img style="margin: 4px 0px; display: inline;" title="ccs_2" src="/pics/ccs_2_thumb.png" alt="ccs_2" width="473" height="367" /></a></p>
<p><strong>ServiceConnectionSection</strong> tipi dikkat edileceği üzere <strong>ConfigurationSection</strong> türevlidir ve içerisinde <strong>Type</strong> isimli <strong>ServiceType</strong> <strong>enum</strong> sabiti tipinden bir özellik ile <strong>ConfigurationElement</strong> türevli olan <strong>DefinitionSection</strong> tipinden başka bir özellik yer almaktadır.</p>
<p><strong>ServiceConnectionSection sınıfı;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Configuration;
namespace HowTo_WritingCustomConfigSection
{
public class ServiceConnectionSection
:ConfigurationSection
{
// Save işlemine izin vermesi için false döndürecek şekilde ezdik
public override bool IsReadOnly()
{
return false;
}
[ConfigurationProperty("type", DefaultValue = ServiceType.WCF, IsRequired = false)]
public ServiceType Type
{
get
{
return (ServiceType)Enum.Parse(typeof(ServiceType), this["type"].ToString());
}
set
{
this["type"] = value.ToString();
}
}
[ConfigurationProperty("definition",IsRequired=true)]
public DefinitionSection Definition
{
get
{
return (DefinitionSection)this["definition"];
}
set
{
this["definition"]=value;
}
}
}
}</pre>
<blockquote>
<p>Konfigurasyon içeriğinde çalışma zamanında da değişiklik yapılması mümkündür ama konfigurasyon yöneticisinin Save metoduna tepki verebilmesi için IsReadOnly özelliğinin ezilmesi(override) ve false döndürmesi gerekmektedir.</p>
</blockquote>
<p>Dikkat edileceği üzere <strong>Definition</strong> ve <strong>Type</strong> isimli özelliklere <strong>ConfigurationProperty</strong> niteliği<em>(Attribute)</em> uygulanmıştır. Bu niteliğe ait özelliklerden yararlanılarak element adı<em>(konfigurasyon dosyasında görünecek olan isim)</em> ve gereklilik<em>(IsRequired) </em>gibi değerler belirtilebilir.</p>
<p>Özelliklerin <strong>get</strong> ve <strong>set</strong> bloklarında fark edileceği gibi <strong>this</strong> anahtar kelimesinden yararlanılmakta ve üst tipin <strong>indeksleyicisine(Indexer)</strong> gidilerek değer ataması veya okunması işlemi gerçekleştirilmektedir.</p>
<blockquote>
<p>F12 ile ConfigurationSection elementine gidiliğinde bu indexleyici görülebilir. <br /><a href="https://www.buraksenyurt.com/pics/ccs_6.png"> <br /><img style="margin: 4px 0px; display: inline;" title="ccs_6" src="/pics/ccs_6_thumb.png" alt="ccs_6" width="616" height="319" /></a></p>
</blockquote>
<p><strong>DefinitionSection sınıfı;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Configuration;
namespace HowTo_WritingCustomConfigSection
{
public class DefinitionSection
:ConfigurationElement
{
// Save işlemine izin vermesi için false döndürecek şekilde ezdik
public override bool IsReadOnly()
{
return false;
}
[ConfigurationProperty("name", IsRequired = true)]
[StringValidator(InvalidCharacters = "~!@#$%^&*()[]{}/;'\"|\\,çşöğüı")]
public string Name
{
get
{
return this["name"].ToString();
}
set
{
this["name"] = value;
}
}
[ConfigurationProperty("wsdlAddress", IsRequired = true)]
public string WsdlAddress
{
get
{
return this["wsdlAddress"].ToString();
}
set
{
this["wsdlAddress"] = value;
}
}
}
}</pre>
<p><strong>DefinitionSection</strong> sınıfı aslında bir alt elementtir ve bu sebepten <strong>ConfigurationElement </strong>sınıfından türetilmiştir. <strong>Save</strong> operasyonuna cevap verebilmesi için <strong>IsReadOnly</strong> özelliği ezilmiştir. <strong>Name</strong> özelliğinde <strong>ConfigurationProperty</strong> dışında <strong>StringValidator</strong> niteliği de kullanılmış ve kullanılması istenmeyen bir karakter seti belirtilmiştir.</p>
<h1>Web.config Bildirimleri</h1>
<p>Artık konfigurasyon dosyası içerisinde kullanacağımız <strong>serviceConnection</strong> section için gerekli tip desteğine sahip bulunmaktayız. Peki <strong>web.config</strong> dosyası içerisinde bu bildirimleri nasıl gerçekleştirebiliriz?</p>
<p>Bazen 3ncü parti araçları sisteme dahil ettiğimizde, konfigurasyon dosyası içerisine koyacakları elementler için ekstra bildirimler eklediklerine şahit olmuşuzdur. Yani bir şekilde çalışma zamanına, <strong>“izleyen config içeriğinde şu tipe ait elementler kullanılabilir”</strong> denilebilmelidir. Bunun için <strong>configSections</strong> elementi içerisinde <strong>sectionGroup</strong> ve <strong>section</strong> tanımlamalarını yapmamız yeterli olacaktır. Aşağıda görüldüğü gibi.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="serviceConnectionGroup">
<section
name="serviceConnection"
type="HowTo_WritingCustomConfigSection.ServiceConnectionSection"
allowLocation="true"
allowDefinition="Everywhere"
/>
</sectionGroup>
</configSections>
<serviceConnectionGroup>
<serviceConnection type="MSMQ">
<definition
name="Some MSMQ Service"
wsdlAddress="msmq://www.azon.com/someservicequeue/inbox" />
</serviceConnection>
</serviceConnectionGroup>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
</configuration></pre>
<p>Kural son derece basittir. <strong>serviceConnectionGroup</strong>, <strong>serviceConnection</strong> ve <strong>definition</strong> elementlerinin kullanılabilmesi için bir <strong>sectionGroup</strong> tanımlaması yapılması yeterlidir. Bu tanımlama içerisinde ki <strong>type</strong> kısmı ise <strong>ConfigurationSection</strong> veya <strong>ConfigurationSectionGroup</strong> türevli tipi işaret etmektedir.</p>
<h1>Test Uygulaması</h1>
<p>Şimdi dilerseniz basit bir <strong>aspx</strong> sayfası hazırlayıp <strong>section</strong> içeriğini ekrana bastıralım ve hatta üzerinde değişiklik yapıp <strong>web.config</strong> dosyasına kayıt edelim. Senaryonun bu kısmını gerçekleştirmek için aşağıdaki basit <strong>aspx</strong> sayfasını tasarlayabiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/ccs_4.png"><img style="margin: 4px 0px; display: inline;" title="ccs_4" src="/pics/ccs_4_thumb.png" alt="ccs_4" width="520" height="162" /></a></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="HowTo_WritingCustomConfigSection.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<table>
<tr>
<td><strong>Service Type</strong></td>
<td>
<asp:DropDownList ID="ddlServiceType" runat="server">
</asp:DropDownList>
</td>
</tr>
<tr>
<td><strong>Definition</strong> <strong>Name</strong></td>
<td>
<asp:TextBox ID="txtName" runat="server" Width="220px"></asp:TextBox>
</td>
</tr>
<tr>
<td><strong>Definition WSDL</strong> <strong>Address</strong></td>
<td>
<asp:TextBox ID="txtAddress" runat="server" Width="300px"></asp:TextBox>
</td>
</tr>
<tr>
<td> </td>
<td>
<asp:Button ID="btnSave" runat="server"
OnClick="btnSave_Click" Text="Save" Width="60px" />
</td>
</tr>
</table>
</div>
</form>
</body>
</html></pre>
<p>Gelelim kod tarafına. Sayfa yüklenirken <strong>serviceSection</strong> içeriğini göstermek arzusundayız. Ayrıca <strong>Save</strong> düğmesine basıldığında, yaptığımız değişiklikleri kaydetmek ve <strong>web.config</strong> dosyasını güncellemek istiyoruz.</p>
<blockquote>
<p>Senaryomuzda exceptional durumları göz ardı ettiğimizi ifade etmek isterim. Örneğin, boş değer geçilmesi, geçeriz bir url bildirimi yapılması vb. Ancak gerçek hayat senaryolarında bu tip veri doğrulama opsiyonlarını da işin içerisine katmanız önem arz etmektedir.</p>
</blockquote>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Configuration;
using System.Web.Configuration;
using System.Web.UI;
namespace HowTo_WritingCustomConfigSection
{
public partial class Default
: System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ddlServiceType.DataSource = Enum.GetNames(typeof(ServiceType));
ddlServiceType.DataBind();
ServiceConnectionSection scs =
ConfigurationManager.GetSection("serviceConnectionGroup/serviceConnection")
as ServiceConnectionSection;
if (scs != null)
{
txtAddress.Text = scs.Definition.WsdlAddress;
txtName.Text = scs.Definition.Name;
ddlServiceType.SelectedValue = scs.Type.ToString();
}
}
}
protected void btnSave_Click(object sender, EventArgs e)
{
Configuration manager = WebConfigurationManager.OpenWebConfiguration("/");
ServiceConnectionSection scs =
manager.GetSection("serviceConnectionGroup/serviceConnection")
as ServiceConnectionSection;
if (scs != null)
{
scs.Type = (ServiceType)Enum.Parse(typeof(ServiceType), ddlServiceType.SelectedValue.ToString());
scs.Definition.Name = txtName.Text;
scs.Definition.WsdlAddress = txtAddress.Text;
manager.Save();
}
}
}
}</pre>
<p><strong>serviceSection</strong> elementinin <strong>managed</strong> karşılığını elde edebilmek için <strong>Configuration</strong> veya <strong>ConfigurationManager</strong> tipinin <strong>GetSection</strong> metodundan yararlanılmaktadır. Söz konusu metodun dönüşü <strong>ServiceConnectionSection</strong> tipine dönüştürüldükten sonra ise <strong>Type</strong> ve <strong>Definition</strong> gibi özelliklere erişilebilinir. Hatta <strong>Definition</strong> özelliği üzerinden <strong>Name</strong> ve <strong>WsdlAddress</strong> değerleri de yakalanabilir. Pek tabi <strong>Save</strong> işleminin gerçekleştirilebilmesi için <strong>WebConfigurationManager</strong> ile açılan <strong>web.config</strong> dosyasını işaret eden <strong>manager</strong> isimli <strong>Configuration</strong> tipinden yararlanılmaktadır.</p>
<h1>Çalışma Zamanı Sonuçları</h1>
<p>Artık çalışma zamanına geçebilir ve sonuçları irdeleyebiliriz. Uygulamayı ilk olarak başlattığımızda <strong>Page_Laod</strong> içerisindeki kodlar devreye girecektir.</p>
<p><a href="https://www.buraksenyurt.com/pics/ccs_5.png"><img style="margin: 4px 0px; display: inline;" title="ccs_5" src="/pics/ccs_5_thumb.png" alt="ccs_5" width="523" height="194" /></a></p>
<p>Görüldüğü gibi varsayılan olarak belirtilen değerler çekilebilmiştir. Eğer bu noktada tip, ad ve adres bilgilerinde değişiklik yapıp <strong>Save</strong> düğmesine basılırsa, kod <strong>web.config</strong> dosyasında da gerekli etkiyi yapacaktır.</p>
<p>Tabi bu senaryoda çok basit bir <strong>section</strong> içeriği ele alınmış ve yönetilmiştir. Size tavsiyem <strong>connectionStrings</strong> gibi bir den fazla aynı tipden element içerebilen bir bölüm geliştirmeye çalışmanız olacaktır. Böyle bir senaryoda devreye <strong>ConfigurationElementCollection</strong> tipi girecektir. Bu örnek senaryoyu geliştirmeye çalışarak kendinizi bu yönde daha ileri bir noktaya taşıyabilirsiniz. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2012%2f9%2fHowTo_WritingCustomConfigSection.zip">HowTo_WritingCustomConfigSection.zip (25,98 kb)</a></p>2014-05-19T16:50:00+00:00asp.netcustom configurationconfiguration apixmlbsenyurtKonfigurasyon tabanlı geliştirme modeli, uygulama kodlarına girilmeden çalışma zamanına yönelik değişiklikler yapabilmemizi sağlar. Bu sayede pek çok programın kodsal müdahale yapmadan davranışları değiştirilebilir. .Net dünyasında baktığımızda da, App.Config, Web.config gibi dosyalar içerisinde Framework’ ün geneline yönelik pek çok konfigurasyon ayarı bulunduğu görülür. appSettings, connectionStrings, httpHandler vb…https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=6f62538d-916a-4688-b7c2-587268b5a19b0https://www.buraksenyurt.com/trackback.axd?id=6f62538d-916a-4688-b7c2-587268b5a19bhttps://www.buraksenyurt.com/post/AspNete28093Custom-Configuration-Section-Gelistirmek#commenthttps://www.buraksenyurt.com/syndication.axd?post=6f62538d-916a-4688-b7c2-587268b5a19bhttps://www.buraksenyurt.com/post/AspNet-Temellerie28093Eglenceli-GridViewAsp.Net Temelleri–Eğlenceli GridView2014-01-26T19:01:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p> <p>Bir süre önce çalıştığım firmada ufak bir web uygulaması ihtiyacı ile karşılaştım. Bir kaç günlük efor isteyen bu basit işte, uzun süredir kullanmadığım eski dostumuz <strong>GridView</strong> kontrolü ile haşır neşir oldum. Onun bir kaç etkili özelliğini ve işe yarar ip uçlarını tekrardan hatırladım. Hal böyle olunca bir araya getirip basit bir video anlatımında birleştirmek istedim. Ele aldığımız konulara gelince.</p> <ul> <li>Hiç veri olmadığında GridView. </li> <li>Alan içeriklerini formatlamak. </li> <li>N sayıda alan kullanan Link vermek. </li> <li>Belirli koşullarda satır bazında makyaj değiştirmek. </li> <li>Null alanlar için hücrede imaj göstermek. </li> <li>Tüm hücre içeriklerini aynı taraftan hizalamak. </li> </ul> <p><object width="640" height="360"><param name="movie" value="//www.youtube.com/v/2nXeoPfhbgY?version=3&hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="//www.youtube.com/v/2nXeoPfhbgY?version=3&hl=en_US" type="application/x-shockwave-flash" width="640" height="360" allowscriptaccess="always" allowfullscreen="true"></embed></object></p> <p>Afiyetle izleyin. Eski günlerimizi yaad edelim.</p>2014-01-26T19:01:00+00:00asp.netasp.net temelleriweb server controlsgridviewc#rowdataboundemptydatatemplateboundfieldhyperlinkfieldemptydatatextdataformatstringnulltextformatbsenyurthttps://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=f7a8fc8c-063f-457b-8a6f-cdca2d32a69813https://www.buraksenyurt.com/trackback.axd?id=f7a8fc8c-063f-457b-8a6f-cdca2d32a698https://www.buraksenyurt.com/post/AspNet-Temellerie28093Eglenceli-GridView#commenthttps://www.buraksenyurt.com/syndication.axd?post=f7a8fc8c-063f-457b-8a6f-cdca2d32a698https://www.buraksenyurt.com/post/AspNet-Routing-HatirlamakAsp.Net Routing – Hatırlamak2013-06-20T05:07:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/oblivious.jpg"><img style="margin: 4px 0px; display: inline; float: right;" title="oblivious" src="/pics/oblivious_thumb.jpg" alt="oblivious" width="320" height="207" align="right" /></a>Merhaba Arkadaşlar,</p>
<p>Geçtiğimiz günlerde şirkette çok küçük bir web uygulamasına ihtiyaç duyuldu. Neredeyse bir günlük geliştirme maliyeti olan, küçük bir departmanın önemli bir gereksinimi karşılayacaktı. Tabi insan uzun zaman kodlama yapmayınca veya kodlamaya ara verince bazı temel bilgileri de unutabiliyor.</p>
<p>Ben de kafayı <strong>Team Foundation Server</strong> entegrasyonu, <strong>SOA </strong>mimarisi ve <strong>Scrum</strong> gibi metodolojiler ile bozunca, zihinsel diskimdeki ana <strong>partition</strong>’ a yeni bilgilerin yazıldığına ve eskilerinin yerinde yeller estiğine şahit oldum. Ama malum, günümüz teknolojilerinde bilginin tamamını ezberlemeye çalışmak yerine, en doğrusuna en hızlı şekilde nasıl ulaşabileceğimizi bilmek daha önemli. İşte bu felsefeden yola çıkıp dedim ki, şu <strong>Asp.Net Routing</strong> konusunu bir hatırlayayım ve hatta kayıt altına alayım. İşte hikayemiz böyle başladı <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_96.png" alt="Smile" /></p>
<p>Asp.Net MVC’ nin en cazip yanlarından birisi sanırım sağladığı <strong>URL</strong> eşleştirme<em>(Routing)</em> sistemidir. Özellikle <strong>Search Engine Optimization<em>(SEO)</em></strong> kriterleri göz önüne alındığında, <strong>orders.aspx?categoryName=Beverages&shipCity=istanbul&orderNumber=12903 </strong>gibi bir ifade yerine, <strong>orders/beverages/istanbul/12903 </strong>şeklinde bir <strong>URL</strong> çok daha değerlidir.</p>
<p>Bilindiği üzere <strong>Asp.Net 4.0</strong> sürümü ile birlikte, <strong>URL</strong> ve asıl kaynak<em>(aslında yönlendirme sonucu gidilmesi gereken bir aspx sayfa kodu düşünebiliriz)</em> eşleştirmelerinde kullanılan yönlendirme işlemleri oldukça kolaylaştırılmıştır. İşte bu yazımızda, biraz temelleri hatırlamaya çalışacak ve <strong>SEO</strong>’cu arama motorlarının olmassa olmaz isterlerinden birisi olan <strong>Routing</strong> konusunu dört basit örnek üzerinden inceleyeceğiz. İlk olarak senaryomuza bir göz atalım.</p>
<h1>Senaryo</h1>
<p>Senaryomuzda veri kaynağı olarak emektar <strong>Northwind</strong> veritabanını kullanacağız. Örneklerimizde hem <strong>Entity Framework</strong> kullanacağımız hem de doğrudan <strong>SQL</strong> sorgusu çalıştıracağımız bir vakamız yer alacak. Temel olarak amacımız aşağıdaki ekran görüntüsünde yer alan <strong>URL</strong> eşleştirmelerini web uygulaması üzerinden işlettirmek.</p>
<p><a href="https://www.buraksenyurt.com/pics/route_2.png"><img style="margin: 4px 0px; display: inline;" title="route_2" src="/pics/route_2_thumb.png" alt="route_2" width="624" height="258" /></a></p>
<p>Dikkat edileceği üzere <strong>URL</strong> satırından girilecek olan anlamlı ifadeler, aslında arka planda bir eşleştirme tablosuna uygun olacak şekilde ilgili kaynaklara yönlendirilmekteler. Örneğin <strong>beverages</strong> isimli kategoride yer alan ürünlerin listelenmesi için yazılan <strong>urunler/beverages</strong> sorgusu, sisteme daha önceden öğretilen <strong>Urunler/{CategoryName}</strong> üzerinden geçerek <strong>urun.aspx</strong> sayfasına yönlendiriliyor. Çok doğal olarak ilgili sayfa içerisinde, <strong>CategoryName</strong> değerine bakılarak bir sonuç kümesinin sunulması gerekiyor.</p>
<h1>Route Eşleştirmelerinin Ayarlanması</h1>
<p>Bu işlem için <strong>global.asax.cs</strong> dosyasında aşağıdaki kodlamaları yapmamız gerekmektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Web;
using System.Web.Routing;
namespace HowTo_EasyRouting
{
public class Global
: HttpApplication
{
private void SetRouteMaps()
{
RouteTable.Routes.MapPageRoute("Varsayilan", "", "~/kategori.aspx");
RouteTable.Routes.MapPageRoute("Kategoriler", "kategoriler", "~/kategori.aspx");
RouteTable.Routes.MapPageRoute("KategoriBazliUrunler","urunler/{CategoryName}","~/urun.aspx");
RouteTable.Routes.MapPageRoute("SehirBazliSiraliMusteriler", "musteriler/{City}$orderby={FieldName}", "~/musteri.aspx");
RouteTable.Routes.MapPageRoute("SehirBazliSiparisler", "siparisler/{ShipCity}", "~/siparis.aspx");
}
protected void Application_Start(object sender, EventArgs e)
{
SetRouteMaps();
}
}
}</pre>
<p><strong>Application_Start</strong> olay metodu bilindiği üzere, Web uygulaması ayağa kalktığında devreye girmektedir. Dolayısıyla uygulamanın başladığı bir yerde, <strong>URL</strong> eşleştirme tanımlamalarını yapmak son derece mantıklıdır. Olayın ana kahramanı <strong>RouteTable</strong> sınıfıdır. Söz konusu tipin <strong>static</strong> olarak erişilebilen <strong>Routes</strong> özelliği bir <strong>RouteCollection</strong> referansını işaret etmektedir. Bu koleksiyon tahmin edileceği üzere <strong>URL</strong> ile asıl kaynak eşleştirmelerini taşımaktadır. Bu nedenle <strong>MapPageRoute</strong> metodundan da yararlanılarak gerekli eşleştirme bilgileri koleksiyona eklenir.</p>
<p>İlk satır ile <strong>Root URL</strong> adresine gelen bir talebin doğrudan <strong>kategori.aspx</strong> sayfasına yönlendirilmesi gerektiği ifade edilmektedir. İkinci satırda ise web kök adresini takiben kategoriler şeklinde gelen bir ifadenin gelmesi halinde yine, kategori.aspx sayfasına gidilmesi gerektiği belirtilmektedir.</p>
<p><strong>KategoriBazliUrunler</strong> ismi ile tanımlanmış eşleştirmeye göre, <strong>urunler/{CategoryName}</strong> şeklinde gelen talepler <strong>urun.aspx</strong> sayfasına yönlendirilmektedir. İlginç kullanımlardan birisi de <strong>SehirBazliSiraliMusteriler</strong> isimli eşleştirmedir. Burada <strong>City</strong> ve <strong>FieldName</strong> isimli iki <strong>Route</strong> parametresi söz konusudur. İfade ise size sanıyorum tanıdık gelecektir. Neredeyse bir <strong>REST</strong> servis sorgusuna<em>(örneğin <strong>OData</strong> sorgusuna)</em> oldukça yakın değil mi? <img class="wlEmoticon wlEmoticon-nerdsmile" style="border-style: none;" src="/pics/wlEmoticon-nerdsmile_1.png" alt="Nerd smile" /></p>
<p>Şimdi bu durumları kod tarafında nasıl karşılayacağımızı örnek bir Asp.Net uygulaması üzerinden incelemeye çalışalım.</p>
<h1>Birinci Durum</h1>
<p>İlk olarak <strong>kategori.aspx</strong> sayfasına doğru yapılacak yönlendirmeleri ele almaya çalışacağız. Bunun için web uygulamamıza <strong>kategori.aspx</strong> isimli bir sayfa ekleyip içeriği ile kod tarafını aşağıdaki gibi geliştirelim.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Kategori.aspx.cs" Inherits="HowTo_EasyRouting.Kategori" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div id="divCategories" runat="server" style="background-color:lightcyan">
</div>
</form>
</body>
</html></pre>
<p>kod tarafı</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace HowTo_EasyRouting
{
public partial class Kategori
: Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
using (NorthwindEntities context = new NorthwindEntities())
{
var categories = from c in context.Categories
orderby c.CategoryName
select new
{
c.CategoryID,
c.CategoryName
};
foreach (var category in categories)
{
HyperLink categoryLink = new HyperLink();
categoryLink.NavigateUrl = GetRouteUrl("KategoriBazliUrunler", new { CategoryName = category.CategoryName });
categoryLink.Text = string.Format("[{0}]-{1}<br/>", category.CategoryID.ToString(), category.CategoryName);
divCategories.Controls.Add(categoryLink);
}
}
}
}
}
}</pre>
<p>Aslında <strong>kategori.aspx</strong> sayfasında tipik olarak <strong>Entity Framework</strong> odaklı bir sorgulama gerçekleştirilmekte ve kategori adları birer <strong>HyperLink</strong> bileşeni olarak <strong>div</strong> içerisine eklenmektedir. Konu itibariyle işin önemli olan kısmı ise <strong>HyperLink</strong> bileşeninin <strong>NavigateUrl</strong> özelliğine <strong>GetRouteUrl</strong> metodu sonucunun atanmasıdır.</p>
<p><strong>GetRouteUrl</strong> metodu dikkat edileceği üzere iki parametre alır. İlk parametre <strong>route</strong> adıdır. Yazdığımız değere göre <strong>urunler/{CategoryName}</strong> şeklindeki atama değerlendirilir. İkinci parametre ise bu <strong>Route</strong> içerisinden kullanılmak istenen değişken adı ve değerini içeren nesnenin örneklendiği kısımdır. <strong>CategoryName</strong> tahmin edileceği üzere <strong>Route</strong> tanımı içerisindeki parametre adıdır. Değeri ise zaten <strong>LINQ<em>(Language INtegrated Query)</em></strong> sorgusu içerisinden elde edilmektedir. İkinci parametre <strong>object</strong> tipinden olduğundan <strong>bir isimsiz tip<em>(anonymous type)</em></strong> ataması yapılabilmiştir. Bu nedenle <strong>Route</strong> içerisinde birden fazla parametre olması halinde, isimsiz tipin de birden fazla özellik içermesi gerektiğini ifade edebiliriz.</p>
<p>İlk durumda herhangibir sayfa talep edilmediğinde veya kök web adresi ardından <strong>/kategoriler</strong> şeklinde bir <strong>URL</strong> ifadesi kullanıldığında, aşağıdaki ekran görüntüsünde yer alan sonuçlar ile karşılaşırız.</p>
<p><a href="https://www.buraksenyurt.com/pics/route_3.png"><img style="margin: 4px 0px; display: inline;" title="route_3" src="/pics/route_3_thumb.png" alt="route_3" width="369" height="331" /></a></p>
<p>Dikkat edilmesi gereken en önemli nokta, her hangi bir bağlantı üstüne gelindiğinde oluşan sorgu adresidir. Örneğin <strong>Condiments</strong> için <strong>http://localhost:54605/urunler/condiments</strong> şeklinde bir <strong>URL</strong> tanımı oluşmuştur. Peki bu bağlantıya tıklanırsak ne olur? <img class="wlEmoticon wlEmoticon-whome" style="border-style: none;" src="/pics/wlEmoticon-whome_10.png" alt="Who me?" /></p>
<h1>İkinci Durum</h1>
<p><strong>kategori.aspx</strong> sayfasında bir bağlantıya tıklandığında, <strong>HyperLink</strong> bileşeninin <strong>NavigateUrl</strong> özelliğinin sahip olduğu değerin <strong>Route</strong> tablosundaki eşleniğine bakılmalıdır. Yaptığımız tanımlamalara göre <strong>urun.aspx</strong> sayfasına gidilmesi beklenmelidir<em>(KategoriBazliUrunler isimli Route tanımına dikkat edin)</em> Buna göre <strong>urun.aspx</strong> sayfasının içeriğini aşağıdaki gibi düzenleyebiliriz.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Urun.aspx.cs" Inherits="HowTo_EasyRouting.Urun" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1 style="color:purple">
<asp:Label ID="lblCategoryName" runat="server" /></h1>
<br />
<asp:GridView ID="grdUrunler" runat="server" />
</div>
</form>
</body>
</html></pre>
<p>kod tarafı</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Linq;
using System.Web.UI;
namespace HowTo_EasyRouting
{
public partial class Urun
: Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (RouteData.Values["CategoryName"] != null)
{
string categoryName = RouteData.Values["CategoryName"].ToString();
using (NorthwindEntities context = new NorthwindEntities())
{
var products = from p in context.Products.Include("Product")
where p.Category.CategoryName == categoryName
orderby p.ProductName
select new
{
p.ProductID,
p.ProductName,
p.UnitPrice
};
grdUrunler.DataSource = products.ToList();
grdUrunler.DataBind();
}
}
else
Response.Redirect(GetRouteUrl("Kategoriler", null));
}
}
}</pre>
<p>Tabi bu sayfaya gelindiğinde aslında <strong>Route</strong> tanımlaması içerisinde yer alan parametre değerinin okunması gerekmektedir. Bu sebepten sayfanın <strong>RouteData</strong> özelliğinden hareket edilerek <strong>RouteValueDictionary</strong> tipinden olan <strong>Values</strong> özelliğine gidilir ve indeksleyiciye verilen <strong>CategoryName</strong> alanının var olup olmadığına bakılır. Malum urun.aspx sayfasına farklı bir şekilde erişilmek istenebilir ve <strong>CategoryName</strong> değeri null olarak gelebilir. Bu nedenle bir <strong>null</strong> değer kontrolü ardından <strong>Entity</strong> sorgulama işlemi yapılmıştır. <strong>RouteData.Values[“CategoryName”] </strong>ile <strong>URL</strong> satırındaki kategori adı bilgisi alındıktan sonra standart olarak bir <strong>Entity</strong> sorgusu icra edilmektedir. Eğer kategori adı <strong>null </strong>olarak gelirse bu durumda varsayılan <strong>URL</strong> eşleştirilmesi nedeniyle kategorilerin gösterildiği sayfaya gidilir.</p>
<p><a href="https://www.buraksenyurt.com/pics/route_4.png"><img style="margin: 4px 0px; display: inline;" title="route_4" src="/pics/route_4_thumb.png" alt="route_4" width="399" height="421" /></a></p>
<h1>Üçüncü Durum</h1>
<p><strong>URL</strong> eşleştirmelerinden <strong>SehirBazliSiraliMusteriler</strong> isimli olanı, iki adet <strong>route</strong> parametresi içermektedir. Burada başta da belirttiğimiz üzere <strong>OData</strong> sorgularına benzer bir ifade tanımlanmıştır. Eşleştirme bilgisine göre <strong>musteri.aspx</strong> sayfasına doğru bir yönlendirme söz konusudur. <strong>musteri.aspx</strong> içeriğini aşağıdaki gibi geliştirdiğimizi düşünelim.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Musteri.aspx.cs" Inherits="HowTo_EasyRouting.Musteri" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1 style="color:purple">Customers</h1>
<asp:GridView ID="grdCustomer" runat="server" />
</div>
</form>
</body>
</html></pre>
<p>kod tarafı</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Linq;
using System.Reflection;
using System.Web.Routing;
namespace HowTo_EasyRouting
{
public partial class Musteri : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(RouteData.Values["City"]!=null
|| RouteData.Values["FieldName"]!=null)
{
string cityName=RouteData.Values["City"].ToString();
string fieldName=RouteData.Values["FieldName"].ToString();
using(NorthwindEntities context=new NorthwindEntities())
{
var customers = context
.Customers
.Where(c => c.City == cityName)
.OrderBy(GetField<Customer>(fieldName))
.Select(c => new
{
ID=c.CustomerID,
Title=c.ContactTitle,
Contact=c.ContactName,
Company=c.CompanyName,
c.City
});
grdCustomer.DataSource = customers.ToList();
grdCustomer.DataBind();
}
}
}
public static Func<T, string> GetField<T>(string fieldName)
{
PropertyInfo pInfo=typeof(T).GetProperty(fieldName);
if (pInfo == null)
pInfo = typeof(T).GetProperty("CustomerID");
return o => Convert.ToString(pInfo.GetValue(o, null));
}
}
}</pre>
<p><strong>RouteData.Values</strong> özelliğinden yararlanılarak <strong>CityName</strong> ve <strong>FieldName</strong> değerlerinin <strong>null</strong> olup olmamasına göre bir kod parçası çalıştırılmaktadır. Bir önceki örnekten farklı bir durum olmasa da <strong>Entity</strong> sorgusunda <strong>OrderBy</strong> <strong>extension</strong> metodunu nasıl kullandığımıza dikkat etmenizi rica ederim. İşte bu vakaya ait örnek ekran çıktıları.</p>
<p><em>Londra’ daki müşterilerin CustomerId bilgilerini göre sıralı olarak çekilmesi</em></p>
<p><a href="https://www.buraksenyurt.com/pics/route_5.png"><img style="margin: 4px 0px; display: inline;" title="route_5" src="/pics/route_5_thumb.png" alt="route_5" width="576" height="322" /></a></p>
<p><em>Londra’ daki müşterilerin ContactName bilgisine göre sıralı olarak çekilmesi</em></p>
<p><a href="https://www.buraksenyurt.com/pics/route_6.png"><img style="margin: 4px 0px; display: inline;" title="route_6" src="/pics/route_6_thumb.png" alt="route_6" width="581" height="333" /></a></p>
<h1>Dördüncü Durum</h1>
<p>Son vakada bir <strong>Route</strong> parametrenin her hangi bir veri bağlı kontrol ile nasıl ilişkilendirilebileceğini görmeye çalışacağız. Örneğin bir <strong>SqlDataSource</strong> bileşenindeki <strong>Select</strong> sorgusuna ait <strong>Where</strong> koşullarını <strong>Route</strong> parametreler ile ilişkilendirebiliriz. Bu durumu <strong>siparis.aspx</strong> sayfası içerisinde ele almaya çalışalım. <strong>Siparis</strong> sayfasına gelinebilmesi için <strong>Route</strong> tablo tanımlamalarına göre <strong>/siparisler/{ShipCity}</strong> şeklinde bir <strong>URL</strong> talebinin gönderilmesi gerekmektedir.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Siparis.aspx.cs" Inherits="HowTo_EasyRouting.Siparis" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1 style="color:purple">Siparişler</h1>
<asp:GridView ID="grdOrders" runat="server" AllowPaging="True" AutoGenerateColumns="False" DataKeyNames="OrderID" DataSourceID="SqlDataSource1" >
<Columns>
<asp:BoundField DataField="OrderID" HeaderText="OrderID" InsertVisible="False" ReadOnly="True" SortExpression="OrderID" />
<asp:BoundField DataField="CustomerID" HeaderText="CustomerID" SortExpression="CustomerID" />
<asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID" SortExpression="EmployeeID" />
<asp:BoundField DataField="ShippedDate" HeaderText="ShippedDate" SortExpression="ShippedDate" />
<asp:BoundField DataField="ShipCity" HeaderText="ShipCity" SortExpression="ShipCity" />
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT [OrderID], [CustomerID], [EmployeeID], [ShippedDate], [ShipCity] FROM [Orders] WHERE ([ShipCity] = @ShipCity)">
<SelectParameters>
<asp:RouteParameter DefaultValue="" Name="ShipCity" RouteKey="ShipCity" Type="String" />
</SelectParameters>
</asp:SqlDataSource>
</div>
</form>
</body>
</html></pre>
<p>Önemli olan, <strong>SqlDataSource</strong> bileşenine ait <strong>SelectCommand</strong> ifadesindeki <strong>where</strong> koşulunda yer alan <strong>ShipCity</strong> isimli parametrenin bir <strong>RouteParameter</strong> ile ilişkilendirilmiş olmasıdır. <strong>RouteParameter</strong> bileşenine ait <strong>RouteKey</strong> özelliği, <strong>Route Table</strong>’ daki ile aynı olmalıdır. Çok doğal olarak <strong>aspx</strong> kaynak tarafında yapılabilen bu eşleştirme, <strong>Wizard</strong> üzerinden de kolayca belirlenebilir. Aynen aşağıdaki ekran görüntüsünde olduğu gibi <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_198.png" alt="Winking smile" /></p>
<p><a href="https://www.buraksenyurt.com/pics/route_1.png"><img style="margin: 4px 0px; display: inline;" title="route_1" src="/pics/route_1_thumb.png" alt="route_1" width="574" height="760" /></a></p>
<p>Buna göre örneğin Stuttgart’ a yapılan sevkiyatları aşağıdaki gibi elde edebiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/route_7.png"><img style="margin: 4px 0px; display: inline;" title="route_7" src="/pics/route_7_thumb.png" alt="route_7" width="513" height="403" /></a></p>
<h1>Sonuç</h1>
<p>Görüldüğü üzere <strong>URL</strong> eşleştirme işlemleri klasik sunucu tabanlı <strong>Asp.Net </strong>uygulamalarında da etkili bir şekilde kullanılabilir. Hatta bu felsefeden yola çıkarak <strong>OData</strong> sorgularının daha fazla gelişmişlerini destekleyecek web uygulamaları yazılması da pekala olasıdır. Yazımıza konu olan basit örneklerimizde ki anahtar noktalar, <strong>RouteTable</strong> sınıfı, <strong>RouteData.Values</strong> özelliği ve <strong>GetRouteUrl</strong> metodudur. Örneği geliştirmek tamamen sizin elinizde. İşe <strong>/siparisler/stuttgart/10301/</strong> şeklinde bir sorguyu ele alıp, <strong>10301</strong> numaralı siparişe ait detay bilgileri göstereceğiniz bir sayfayı üretmeyi deneyerek başlayabilirsiniz. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2013%2f3%2fHowTo_EasyRouting.zip">HowTo_EasyRouting.zip (605,23 kb)</a></p>2013-06-20T05:07:00+00:00asp.netroutingroute tablemvcmodel view controllerurlurl mappingbsenyurtAsp.Net MVC’ nin en cazip yanlarından birisi sanırım sağladığı URL eşleştirme(Routing) sistemidir. Özellikle Search Engine Optimization(SEO) kriterleri göz önüne alındığında, orders.aspx?categoryName=Beverages&shipCity=istanbul&orderNumber=12903 gibi bir ifade yerine, orders/beverages/istanbul/12903 şeklinde bir URL çok daha değerlidir.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=9ea12f5b-1b7a-4f4a-8f26-90f8c860e6284https://www.buraksenyurt.com/trackback.axd?id=9ea12f5b-1b7a-4f4a-8f26-90f8c860e628https://www.buraksenyurt.com/post/AspNet-Routing-Hatirlamak#commenthttps://www.buraksenyurt.com/syndication.axd?post=9ea12f5b-1b7a-4f4a-8f26-90f8c860e628https://www.buraksenyurt.com/post/Asp-Net-Temelleri-Derinlemesine-DownloadUpload-Islemleri-bsenyurt-com-danAsp.Net Temelleri : Derinlemesine Download/Upload İşlemleri2007-08-15T06:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Tatile çıkan herkes, iyi ve dinlendirici geçen günlerin ardından tekrar hayatın akışına kapıldığında kısa süreliğinede olsa adaptasyon problemi yaşar. Tatildeyken hatırlayacağınız gibi hafif ve dinlendirici bir Asp.Net konusu ile ilgilenmeye çalışmıştık. Tatil dönüşündeki adaptasyon sürecinde de benzer nitelikte bir konuyu incelemenin uygun olacağı kanısındayım. Bu yazımızda Asp.Net uygulamalarında sıklıkla başvurduğumuz temel dosya giriş/çıkış <strong>(Input/Output -IO)</strong> işlemlerinden yararlanarak <strong> Download</strong> ve <strong>Upload</strong> işlemlerinin nasıl yapılabileceğini ele almaya çalışacağız.</p>
<p>Özellikle web tabanlı içerik yönetim sistemlerinde <strong>(Content Management System)</strong>, kullanıcıların sunucu üzerinde dökümanlar ile etkin bir şekilde çalışabilmeleri sağlanmaktadır. Bu sistemlerde genel olarak kullanıcı kimliği veya rolüne göre istemci bilgisayarlara indirilebilen<strong>(Download)</strong>. Hatta çoğu içerik yönetim sisteminde, istemciler herkesin okuyabileceği yada belirli kişilerin görebileceği şekilde sunucuya döküman aktarma <strong>(Upload)</strong> işlemleride yapabilirler. Söz gelimi bir yazılım şirketinin içerik yönetim sistemi göz önüne alındığında, yazılım departmanındaki geliştiricilerin hazırladıkları teknik dökümantasyonları Upload veya Download edebilecekleri bir ortam hazırlanabilir.</p>
<p>Hangi açıdan bakılırsa bakılsın, web tabanlı olarak yapılan bu işlemler için şirketler büyük ölçekli sistemler tasarlayıp geliştirmiştir. Fakat temel ilke ve yaklaşımlar benzerdir. Dosya indirme veya gönderme işlemleri, web tabanlı bir sistem göz önüne alındığında <strong>HTTP</strong> kurallarına bağlıdır. Dolayısıyla bu kuralların sadece uygulanma ve ele alınma biçimleri programlama ortamları arasında farklılıklar gösterebilir. İşte biz bu makalemizde, Asp.Net 2.0 tarafından olaya bakmaya çalışıyor olacağız. İlk olarak dosya indirme işlemlerini ele alacağız. Sonrasında ise <strong>Asp.Net 2.0</strong> ile gelen <strong>FileUpload</strong> aracı yardımıyla sunucuya dosya gönderme<strong>(Upload) </strong>işlemlerinin nasıl yapılabileceğini inceleyeceğiz. Ek olarak, upload edilen bir dosyanın kaydedilmeden, sunucu belleği üzerinde canlandırılıp işlenmesinin nasıl gerçekleştirilebileceğine bakacağız. Son olarakta, Upload edilen dosyaların bir veritabanı tablosunda alan(Field) olarak saklanması için gereken adımları göreceğiz. Dilerseniz vakit kaybetmeden dosya indirme süreci ile işe başlayalım.</p>
<p>Dosya indirme (Download) işlemlerinde bilinen <strong>IO</strong> tiplerinden ve <strong>Response</strong> sınıfının ilgili metodlarından yararlanılır. Hatırlanacağı gibi herhangibir resim dosyasını bir web sayfası içerisinde göstermek için üretilen <strong>HTML</strong> içeriği ile oynamak gerektiğinden daha önceki <a href="https://www.buraksenyurt.com/post/Asp-Net-Temelleri-Tablo-Bazlc4b1-Resimleri-Ele-Almak-bsenyurt-com-dan">makalemizde</a> bahsetmiştik. Dosya indirme(Download) işlemindede içeriğin tipi<strong>(Content-Type)</strong>, uzunluğu<strong>(Content-Length)</strong> gibi bilgiler önem kazanmaktadır. İlk örneğimizde, IIS üzerinde yayınlanan bir web projesindeki Dokumanlar isimli bir klasörde yer alan dosyaların indirilme işlemlerini gerçekleştirmeye çalışacağız. Bu amaçla web uygulamasına ait dokumanlar klasörü altına aşağıdaki şekildende görüldüğü üzere farklı formatlarda örnek dosyalar atılmasında fayda vardır.</p>
<p><img src="/makale/images/mk218_1.gif" alt="" width="621" height="197" border="0" /></p>
<p>Web uygulamamızın ilk amacı Dokumanlar klasöründeki dosyaların listelenmesini sağlamak olacak. Bu amaçla sayfada bir GridView kontrolü kullanılabilir. Hatta bu kontrolün içeriği <strong>FileInfo</strong> tipinden nesnelerden oluşan generic bir liste koleksiyonundan <strong>(List<FileInfo>)</strong> gelebilir. Böylece istemciler indirebilecekleri dosyalarıda görebilir. Download işleminin gerçekleştirilmesi için GridView kontrolünde bir Select Button' dan faydalanılabilir. İndirme işlemi sırasında indirilmek istenen dosyanın fiziki adresi, uzunluğu gibi bilgiler önemlidir. Bu bilgileri ve fazlasını <strong>FileInfo</strong> sınıfına ait bir nesne örneği yardımıyla elde edebiliriz. Uygulamamıza ait Default.aspx sayfasının içeriği aşağıdaki gibi olacaktır.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.IO" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string[] dosyalar = Directory.GetFiles(Server.MapPath("Dokumanlar"));
List<FileInfo> dosyaBilgileri = new List<FileInfo>();
foreach (string dosya in dosyalar)
{
dosyaBilgileri.Add(new FileInfo(dosya));
}
grdDosyalar.DataSource = dosyaBilgileri;
grdDosyalar.DataBind();
}
}
protected void grdDosyalar_SelectedIndexChanged(object sender, EventArgs e)
{
string dosyaAdi = Server.MapPath("dokumanlar") + "\\" + grdDosyalar.SelectedRow.Cells[0].Text;
FileInfo dosya = new FileInfo(dosyaAdi);
Response.Clear(); // Her ihtimale karşı Buffer' da kalmış herhangibir veri var ise bunu silmek için yapıyoruz.
Response.AddHeader("Content-Disposition","attachment; filename=" + dosyaAdi); // Bu şekilde tarayıcı penceresinden hangi dosyanın indirileceği belirtilir. Eğer belirtilmesse bulunulan sayfanın kendisi indirilir. Okunaklı bir formattada olmaz.
Response.AddHeader("Content-Length",dosya.Length.ToString()); // İndirilecek dosyanın uzunluğu bildirilir.
Response.ContentType = "application/octet-stream"; // İçerik tipi belirtilir. Buna göre dosyalar binary formatta indirilirler.
Response.WriteFile(dosyaAdi); // Dosya indirme işlemi başlar.
Response.End(); // Süreç bu noktada sonlandırılır. Bir başka deyişle bu satırdan sonraki satırlar işletilmez hatta global.asax dosyasında eğer yazılmışsa Application_EndRequest metodu çağırılır.
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Dosya Indirme Islemleri</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>Dosyalar</h2>
<asp:GridView ID="grdDosyalar" runat="server" AutoGenerateColumns="False" SelectedRowStyle-BackColor="Gold" OnSelectedIndexChanged="grdDosyalar_SelectedIndexChanged">
<Columns>
<asp:BoundField DataField="Name" HeaderText="Dosya Adı" />
<asp:BoundField DataField="Length" HeaderText="Dosya Uzunluğu" />
<asp:BoundField DataField="Extension" HeaderText="Uzantısı" />
<asp:BoundField DataField="CreationTime" HeaderText="Oluşturulma Zamanı" DataFormatString="{0:dd.MMMM.yy}" HtmlEncode="False" />
<asp:BoundField DataField="LastWriteTime" HeaderText="Son Yazılma Zamanı" DataFormatString="{0:dd.MMMM.yy}" HtmlEncode="False" />
<asp:CommandField ButtonType="Button" SelectText="indir" ShowSelectButton="True" />
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html></pre>
<p>Öncelikli olarak sayfamızda neler yaptığımıza kısaca bakalım. Default.aspx sayfası ilk yüklendiğinde (bunu sağlamak için <strong>IsPostBack</strong> özelliği ile kontrol yapılmıştır) Dokumanlar klasöründeki dosyaların adları elde edilmektedir. Bu işlem için <strong>Directory</strong> sınıfının <strong>GetFiles</strong> metodu kullanılmaktadır. Bir web uygulaması söz konusu olduğu için, sanal klasörün karşılık geldiği fiziki adresi bulmak adına <strong>Server.MapPath</strong> metodu ele alınmaktadır. GetFiles metodu parametre olarak belirtilen klasördeki dosya isimlerinin elde edilmesini sağlamaktır. Bu nedenle geriye string tipinden bir dizi döndürür. GridView kontrolü içerisinde, elde edilen bu dosyalara ait bazı temel bilgilerin gösterilmesi hedeflenmiştir.</p>
<p>Örnekte dosyanın adı<strong>(Name)</strong>, uzunluğu<strong>(Length)</strong>, uzantısı<strong>(Extension)</strong>, oluşturulma<strong>(CreationTime)</strong> ve son yazılma zamanı<strong>(LastWriteTime)</strong> bilgileri ele alınmaktadır. Elde edilen dosya adları aslında fiziki adresleride içermektedir. Bu nedenle ilgili dosya adları <strong>FileInfo</strong> tipinden örneklerin oluşturulmasında kullanılır. Bu nesne örnekleride generic koleksiyonda toplanır. Son olarak GridView kontrolüne veri kaynağı olarak generic liste koleksiyonu atanır. FileInfo ile gelen bilgilerden bazılarını GridView kontrolünde göstermek istediğimizden, <strong> AutoGenerateColumns</strong> özelliğine <strong>false</strong> değeri atanmış ve Columns elementi içerisinde ilgili alanlar açık bir şekilde yazılmıştır. <strong>BoundField</strong> elementlerine ait <strong>DataField</strong> niteliklerinin değerleri FileInfo ile gelen nesne örneklerindeki özellik adları olarak ayarlanmıştır.</p>
<p>İndirilmek istenen dosya için GridView kontrolüne bir adet <strong>CommandField</strong> elementi dahil edilmiştir. Burada seçme işlemi ele alınarak aslında küçük bir hile yapılmaktadır. GridView kontrolünde seçim düğmesine basıldıktan sonra devreye giren <strong> SelectedIndexChanged</strong> olayı içerisinde dosya indirme(Download) işlemi başlatılmaktadır. Teorik olarak Response sınıfının WriteFile metodu ile parametre olarak verilen dosya istemci bilgisayara indirilebilirmektedir. Ancak ön hazırlıklar yapılması gerekmektedir. Bu amaçla, indirilmek istenen dosya adı, GridView kontrolünde seçilen satıra ait ilk hücreden seçildikten sonra, Response sınıfının ilgili metodları ile ön hazırlıklar yapılır. İndirilecek dosya üretilen çıktının <strong>Header</strong> kısmında ele alınmaktadır.</p>
<p>Benzer şekilde indirilecek dosyanın uzunluğuda Header kısmına eklenir. Daha sonra içerik tipi belirlenir. <strong> application/octet-stream</strong> değeri, dosyanın ikili<strong>(binary)</strong> formatta indirileceğini belirtmektedir. Bu işlemlerin arkasındanda Response sınıfının <strong>WriteFile</strong> ve <strong>End</strong> metodları sırasıyla çalıştırılır. End metodu, o anki sayfaya ait yaşam sürecinin kesilmesini sağlamaktadır. Bir başka deyişle <strong>Response.End</strong> çağrısından sonra herhangibir kod satırı var ise işletilmeyecektir. Hatta, global.asax dosyasında yer alan <strong>Application_EndRequest</strong> metoduda devreye girecektir. Bu durumu analiz etmeden önceği örneğimizi test edelim. Uygulama çalıştırıldığında aşağıdakine benzer bir ekran görüntüsü ile karşılaşılacaktır.</p>
<p><img src="/makale/images/mk218_2.gif" alt="" width="626" height="442" border="0" /></p>
<p>Burada pek çok ek özellik tasarlanabilir. Örneğin uzantıya göre içerik tipi değiştirilebilir. Hatta download işlemi yerine örnek bir dökümanın sayfaya çıktı olarak verilmesi sağlanabilir. PDF içeriklerinin tarayıcıda gösterilmesi buna örnek olarak verilebilir. Bunların dışında uygulamanın kullanıcıya göre yetkilendirilmesi ve sadece ele alabileceği dosyaları indirebilmesi sağlanabilir. Bu tamamen projenin ihtiyaçlarına ve geliştiricinin kullanıcılara sunmak istediklerine bağlı olarak gelişebilecek bir modeldir. indir başlıklık düğmelerden herhangibirine bastığımızda indirme işleminin aşağıdaki ekran görüntüsünde yer aldığı gibi başladığı görülür.</p>
<p><img src="/makale/images/mk218_3.gif" alt="" width="624" height="514" border="0" /></p>
<p>Şimdi gelelim Response.End metodunun etkisine. Bu durumu analiz etmek için, Response.End sonrasına aşağıdaki gibi örnek bir kod satırı ekleyelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">protected void grdDosyalar_SelectedIndexChanged(object sender, EventArgs e)
{
string dosyaAdi = Server.MapPath("dokumanlar") + "\\" + grdDosyalar.SelectedRow.Cells[0].Text;
FileInfo dosya = new FileInfo(dosyaAdi);
Response.Clear();
Response.AddHeader("Content-Disposition","attachment; filename=" + dosyaAdi);
Response.AddHeader("Content-Length",dosya.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.WriteFile(dosyaAdi);
Response.End();
Response.Write("Dosya indirildi");
}</pre>
<p>Hemen arkasından bilinen yaşam döngüsünü izlemek adına default.aspx sayfasını aşağıdaki gibi değiştirelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">protected void Page_PreInit(object sender, EventArgs e)
{
Debug.WriteLine("Page_PreInit metodu");
}
protected void Page_Init(object sender, EventArgs e)
{
Debug.WriteLine("Page_Init metodu");
}
protected void Page_Load(object sender, EventArgs e)
{
// Diğer kod satırları
Debug.WriteLine("Page_Load metodu");
}
protected void Page_PreRender(object sender, EventArgs e)
{
Debug.WriteLine("Page PreRender Metodu");
}
protected void Page_Unload(object sender, EventArgs e)
{
Debug.WriteLine("Page Unload Metodu");
}
protected void grdDosyalar_SelectedIndexChanged(object sender, EventArgs e)
{
Debug.WriteLine("Dosya indirme işlemi başlıyor");
// Diğer kod satırları
Response.End();
Debug.WriteLine("Response.End metodu çağırıldı");
Response.Write("Dosya indirildi");
}</pre>
<p>Bu değişikliklere ek olarak projeye <strong>global.asax</strong> dosyası ekleyip içerisine <strong>Application_EndRequest</strong> metodunu aşağıdaki gibi dahil edelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">void Application_EndRequest(object sender, EventArgs e)
{
Debug.WriteLine("Application EndRequest Metodu Çağırıldı");
}</pre>
<p>Şimdi uygulamayı <strong>Debug</strong> modda çalıştırıp <strong>output</strong> penceresindeki çıktıları izleyebiliriz. Bir dosya indirme işlemi gerçekleştirildikten sonra sayfanın yaşam döngüsü aşağıdaki gibi çalışacaktır.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">Page_PreInit metodu
Page_Init metodu
Page_Load metodu
Dosya indirme işlemi başlıyor
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code
Page Unload Metodu
Application EndRequest Metodu Çağırıldı</pre>
<p>Çok doğal olarak GridView kontrolü üzerindeki düğmeye basıldığında sayfanın sunucuya tekrar gitmesi ve işlenmesi söz konusudur. Bu nedenle süreç <strong>Page_PreInit</strong> ile başlamaktadır. Ancak dikkat edilecek olursa Response.End çağrısından sonraki satırlar devreye girmemiştir. Debug penceresine ve tarayıcıdaki çıktıya herhangibir kod yazılmamıştır. Dahası, bir <strong> exception(System.Threading.ThreadAbortException)</strong> fırlatılmış ve sayfa yaşam döngüsü <strong>Page_PreRender</strong> metodunu işletmeden doğrudan <strong> Page_Unload</strong> olayını işletmiş ve arkasından <strong>global.asax</strong> dosyasındaki <strong>Application_EndRequest</strong> devreye girmiştir. Elbetteki üretilen istisna<strong>(exception)</strong> Asp.Net çalışma ortamı tarafından görmezden gelinmektedir. Bu nedenle istemci herhangibir şekilde hata mesajı ile karşılaşmaz.</p>
<p>Gelelim makalemizin ikinci konusuna. İndirme işlemleri kadar Upload işlemleride önemlidir. Tabi burada istemcilerin her dosya tipini veya çeşidini sunucuya göndermesi doğru olmayabilir. Kapalı ağ(<strong>intranet)</strong> sistemlerinde bu söz konusu olabilir. Nitekim kimin handi dosyayı Upload ettiğinin belirlenmesi dışında, bu kişiye ulaşılmasıda kolaydır :). Ancak <strong>internet</strong> tabanlı daha geniş sistemlerde her ne kadar kullanıcılar tespit edilebilsede, kötü niyetli istemcilerin varlığı nedeniyle sistemin genelini tehlikeye atmamak adına tedbirler almak doğru bir yaklaşım olacaktır. Biz tabiki basit olarak Upload işlemlerini ele alacağız ve bahsettiğimiz güvenlik konularını şimdilik görmezden geleceğiz.</p>
<p>Upload işlemlerini kolaylaştırmak adına Asp.Net 2.0, <strong>FileUpload</strong> isimli bir kontrol getirmektedir. Bu kontrol basit olarak istemcinin Upload etmek istediği dosyayı seçebilmesini sağlamaktadır. Bu seçim işlemi ile birlikte, sunucuya gönderilmek istenen dosyaya ait bir takım bilgilerde FileUpload kontrolünce elde edilir. Örneğin içeriğin tipi kontrol edilerek sadece bazı dosyaların gönderilmesine izin verilebilir. İlk olarak web uygulamamıza aşağıdaki gibi Default2.aspx sayfasını ekleyelim.</p>
<p><img src="/makale/images/mk218_4.gif" alt="" width="357" height="101" border="0" /></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnGonder_Click(object sender, EventArgs e)
{
if (uplDosya.HasFile)
{
uplDosya.SaveAs(Server.MapPath("Dokumanlar") + "\\" + uplDosya.FileName);
}
else
Response.Write("Upload edilecek dosya yok");
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Upload Islemleri</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Dosyayı Seçin : <asp:FileUpload ID="uplDosya" runat="server" />
<br />
<asp:Button ID="bntGonder" runat="server" Text="Gönder" OnClick="btnGonder_Click" />
</div>
</form>
</body>
</html></pre>
<p>Öncelikli olarak neler yaptığımıza bir bakalım. FileUpload kontrolü ile dosya seçilmesi, gönderme işlemi için yeterli değildir. Sayfanın sunucuya doğru gönderilmesi gerekmektedir. Bu işi basit olarak bir Button kontrolü üstelenebilir. Button kontrolümüze ait Click olay metodunda ise öncelikli olarak seçili bir dosya olup olmadığı <strong>HasFile</strong> özelliği ile kontrol edilmektedir. Bu kontrol, dosya seçmeden gönderme işlemi yapıldığı takdirde oluşacak hataların önüne geçilmesini sağlamaktadır. Eğer seçili olan bir dosya var ise basit olarak <strong> FileUpload</strong> sınıfının <strong>SaveAs</strong> metodu çağırılır. SaveAs metoduna dosyanın yol adresinin fiziki olarak verilmesi gerekmektedir. Bu nedenle yine <strong>Server.MapPath</strong> ile Dokumanlar klasörünün fiziki adresi elde edilir. FileUpload kontrolünün <strong>FileName</strong> özelliği ile seçilen dosyanın adı yakalanmakta ve fiziki adresin sonuna eklenmektedir. Eğer kod Debug edilirse, dosya seçildikten sonra Upload işlemi için düğmeye basıldığında FileUpload kontrolü üzerinde, seçilen dosyaya ilişkin çeşitli ek bilgilere ulaşılabildiği görülür.</p>
<p><img src="/makale/images/mk218_5.gif" alt="" width="636" height="413" border="0" /></p>
<p>Söz gelimi dosyanın tipi hakkında fikir elde etmek için <strong>ContentType</strong> özelliğine bakılabilir. Buna göre belirli dosya tiplerinin indirilmesine izin verilmesi istenen durumlara karşı tedbirler alınabilir. Gönderilecek dosyanın boyutu ile ilgili olarak bir kısıtlama getirilmek isteniyorsa içerik uzunluğu <strong> ContentLength</strong> özelliği ile tedarik edilerek gerekli değişiklikler yapılabilir. Şimdi örneği deniyerek devam edelim. Basit olarak bir döküman dosyasını aşağıdaki gibi seçip Upload etmeyi deneyeceğiz.</p>
<p><img src="/makale/images/mk218_6.gif" alt="" width="583" height="595" border="0" /></p>
<p>Görüldüğü üzere Browse işleminden sonra otomatik olarak standart <strong>Choose File</strong> iletişim penceresi (Dialog Box) ile karşılaştık. Örnekte bir resim dosyası seçilmiştir. Seçim işleminden sonra Gönder düğmesine basılırsa dosyanın başarılı bir şekilde Dokumanlar klasörü altına yazıldığı görülecektir.</p>
<blockquote>
<p>Upload işlemleri sırasında yetki problemi nedeni ile IIS altındaki herhangibir klasöre dosya yazma işlemi sırasında hata mesajı alınabilir. Bu durumda <strong>ASPNET</strong> kullanıcısına ilgili klasöre yazma hakkı verilmesi gerekebilir.</p>
</blockquote>
<p>Upload işlemleri sırasında dikkat edilmesi gereken kritik bir sayı vardır. <strong>4096 byte</strong>. Yani 4 megabyte. Boyutu bu değerin üzerindeki bir dosyayı Upload etmek istediğimizde Asp.Net ortamı bir hata üretir ve dosyanın sunucuya gönderilmesine izin vermez. Ne yazıkki hata üretimi kodların işletilmesinden önce gerçekleşir. Bu nedenle kullanıcıya anlamlı bir mesaj gösterilmeside pek mümkün olmamamaktadır. Eğer 4 megabyte üzerinde dosyaların upload edilebilmesini başka bir deyişle izin verilen limite kadar olan dosyaların gönderilebilmesini istiyorsak <strong>web.config</strong> dosyası içerisinde <strong>httpRuntime</strong> elementinin ayarlanması gerekmektedir. <strong>system.web</strong> boğumu içerisinde yer alan httpRuntime elementi sayesinde, http çalışma zamanına ait çeşitli ayarlamalar yapılabilmektedir. Bizim ihtiyacımız olan dosya büyüklüğü sınıfı ayarı için örneğimizde <strong> web.config</strong> dosyasını aşağıdaki gibi düzenlememiz yeterlidir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<httpRuntime maxRequestLength="51200"/>
<compilation debug="true"/>
<authentication mode="Windows"/>
</system.web>
</configuration></pre>
<p>Burada yapılan ayarlamaya göre istemciler 50 megabyte' a kadar dosyaları sunucuya gönderebilecektir. Upload işlemlerini içerik yönetim sistemlerinde resim, döküman veya örnek uygulamaların, programların gönderilmesinde, grafik kütüphanesi tarzındaki sistemlerde çeşitli formatta resim veya akıcı görüntü formatlarının gönderilmesinde ve buna benzer durumlarda kullanmak yaygındır.</p>
<p>Gelelim makalemizin üçüncü konusuna. Yine istemciden sunucuya doğru bir dosya gönderme işlemi gerçekleştirmeyi hedeflediğimizi düşünelim. Ancak bu sefer XML tabanlı bir dosyayı gönderiyor olacağız. Bu dosyanın özelliği, bizim istediğimiz şekilde tasarlanmış olması dışında, sunucu tarafında anında işlenecek olmasıdır. Söz gelimi, XML dosyası içerisinde dinamik olarak sayfaya yüklenmesi istenen kontrollere ait bilgiler yer alabilir. Bu durumda Upload edilen XML dosyasının sunucu tarafında işlenerek bir çıktı üretilmesi gerekmektedir. Bir başka deyişle istemciden sunucuya gönderilen sayfayı, fiziki olarak yazmadan işlemek ve kullanmak istediğimizi göz önüne alıyoruz. Peki bu sistemi nasıl yazabiliriz? İlk olarak istemcinin göndereceği basit XML dökümanını hazırlayarak başlayalım. Örnek olarak aşağıdaki gibi bir içerik düşünülebilir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8"?>
<Kontroller>
<Kontrol tip="MetinKutusu" id="metinKutusu1"/>
<Kontrol tip="Dugme" metin="Gonder" id="gonder1"/>
<Kontrol tip="Label" metin="Ad" id="ad1"/>
</Kontroller></pre>
<p>Olayı basit bir şekilde ele almak adına Xml içeriğini mümkün olduğu kadar basit düşünmeye çalıştık. Elbetteki çok daha karmaşık ve daha çok parametre sunan bir Xml dökümanı söz konusu olabilir. Şimdi bu dosyayı nasıl ele alacağımıza bakalım. Dikkat edilmesi gereken noktalardan birisi, Upload edilecek dökümanın <strong>XML</strong> formatında olması gerekliliğidir. Bunu sağlamak için, içerik tipine<strong>(ContentType)</strong> bakmak gerekecektir. Sonrasında ise Upload edilen dosyanın Framework içerisinde yer alan XML tipleri yardımıyla ele alınması yeterlidir. Sonuç olarak Default3.aspx dosyamızın içeriği aşağıdaki gibi olacaktır.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" %>
<%@ Import Namespace="System.Xml" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnGonder_Click(object sender, EventArgs e)
{
if (uplKontroller.HasFile)
{
HttpPostedFile dosya = uplKontroller.PostedFile;
if (dosya.ContentType == "text/xml")
{
XmlDocument doc = new XmlDocument();
doc.Load(dosya.InputStream);
XmlNodeList kontroller=doc.GetElementsByTagName("Kontrol");
foreach (XmlNode kontrol in kontroller)
{
switch (kontrol.Attributes["tip"].Value)
{
case "Dugme":
Button btn = new Button();
btn.ID = kontrol.Attributes["id"].Value;
btn.Text = kontrol.Attributes["metin"].Value;
phlKontroller.Controls.Add(btn);
break;
case "MetinKutusu":
TextBox txt = new TextBox();
txt.ID = kontrol.Attributes["id"].Value;
phlKontroller.Controls.Add(txt);
break;
case "Label":
Label lbl = new Label();
lbl.ID = kontrol.Attributes["id"].Value;
lbl.Text = kontrol.Attributes["metin"].Value;
phlKontroller.Controls.Add(lbl);
break;
}
}
}
else
Response.Write("Dosya içeriği XML olmalıdır");
}
else
Response.Write("Dosya bulunamadı");
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Upload Edilen Dosyayi O Anda Islemek</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Xml Dosayasını Seçin : <asp:FileUpload ID="uplKontroller" runat="server" />
<br />
<asp:Button ID="btnGonder" Text="Gönder" runat="server" OnClick="btnGonder_Click" />
<br />
Yüklenen Kontroller:
<asp:PlaceHolder ID="phlKontroller" runat="server" />
</div>
</form>
</body>
</html></pre>
<p>Dilerseniz kod içerisinde neler yaptığımıza kısaca bakalım. İlk olarak düğmeye basıldığında HasFile özelliği ile seçilen bir dosya olup olmadığını kontrol ediyoruz. Sonrasında ise FileUpload kontrolünün <strong>PostedFile</strong> özelliğinden yararlanıp seçilen dosyaya ait bazı temel bilgileri taşıyan <strong>HttpPostedFile</strong> tipine ait nesne örneğini elde ediyoruz. Bu nesne örneği üzerinden <strong> ContentType</strong> özelliğine geçerek içeriğin <strong>XML</strong> olup olmadığını <strong>text/xml</strong> eşleştirmesi ile kontrol ediyoruz.</p>
<p>Sonrasında ise okunan dosya içeriğini XmlDocument nesnesine yüklüyoruz. <strong>XmlDocument</strong> nesne örneğine ait Load metodunun parametresi olarak bir Stream kullanılabildiğinden, <strong>HttpPostedFile</strong> nesne örneğinin <strong> InputStream</strong> özelliğinden yararlanıyoruz. Son olarak yüklenen XML dökümanı içerisinde dolaşarak ilgili nitelikleri okuyor ve dinamik olarak oluşturulan kontrolleri, sayfadaki <strong>PlaceHolder</strong> kontrolünün Controls koleksiyonuna ekliyoruz.</p>
<blockquote>
<p>İşlemlerin daha sağlıklı olması açısından yüklenen XML içeriğinin belirli kurallara uygun olup olmadığı bir şema dosyası (<strong>XSD</strong> olabilir örneğin) yardımıyla kontrol edilebilir. Söz gelimi gelen XML dökümanının yapısı, element adları, nitelik adları veya tipleri bu şema yardımıyla denetlenip, kontrollerin belirli standartlara göre okunabilmesi sağlanmış olunur. Şema kontrolünün nasıl yapılabileceğine dair daha önceki bir <a href="http://www.bsenyurt.com/MakaleGoster.aspx?ID=172">makalemizden</a> yararlanabilirsiniz.</p>
</blockquote>
<p>Uygulamayı çalıştırdığımızda ve istemci tarafından Kontrollerim.xml dosyasını yüklediğimizde aşağıdaki ekran görüntüsünde olduğu gibi kontrollerin başarılı bir şekilde üretilip sayfaya yüklendiğini görebiliriz.</p>
<p><img src="/makale/images/mk218_7.gif" alt="" width="419" height="222" border="0" /></p>
<p>Sıra geldi makalemizin son konusuna. İçerik yönetimi adına, istemcinin sunucuya gönderdiği dosyaların veritabanı üzerindeki bir tabloda tutulması istenebilir. Eğer ilişkisel veritabanı sistemi <strong>(Relational Database Management System)</strong> söz konusu ise, dosyaların tabloda tutuluyor olması taşıma işlemlerini kolaylaştırabileceği gibi, içerik güvenliğininde de daha etkin bir seviyede yapılabilmesini sağlayacaktır. <em>(Tabi tersine saklanacak dosya boyutlarına göre veritabanı daha hızlı şişecektir.)</em> Bu tarz bir ihtiyacın çıkış noktası son derece basittir.</p>
<p>Her zaman olduğu gibi bir <strong>FileUpload</strong> kontrolü ile istemciye dosya seçtirilmeli daha sonra ilgili içerik sunucu tarafında işlenerek veritabanındaki ilgili tabloya yazdırılmalıdır. Burada çalıştıralacak komut dışında tablodaki alan tipide önemlidir. <strong>Text</strong> tabanlı bir içerik gönderilecek olsada, tablo tarafında <strong>image</strong> veya <strong>VarBinary</strong> tipinden alanlar tutmak Unicode tutarlılığı açısından daha doğru bir yaklaşım olacaktır. Dilerseniz bir örnek ile bu durumu incelemeye çalışalım. İlk olarak içeriği saklayacağımız basit bir tablo oluşturalım. Bunun için SQL Server 2005 üzerinde aşağıdaki gibi bir tablo göz önüne alınabilir.</p>
<p><img src="/makale/images/mk218_8.gif" alt="" width="598" height="351" border="0" /></p>
<p>Dosyalar isimli tabloda, sunucuya gönderilen dosya içeriğini saklamak için <strong>image</strong> tipinden bir alan kullanılmaktadır. Bunlara ek olarak dosyanın eklenme tarihi,içeriğin tipi ve dosya adı bilgileride yer almaktadır. Söz konusu alanlar dışında, web sitesinde kullanılan <strong>doğrulama(Authentication)</strong> sistemine göre, <strong>Upload</strong> işlemini yapan kullanıcının bilgilerinin saklanması hatta varsa <strong>Membership</strong> gibi kullanıcı tablo sistemleri ile ilişkilendirilmeside mümkün olabilir. Gelelim yükleme işlemini tabloya yazacak kodlarımıza.</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnGonder_Click(object sender, EventArgs e)
{
if (uplDosya.HasFile)
{
string conStr = ConfigurationManager.ConnectionStrings["ConStr"].ConnectionString;
using (SqlConnection conn = new SqlConnection(conStr))
{
SqlCommand cmd = new SqlCommand("Insert into Dosyalar (EkleNmeTarihi,DosyaIcerigi,DosyaAdi,IcerikTipi) Values (@EklenmeTarihi,@DosyaIcerigi,@DosyaAdi,@IcerikTipi)", conn);
cmd.Parameters.AddWithValue("@EklenmeTarihi", DateTime.Now);
cmd.Parameters.AddWithValue("@DosyaIcerigi", uplDosya.FileBytes);
cmd.Parameters.AddWithValue("@DosyaAdi", uplDosya.FileName);
cmd.Parameters.AddWithValue("@IcerikTipi", uplDosya.PostedFile.ContentType);
conn.Open();
int sonuc=cmd.ExecuteNonQuery();
Response.Write(sonuc + " dosya aktarıldı");
}
}
else
Response.Write("Dosya seçmelisiniz");
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Upload Edilen Dosyayı Veritabanına Yazma</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Dosyayı Seçin : <asp:FileUpload ID="uplDosya" runat="server" />
<br />
<asp:Button ID="btnGonder" Text="Gönder" runat="server" OnClick="btnGonder_Click" />
</div>
</form>
</body>
</html></pre>
<p>Yine kodları kısaca incelemekte fayda var. Her zamanki gibi, istemcinin bir dosya seçtiğinden emin olduktan sonra gerekli işlemleri yapıyoruz. Burada önemli olan nokta çalıştırılacak SQL cümlesinde kullanılan parametrelerin değerlerinin nasıl verildiği. Dikkat edilecek olursa, image tipindeki alanın içeriğini verirken <strong>FileUpload</strong> kontrolünün <strong>FileBytes</strong> özelliğinden yararlanıyoruz. Burada dikkat edilmesi gereken noktalardan biriside çok büyük boyutlu dosyaların aktarılması sırasında yaşanabilecek <strong>timeout</strong> sorunudur.</p>
<p>Böyle bir durumda kalındığı takdirde bağlantı için timeout sürelerinin arttırılması yoluna gidilebilir yada dosyanın parçalanarak sunucuya gönderilmesi ve burada o şekilde ele alınması sağlanabilir. Uygulama çeşitli tipteki dosyalar ile test edildiğinde başarılı bir şekilde çalıştığı görülecektir. Aşağıdaki ekran görüntüsünde bir kaç dosya tipinin upload edilmesi sonrasındaki durum vurgulanmaktadır.</p>
<p><img src="/makale/images/mk218_10.gif" alt="" width="602" height="190" border="0" /></p>
<p>Her dosya eklenme işleminden sonrada tarayıcı penceresindeki görüntü aşağıdaki gibi olacaktır.</p>
<p><img src="/makale/images/mk218_9.gif" alt="" width="379" height="231" border="0" /></p>
<p>Elbette Upload edilen içeriklerin, istemciler tarafından indirilmeside gerekecektir. Bu durumda tablo alanındaki dosya içeriğinin stream olarak yazdırılması gerekir. Tabi bunun için <strong>Response</strong> sınıfının <strong>WriteFile</strong> metodu yerine <strong> BinaryWrite</strong> metodunu tercih edeceğiz. <em>(Alternatif bir yaklaşım olarak, tablodan okunan dosya içeriğinin bir temp dosyaya atılması ve oradanda WriteFile metodu ile yazdırılmasıda düşünülebilir) </em>Nitekim dosya içerikleri tabloda <strong>binary</strong> olarak tutulmaktadır. Öyleyse son olarak bu işlemide nasıl yapabileceğimizi inceleyeceğimiz bir örnek sayfa daha ekleyelim. Default5.aspx sayfamızın içeriği aşağıdaki gibi olacaktır.</p>
<p><img src="/makale/images/mk218_11.gif" alt="" width="429" height="285" border="0" /></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConStr"].ConnectionString))
{
SqlCommand cmd=new SqlCommand("SELECT DosyaAdi,DosyaIcerigi,IcerikTipi FROM Dosyalar Where Id=@Id",conn);
cmd.Parameters.AddWithValue("@Id", GridView1.SelectedValue);
conn.Open();
SqlDataReader reader=cmd.ExecuteReader();
if (reader.Read())
{
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + reader.GetString(0));
byte[] dosyaIcerigi = reader.GetSqlBinary(1).Value;
Response.AddHeader("Content-Length", dosyaIcerigi.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(dosyaIcerigi);
}
reader.Close();
}
Response.End();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Dosyalar<br />
<br />
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="dsDosyalar" OnSelectedIndexChanged="GridView1_SelectedIndexChanged" DataKeyNames="Id">
<Columns>
<asp:BoundField DataField="DosyaAdi" HeaderText="DosyaAdi" SortExpression="DosyaAdi" />
<asp:BoundField DataField="EklenmeTarihi" HeaderText="EklenmeTarihi" SortExpression="EklenmeTarihi" />
<asp:BoundField DataField="IcerikTipi" HeaderText="IcerikTipi" SortExpression="IcerikTipi" />
<asp:CommandField ButtonType="Button" SelectText="indir" ShowSelectButton="True" />
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="dsDosyalar" runat="server" ConnectionString="<%$ ConnectionStrings:ConStr %>" SelectCommand="SELECT Id,DosyaAdi, EklenmeTarihi, IcerikTipi FROM Dosyalar">
</asp:SqlDataSource>
</div>
</form>
</body>
</html></pre>
<p>Tasarladığımız sayfayı dilerseniz inceleyelim. Basit olarak Dosyalar tablosunun içeriğini göstermek amacıyla <strong>SqlDataSource</strong> ve <strong>GridView</strong> kontrollerinden faydalanıyoruz. Yine ilk örneğimizde olduğu gibi indirme işlemini başlatmak adına küçük hilemizi yaptık ve bir Select düğmesi kullandık. Burada dosya içeriği tabloda alan olarak tutulduğu için, <strong>SqlCommand</strong> ile verinin çekilmesi gerekiyor. Bunu kolaylaştırmak adına GridView içerisinde seçilen satıra ait Id alanının değerini almalıyız. Bu amaçla GridView kontrolünün <strong>DataKeyNames</strong> özelliğine Id değerini verdik.</p>
<p>Bu değeri <strong>SelectedIndexChanged</strong> metodu içerisinden alarak sorgu cümlesinde parametre olarak kullanıyor ve böylece indirilmek istenen dosyaya ait içeriğin olduğu tablo satırını çekebiliyoruz. Bizim için önemli olan nokta, <strong>binary</strong> içeriği okumak için <strong>SqlDataReader</strong> sınıfının <strong>GetSqlBinary</strong> metodunu kullanıyor olmamız. Bu metod ile dönen tipin <strong>Value</strong> özelliğinden faydalanıp elde edilen <strong>byte[] </strong>dizisini <strong>Response</strong> sınıfının <strong>BinaryWrite</strong> metoduna parametre olarak verdiğimizde yazma işlemi gerçekleştirilmiş oluyor. Sonuç olarak çalışma zamanında istediğimiz sonuca ulaşıyor ve dosya indirme işlemlerini gerçekleştirebiliyoruz.</p>
<p><img src="/makale/images/mk218_12.gif" alt="" width="625" height="527" border="0" /></p>
<p>Bu makalemizde Asp.Net uygulamalarında <strong>Download</strong> ve <strong>Upload</strong> işlemlerini detayları ile incelemeye çalıştık. İlk olarak bir dosyanın indirilme işleminin nasıl yapılabileceğine baktık. Sonrasında ise basit olarak bir Upload işlemi ile sunucuya dosya gönderme olayını ele aldık. Upload işleminin farklı yönlerini ele almak adına, anında sunucu tarafında işleme ve tabloya satır olarak ekleme işlemlerini inceledikten sonra, tablodaki bir binary içeriği indirme sürecine göz attık. Böylece geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/makale/images/AspNetIOIslemleri.zip">Örnek Uygulama için Tıklayın</a> <em>(Dosyanın çok yer tutmaması açısından mdf dosyası çıkartılmıştır)</em></p>2007-08-15T06:00:00+00:00asp.netdownloaduploadbsenyurtTatile çıkan herkes, iyi ve dinlendirici geçen günlerin ardından tekrar hayatın akışına kapıldığında kısa süreliğinede olsa adaptasyon problemi yaşar. Tatildeyken hatırlayacağınız gibi hafif ve dinlendirici bir Asp.Net konusu ile ilgilenmeye çalışmıştık...https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=56c636c7-aceb-4266-b1a0-ceff6b389e6313https://www.buraksenyurt.com/trackback.axd?id=56c636c7-aceb-4266-b1a0-ceff6b389e63https://www.buraksenyurt.com/post/Asp-Net-Temelleri-Derinlemesine-DownloadUpload-Islemleri-bsenyurt-com-dan#commenthttps://www.buraksenyurt.com/syndication.axd?post=56c636c7-aceb-4266-b1a0-ceff6b389e63