https://www.buraksenyurt.com/Burak Selim Şenyurt - Asp.Net 4.02016-08-08T07:33:08+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/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/Birlikte-GelistirdikBirlikte Geliştirdik2011-10-02T23:48:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/corba_739.jpg"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border: 0px;" title="corba_739" src="/pics/corba_739_thumb.jpg" alt="corba_739" width="240" height="240" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>Eğer benim gibi yaz kış kolayca grip oluyorsanız eminim ki bol limonlu çorbalara aşinasınızdır. Hele ki şanslıysanız ve eşinizin ya da annenizin yanındaysanız şöyle evde var olan tüm sebzelerden oluşan karma bir sebze çorbası süper rahatlatıcı olacaktır. E bazen .Net Framework tarafında da eldeki materyalleri bir araya getirip güzel bir çorba yapmak gerekir <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_67.png" alt="Göz kırpan gülümseme" /> Güzel bir çorba hazırlamaya ne dersiniz?</p>
<p>Hazırlayacağımız çorbamızda çok kıymetli yardımcılarımız da var. Son katılımcılarımız ile gerçekleştirmekte olduğumuz <strong>Asp.Net</strong> eğitiminden çok güzel fikirler ve örnekler çıkmaya devam ediyor. Geliştireceğimiz örnek <strong>Solution</strong> içerisinde <strong>Entity Framework, WCF Service, LINQ, Asp.Net Web Application, Web User Control, LINQ</strong> gibi pek çok kavram yer almakta. Temel olarak başlangıçtaki senaryomuz ise şu : <em>“Asp.Net web uygulamamızda yer alan bir Web User Control’ ümüz, AdventureWorks veritabanında yer alan herhangibir Product' satırına ait bazı alan bilgilerini gösterecek”</em></p>
<p>Pekala bu iş için hemen bir <strong>Web User Control</strong> geliştirebilir ve <strong>Load</strong> metodunda ilgili veri çekme işlemlerini gerçekleştirerek basitçe sonuca gitmeyi düşünebilirsiniz. Ama biz bu şekilde button arkası programlama yapmamayı tercih ediyoruz <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_16.png" alt="Gülümseme" /> Bunun yerine aşağıdaki grafikte yer alan Solution içeriğini üreterek ilerleyeceğiz. <em>(Dilerseniz yazının son satırından Solution’ ı indirip inceleyin)</em></p>
<p><a href="https://www.buraksenyurt.com/pics/bei_10.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_10" src="/pics/bei_10_thumb.gif" alt="bei_10" width="268" height="549" border="0" /></a></p>
<p><img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_16.png" alt="Gülümseme" /> İlk olarak <strong>AdventureWorks</strong> veritabanındaki bazı tabloları içerecek olan <strong>Entity Model’</strong> imizi tasarlayarak işe başlayalım. Söz konusu <strong>Class Library</strong> projesi içerisinde kullanacağımız <strong>Entity Model</strong> diagramının içeriğini ise başlangıçta aşağıdaki gibi oluşturabiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_11.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_11" src="/pics/bei_11_thumb.gif" alt="bei_11" width="530" height="155" border="0" /></a></p>
<p>Şimdilik <strong>Product</strong> ve <strong>ProductSubCategory</strong> tablolarının karşılığı olan Entity içeriklerinin yer aldığı bir diagram ile karşı karşıyayız. Diğer yandan<strong> Web User Control’</strong> ümüzü geliştirene kadar kat etmemiz gereken daha çok yolumuz olacak. Öncelikle bir <strong>Product</strong> içeriğini dış ortama verecek olan iş fonksiyonelliklerini içeren bir katman daha yazacağız. Söz konusu katman tahmin edeceğiniz üzere bir <strong>Class Library</strong> olacak. Bu katmanda ekstradan bir<strong> Business Object</strong> daha kullanıyor olacağız. <em>Çünkü <strong>Product</strong> tipinin tüm içeriğini dış ortama sunmak gibi bir niyetimiz olmadığını düşünmekteyiz.</em> Bu sebepten <strong>Product</strong> tipi yerine geçecek olan bir<strong> Surrogate Type</strong> kullanımını ele alacağız. Dolayısıyla <strong>Company.Operations</strong> isimli <strong>Class Library</strong> içeriğini aşağıdaki sınıf çizelgesinde olduğu gibi tasarlayarak devam ediyoruz.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_12.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_12" src="/pics/bei_12_thumb.gif" alt="bei_12" width="364" height="323" border="0" /></a></p>
<p>Burada görülen <strong>ProductBusinessObject</strong> aslında dış ortama sunulacak olan <strong>Product</strong> içeriğini taşıyan bir <strong>POCO</strong> nesnesidir. Aslında bu tip <strong>Business Object</strong> türlerini başka bir katman içerisinde tutmayı da düşünebiliriz <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_67.png" alt="Göz kırpan gülümseme" /> </p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace Company.Operations
{
public class ProductBusinessObject
{
public int ProductId { get; set; }
public string Name { get; set; }
public decimal ListPrice { get; set; }
public string SubCategoryName { get; set; }
public string Color { get; set; }
}
}</pre>
<p><strong>ProductionOperations</strong> isim sınıfımız ise şu an için tek bir operasyon sunmaktadır ve <strong>ProductId</strong> değerine göre <strong>ProductBusinessObject</strong> tipinden bir referans döndürmek üzere tasarlanmıştır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Linq;
using AdventureWorksEntity;
namespace Company.Operations
{
public class ProductionOperations
{
public ProductBusinessObject GetProduct(int ProductId)
{
ProductBusinessObject result = null;
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
result = (from p in context.Products
where p.ProductID == ProductId
select new ProductBusinessObject
{
ProductId=p.ProductID,
Name=p.Name,
ListPrice=p.ListPrice,
Color=p.Color,
SubCategoryName=p.ProductSubcategory.Name
}).FirstOrDefault<ProductBusinessObject>();
}
return result;
}
}
}</pre>
<p>Örneğimizi <strong>EF 4.0</strong> versiyonu ile geliştirdiğimiz için bir <strong>Lazy Loading</strong> durumunun da söz konusu olduğunu ifade edelim. <strong>SubCategoryName</strong> özelliğine değer atanan satıra dikkat edin <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_67.png" alt="Göz kırpan gülümseme" /></p>
<p>Artık söz konusu iş operasyonunu dış dünyaya sunacak bir uygulamayı da geliştirebiliriz. Bu uygulama favorimiz olan <strong>WCF</strong> tabanlı bir servis olarak düşünülmektedir. Böylece <strong>Entity</strong> modelimiz, <strong>Business Object’</strong> lerimiz ve <strong>Business Operasyonlarımızın</strong> tamamı söz konusu servis uygulamasının arkasında kalacaktır. Yani asıl End User’ lar bu işlevselliklere erişebilmek için servis katmanı üzerinden geçmek zorunda kalacaklardır. WCF Service uygulamamızın içeriği aşağıdaki diagramda görüldüğü gibidir.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_13.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_13" src="/pics/bei_13_thumb.gif" alt="bei_13" width="355" height="329" border="0" /></a></p>
<p><strong>IProductionService</strong> isimli <strong>servis sözleşmemiz(Service Contract)</strong> şu şekildedir;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.ServiceModel;
using Company.Operations;
namespace AdventureWorksService
{
[ServiceContract]
public interface IProductionService
{
[OperationContract]
ProductBusinessObject GetProduct(int ProductId);
}
}</pre>
<p>Sözleşmemiz senaryomuza göre bir ürünün elde edilmesi ve <strong>ProductBusinessObject</strong> tipinden geriye döndürülmesi işlemini üstlenen basit bir operasyon sunmaktadır. Bu operasyonun uyarlaması ise <strong>ProductionService</strong> sınıfı içerisinde gerçekleştirilmektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using Company.Operations;
namespace AdventureWorksService
{
public class ProductionService
: IProductionService
{
ProductionOperations productionOperator = new ProductionOperations();
public ProductBusinessObject GetProduct(int ProductId)
{
return productionOperator.GetProduct(ProductId);
}
}
}</pre>
<p>Servis uygulamamızda dikkat edilmesi gereken noktalardan birisi de, <strong>AdventureWorks</strong> entity modelini kullanabilmesi için gerekli <strong>Connection String</strong> bilgisine sahip olma zorunluluğudur. Bildiğiniz üzere Entity Modelimizi bir <strong>Class Library</strong> içerisinde tutumaktayız ve çalışma zamanında ilgili mapping işlemlerini üstlenecek olan yürütücü uygulamanın söz konusu mapping için bir bağlantı bilgisine sahip olması gerekiyor. Dolayısıyla <strong>AdventureWorksEntity</strong> kütüphanesinde otomatik olarak üretilen <strong>connection string</strong> bilgisinin servis uygulamasındaki <strong>web.config</strong> dosyasında yer alması şart.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="AdventureWorksEntities" connectionString="metadata=res://*/AdventureModel.csdl|res:
//*/AdventureModel.ssdl|res://*/AdventureModel.msl;
provider=System.Data.SqlClient;provider connection string="Data Source=.;Initial Catalog=AdventureWorks;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" />
</connectionStrings>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration></pre>
<p>Çorbamıza yeni baharatlar katarak devam edelim <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_67.png" alt="Göz kırpan gülümseme" /></p>
<p>Artık servis uygulamamız da hazır olduğuna göre bunu kullanacak olan <strong>Asp.Net Web</strong> <strong>Uygulamasını</strong> geliştirerek yol alabiliriz. Tahmin edileceği üzere Asp.Net Web uygulamamız, <strong>AdventureWorksService</strong> isimli <strong>WCF Servisini</strong> referans etmelidir.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_17.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_17" src="/pics/bei_17_thumb.gif" alt="bei_17" width="282" height="225" border="0" /></a></p>
<p>Yazımızın başında belirttiğimiz senaryomuzdan hatırlayacağınız üzere bir <strong>Web User Control’</strong> den bahsediyorduk. Bu <strong>Web User Control’</strong> ün <strong>Business Object’</strong> imize göre tasarlanması gerekiyor. Bu amaçla aşağıdaki gibi bir tasarım gerçekleştirebiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_14.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_14" src="/pics/bei_14_thumb.gif" alt="bei_14" width="419" height="267" border="0" /></a></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ProductControl.ascx.cs" Inherits="CompanyPortal.ProductControl" %>
<style type="text/css">
.style1
{
width: 400px;
font-family: "Courier New", Courier, monospace;
}
.style2
{
width: 176px;
}
.style3
{
width: 176px;
font-weight: bold;
}
</style>
<table border="1" cellpadding="5" cellspacing="1" class="style1">
<tr>
<td class="style2">
</td>
<td style="text-align: right">
<asp:Label ID="ProductIdLabel" runat="server"></asp:Label>
</td>
</tr>
<tr>
<td class="style3">
Name</td>
<td>
<asp:Label ID="ProductNameLabel" runat="server"></asp:Label>
</td>
</tr>
<tr>
<td class="style3">
List Price</td>
<td>
<asp:Label ID="ProductListPriceLabel" runat="server"></asp:Label>
</td>
</tr>
<tr>
<td class="style3">
Color</td>
<td>
<asp:Label ID="ProductColorLabel" runat="server"></asp:Label>
</td>
</tr>
<tr>
<td class="style3">
Sub Category Name</td>
<td>
<asp:Label ID="ProductSubCategoryNameLabel" runat="server"></asp:Label>
</td>
</tr>
</table></pre>
<p>Kod tarafında ise aşağıdaki işlemleri gerçekleştirebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using CompanyPortal.Production;
namespace CompanyPortal
{
public partial class ProductControl
: System.Web.UI.UserControl
{
ProductionServiceClient proxy = new ProductionServiceClient();
int productId;
public int ProductId
{
get { return productId; }
set { productId = value; }
}
public string ProductName
{
get { return ProductNameLabel.Text; }
}
public decimal ProductListPrice
{
get { return Decimal.Parse(ProductListPriceLabel.Text); }
}
public string ProductColor
{
get { return ProductColorLabel.Text; }
}
public string ProductSubCategoryName
{
get { return ProductSubCategoryNameLabel.Text; }
}
public void LoadContent()
{
ProductBusinessObject bO = proxy.GetProduct(ProductId);
if (bO != null)
{
ProductIdLabel.Text = bO.ProductId.ToString();
ProductNameLabel.Text = bO.Name;
ProductListPriceLabel.Text = bO.ListPrice.ToString();
ProductColorLabel.Text = bO.Color;
ProductSubCategoryNameLabel.Text = bO.SubCategoryName;
}
}
}
}</pre>
<p>Dikkat edileceği üzere <strong>Web User Control</strong> herhangibir ürün numarasına bağlı bir <strong>ProductBusinessObject</strong> içeriğini dış ortama <strong>özellikler(Properties)</strong> yardımıyla da sunmaktadır. Ayrıca içerisinde yer alan <strong>Label</strong> kontrollerinin çalışma zamanında yüklenmesi için <strong>LoadContent</strong> isimli bir metoddan yararlanılmaktadır.</p>
<p>Artık <strong>Web User Control</strong> bileşenimizi örnek bir <strong>aspx</strong> sayfasında kullanmaya çalışarak ilk testimizi gerçekleştirebiliriz. Bunun için aşağıdaki tasarıma ve kod içeriğine sahip bir sayfa eklediğimizi düşünelim.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_15.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_15" src="/pics/bei_15_thumb.gif" alt="bei_15" width="290" height="155" border="0" /></a></p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CompanyPortal.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
Ürün Numarasını Girin :
<asp:TextBox ID="ProductIdTextBox" runat="server"></asp:TextBox>
<br />
<br />
<asp:Button ID="GetProductButton" runat="server"
onclick="GetProductButton_Click" Text="Get Product" />
<br />
<br />
<asp:PlaceHolder ID="ProductPlaceHolder" runat="server"></asp:PlaceHolder>
</div>
</form>
</body>
</html></pre>
<p>ve kod içeriği;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
namespace CompanyPortal
{
public partial class Default : System.Web.UI.Page
{
protected void GetProductButton_Click(object sender, EventArgs e)
{
int productId;
if (Int32.TryParse(ProductIdTextBox.Text, out productId))
{
ProductControl prdControl = LoadControl("ProductControl.ascx") as ProductControl;
if (prdControl != null)
{
prdControl.ProductId = productId;
prdControl.LoadContent();
ProductPlaceHolder.Controls.Add(prdControl);
}
}
}
}
}</pre>
<p>Sayfamızda sembolik olarak <strong>TextBox</strong> kontrolüne girilen sayısal değere göre <strong>Product</strong> bilgisinin çekilmesi ve bulunan ürüne ait bazı bilgilerin dinamik olarak yüklenen <strong>Web User Control</strong> içerisinde gösterilmesi sağlanmaktadır. Söz gelimi çalışma zamanında <strong>774</strong> numaralı ürüne ait bilgileri çekmek istersek aşağıdaki ekran görüntüsünde yer alan sonucu elde ederiz <img class="wlEmoticon wlEmoticon-openmouthedsmile" style="border-style: none;" src="/pics/wlEmoticon-openmouthedsmile_16.png" alt="Açık ağızlı gülümseme" /></p>
<p><a href="https://www.buraksenyurt.com/pics/bei_16.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_16" src="/pics/bei_16_thumb.gif" alt="bei_16" width="429" height="335" border="0" /></a></p>
<p>Eğer örneği buraya kadar başarılı bir şekilde getirdiyseniz şöyle bir arkanıza yaslanın ve neler yaptığımızı bir düşünün <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_67.png" alt="Göz kırpan gülümseme" /></p>
<p>Son olarak <strong>Solution</strong> içerisinde yer alan <strong>Assembly’</strong> lar arasındaki ilişkiyi göstererek yazımızı yavaş yavaş sonlandıralım.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_9.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_9" src="/pics/bei_9_thumb.gif" alt="bei_9" width="640" height="200" border="0" /></a></p>
<p>Örneği geliştirmek, farklı testler uygulayarak olası hataları düzeltmek, kodu tekrardan gözden geçirip iyileştirmek de sizin göreviniz olsun. Söz gelimi bir alt kategoriye bağlı ürünleri <strong>ProductBusinessObject</strong> tipinden çekip her biri için dinamik olarak <strong>ProductControl</strong> bileşeni üreten ve web sayfasına ekleyen bir senaryoyu çözümümüze ilave edebilirsiniz. Hatta alt kategori bilgilerinin Web uygulaması arayüzüne dahil edilmesi için gerekli geliştirmeleri de yapabilirsiniz. Eğer buraya kadar yaptıklarımızla kafanız çok karıştıysa aşağıdaki parçayı dinleyip kendinize gelmeyi de deneyebilirsiniz <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_16.png" alt="Gülümseme" /></p>
<p><object width="250" height="40">
<param name="movie" value="http://grooveshark.com/songWidget.swf" />
<param name="wmode" value="window" />
<param name="allowScriptAccess" value="always" />
<param name="flashvars" value="hostname=cowbell.grooveshark.com&songIDs=32204406&style=wood&p=0" /><embed type="application/x-shockwave-flash" width="250" height="40" src="http://grooveshark.com/songWidget.swf" wmode="window" allowscriptaccess="always" flashvars="hostname=cowbell.grooveshark.com&songIDs=32204406&style=wood&p=0" />
</object></p>
<p>Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2011%2f10%2fCompany.rar">Company.rar (153,21 kb)</a></p>2011-10-02T23:48:00+00:00web user controldynamic control loadingentity frameworklinqwcfserviceentity modelbsenyurtHazırlayacağımız çorbamızda çok kıymetli yardımcılarımız da var. Son katılımcılarımız ile gerçekleştirmekte olduğumuz Asp.Net eğitiminden çok güzel fikirler ve örnekler çıkmaya devam ediyor. Geliştireceğimiz örnek Solution içerisinde Entity Framework, WCF Service, LINQ, Asp.Net Web Application, Web User Control, LINQ gibi pek çok kavram yer almakta. Temel olarak başlangıçtaki senaryomuz ise şu : “Asp.Net web uygulamamızda yer alan bir Web User Control’ ümüz, AdventureWorks veritabanında yer alan herhangibir Product' satırına ait bazı alan bilgilerini gösterecek”https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=595060d7-fb4b-4fc9-bb48-1a2ad9f901a30https://www.buraksenyurt.com/trackback.axd?id=595060d7-fb4b-4fc9-bb48-1a2ad9f901a3https://www.buraksenyurt.com/post/Birlikte-Gelistirdik#commenthttps://www.buraksenyurt.com/syndication.axd?post=595060d7-fb4b-4fc9-bb48-1a2ad9f901a3https://www.buraksenyurt.com/post/Haydi-Bir-Captcha-Kontrolu-YazalimHaydi Bir Captcha Kontrolü Yazalım2011-09-26T23:11:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/terzi.gif"><img style="margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px; float: right; background-image: none;" title="terzi" src="/pics/terzi_thumb.gif" alt="terzi" width="200" height="278" align="left" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>Şu sıralarda kurumsal bir Asp.Net eğitimi vermekteyim. Eğitim içeriği oldukça geniş ve güzel konuları içermekte. Bunlardan bir tanesi de Asp.Net uygulamalarında <strong>Captcha</strong> doğrulamasının kullanımı. Bildiğiniz üzere web ortamı üzerinde özellikle Form veri girişlerinin yapıldığı senaryolarda akıllı robotların gereksiz yere post atma işlemlerini engellemek çok önemlidir.</p>
<p>Örneğin şu an kullanmakta olduğum <strong>BlogEngine</strong> sürümünde, sisteme monte edilmiş bir <strong>Captcha</strong> kontrolü veya modülü bulunmamakta <em>(Biliyorum son sürüme geçmeliydim <img class="wlEmoticon wlEmoticon-sadsmile" style="border-style: none;" src="/pics/wlEmoticon-sadsmile_6.png" alt="Üzgün gülümseme" />)</em> . Bu nedenle özellikle yorum kısımlarında dünyanın çeşitli bölgelerindeki robot programlarının tacizlerine fazlasıyla maruz kalmaktayım. Anlamsız pek çok bilgiden oluşan spam yorumlar söz konusu.</p>
<p>E tabi diyebilirsiniz ki, "Ya Hocam sen de amma yaptın...Eklesene şu Captcha kontrolünü bloğa...Hayret bişi" <img class="wlEmoticon wlEmoticon-openmouthedsmile" style="border-style: none;" src="/pics/wlEmoticon-openmouthedsmile_14.png" alt="Açık ağızlı gülümseme" /> E ama ne demişler bilirsiniz... Terzi kendi söküğünü dikemezmiş <em>(Aslında ben söküğümü nedense dikmek istemiyorum sanırım. Üşengeçlik bu olsa gerek)</em></p>
<p>Neyse sözü fazla uzatmadan konumuza devam edelim. Asp.Net uygulamalarında form veri girişlerinin yapılabildiği ve post işlemlerinin gerçekleştirilebildiği her ortamda robot saldırılarına maruz kalmamız olasıdır. Bu sebepten formu dolduran kişinin gerçek insan gözüne sahip olduğunu bir şekilde anlamamız ve bunu doğrulatmamız gerekmektedir. İşte Captcha kontrolleri bu noktada devreye girmektedir. Üretilen resim formatlı metinsel içeriklerin o anki HTTP Context verisinden okunması mümkün değildir. En azından şu an için. Dolayısıyla bu içeriği gören birisinin elle giriş yapması gerekmektedir. Bize kalan ise sadece ve sadece Captcha içeriği ile kullanıcının girdiği verinin eşit olup olmadığını kontrol etmektir.</p>
<p>Olayı daha net bir şekilde kavrayabilmek adına örnek bir uygulama üzerinden gitmemizde yarar vardır. Bu amaçla dilerseniz <strong>Visual Studio 2010</strong> ortamı üzerinde basit bir <strong>Web Site</strong> şablonu açarak işe başlayalım. Söz konusu <strong>Captcha</strong> kontrolü esas itibariyle <strong>Drawing</strong> tipleri tarafından çizilen bir resim ve üzerine yerleştirilen bir takım sayısal veya karakter bazlı verilerden oluşmaktadır. Burada bahsedilen çizim işini ise genellikle bir <strong>Handler</strong> tipi üstlenmektedir. İşte Handler içeriğimiz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false"><%@ WebHandler Language="C#" Class="CaptchaHandler" %>
using System;
using System.Web;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Web.SessionState;
public class CaptchaHandler
: IHttpHandler,IReadOnlySessionState
{
public void ProcessRequest (HttpContext context) {
using (Bitmap bmp = new Bitmap(220, 80)) //120X40 uzunluğunda bir Bitmap tanımlıyoruz.
{
using (Graphics painter = Graphics.FromImage(bmp)) // Grafik nesnesi söz konusu alan üzerinde çizim yapabilmek için bmp isimli nesneden üretiliyor
{
painter.Clear(Color.LightGray);
// Font nesnesi üretiliyor.
using (Font writer = new Font("Helvetica", 10, System.Drawing.FontStyle.Bold))
{
string capcthaContent = string.Empty;
// Session nesnesinden Captcha kontrolünün içeriğini oluşturan bilgi alınır
if (context.Session["CaptchaContent"].ToString() != null)
capcthaContent = context.Session["CaptchaContent"].ToString();
// ve bu içerik belirlenen font, fırça rengi bilgileri ile Graphics tipinden olan painter üzerine çizdirilir
painter.DrawString(capcthaContent, writer, Brushes.Black, 3, 3);
using (MemoryStream mStream = new MemoryStream())
{
bmp.Save(mStream, ImageFormat.Gif);
// üretilen captcha resmini HttpContext tipinin Response özelliği üzerinden Binary formatta yazdırarak görünmesini sağlıyoruz.
byte[] bmpBytes = mStream.GetBuffer();
context.Response.ContentType = "image/gif";
context.Response.BinaryWrite(bmpBytes);
}
}
}
}
context.Response.End();
}
public bool IsReusable {
get {
return false;
}
}
}</pre>
<p>Aslında Handler tipinin temel görevi <strong>Captcha</strong> resmini içeriği ile birlikte çizmektir. Çizim işlemi sırasında bir Asp.Net sunucu kontrolünden yararlanılması gerekmektedir. Bu sunucu kontrolü yukarıda yazılmış olan Handler tipinden yararlanacaktır. Söz konusu kontrol içeriğini aşağıdaki gibi oluşturabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
namespace CustomControls
{
public class CaptchaBox
: Control
{
Image capcthaImage;
public string Text
{
get
{
if (HttpContext.Current.Session["CaptchaContent"] != null)
return HttpContext.Current.Session["CaptchaContent"].ToString();
else
return null;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
List<string> strArray=new List<string> { "A", "B", "C", "Ç","D", "E", "F", "G","Ğ", "H", "I","İ", "J", "K", "L", "M", "N", "O","Ö", "P", "R", "S", "Ş","T", "U","Ü", "V","Y", "Z", "a", "b", "c", "ç","d", "e", "f", "g", "h","ı", "i", "j", "k", "l", "m", "n", "o","ö", "p", "r", "s","ş", "t", "u","ü" ,"v", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
Random rnd = new Random();
string strCaptcha = string.Empty;
// 10 haneli rastgele bir Captcha değeri üretmek için aşağıdaki döngüden yararlanıyoruz.
for (int i = 0; i < 10; i++)
{
int j = Convert.ToInt32(rnd.Next(0, strArray.Count-1));
strCaptcha += strArray[j].ToString();
}
HttpContext.Current.Session.Add("CaptchaContent", strCaptcha);
capcthaImage = new Image();
capcthaImage.ImageUrl = "~/CaptchaHandler.ashx";
// captcha resmini üretecek olan Handler' ımızı ImageUrl özelliğine set ettikten sonra CaptchaBox kontrolünün Controls koleksiyonuna ekliyoruz.
this.Controls.Add(capcthaImage);
}
}
}</pre>
<p>Oldukça basit bir içeriğe sahip olan bu kontrol aslında <strong>strArray</strong> isimli <strong>string</strong> tipindeki <strong>List</strong> koleksiyonu içerisinden <strong>rastgele</strong> <strong>10</strong> değeri alarak Captcha handler tipine vermektedir. Bunun karşılığında Captcha Handler tipinin ürettiği resim içeriği sayfaya basılmaktadır. CustomControls isim alanında yer alan kontrolümüzü bir ön takı(Tag Prefix) ile kullanabilmek adına <strong>web.config</strong> dosyasında aşağıdaki bildirimi yapmamız yeterlidir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<pages>
<controls>
<add namespace="CustomControls" tagPrefix="ccntrl"/>
</controls>
</pages>
</system.web>
</configuration></pre>
<p>Artık örnek bir web sayfasında söz konusu kontrolümüzü test edecek içeriği ve kod parçasını geliştirebiliriz. Dilerseniz aspx sayfamızın ön yüz içeriğini aşağıdaki gibi geliştirelim.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_1.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_1" src="/pics/bei_1_thumb.gif" alt="bei_1" width="292" height="128" border="0" /></a></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<ccntrl:CaptchaBox ID="Captcha1" runat="server" />
<br />
Captcha İçeriğini Giriniz :
<asp:TextBox ID="txtContent" runat="server" />
<br />
<asp:Button ID="btnSubmit" runat="server" Text="Validate" />
</div>
</form>
</body>
</html></pre>
<p>Burada dikkat edilmesi gereken bir nokta vardır. O da düğmeye bastığımızda <strong>PostBack</strong> işlemi sırasında Handler tipinin çok doğal olarak tekrar devreye girmesi ve yeniden taze bir <strong>Captcha</strong> içeriği üretecek olmasıdır. Dolayısıyla <strong>TextBox</strong> içeriği ile <strong>Capctha</strong> kontrolünü karşılaştırırken <strong>PreInit</strong> olayından yararlanabiliriz. İşte <strong>aspx.cs</strong> tarafındaki kodlarımız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
// Postback olması durumunda Captcha içeriği yeniden üretileceğinden PreInit olay metodunu değerlendiriyoruz.
protected override void OnPreInit(EventArgs e)
{
if (IsPostBack)
if (Session["CaptchaContent"].ToString() == Request["txtContent"]) // Session' da tutulmakta olan Captcha içeriği ile kullanıcının girmiş olduğu bilgiyi karşılaştırıyoruz.
Response.Write("Sen bir robot değilsin :)");
else
Response.Write("Seni gidi seni :)");
base.OnPreInit(e);
}
} </pre>
<p>Artık uygulamamızı test edebiliriz ne dersiniz. Hatta edebilirsiniz <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_14.png" alt="Gülümseme" /> Örneğin ben Captcha içeriğini doğru girdikten sonra aşağıdaki çıktıyı elde ettim. Tabi burada post işleminden sonra yeni bir Captcha değeri üretildiğini unutmayalım. Bu yüzden TextBox kontrol içeriği ile şu anki Capctha içeriği farklı görünüyor. En iyisi siz uygulamayı bir çalıştırında ne demek istediğimi kendiniz görün <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_65.png" alt="Göz kırpan gülümseme" /></p>
<p><a href="https://www.buraksenyurt.com/pics/bei_2.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="bei_2" src="/pics/bei_2_thumb.gif" alt="bei_2" width="340" height="219" border="0" /></a></p>
<p>Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2011%2f9%2fCaptchaKullanimi.rar">CaptchaKullanimi.rar (3,23 kb)</a></p>2011-09-26T23:11:00+00:00asp.netcaptchahandlerbsenyurtOlayı daha net bir şekilde kavrayabilmek adına örnek bir uygulama üzerinden gitmemizde yarar vardır. Bu amaçla dilerseniz Visual Studio 2010 ortamı üzerinde basit bir Web Site şablonu açarak işe başlayalım. Söz konusu Captcha kontrolü esas itibariyle Drawing tipleri tarafından çizilen bir resim ve üzerine yerleştirilen bir takım sayısal veya karakter bazlı verilerden oluşmaktadır. Burada bahsedilen çizim işini ise genellikle bir Handler tipi üstlenmektedir. İşte Handler içeriğimiz.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=9ca5696a-cda8-4e07-bcd1-6e1a66f901482https://www.buraksenyurt.com/trackback.axd?id=9ca5696a-cda8-4e07-bcd1-6e1a66f90148https://www.buraksenyurt.com/post/Haydi-Bir-Captcha-Kontrolu-Yazalim#commenthttps://www.buraksenyurt.com/syndication.axd?post=9ca5696a-cda8-4e07-bcd1-6e1a66f90148https://www.buraksenyurt.com/post/Asp-Net-4-0-Memory-CacheMemoryCache2010-11-08T03:34:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/blg238_Giris.jpg"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border: 0px;" title="blg238_Giris" src="/pics/blg238_Giris_thumb.jpg" alt="blg238_Giris" width="304" height="229" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p><strong>Deadline</strong>…Benim gibi yazılım geliştirici olan pek coğumuzun sevmediği kelimelerin başında geldiğinden eminim <img class="wlEmoticon wlEmoticon-annoyed" style="border-style: none;" src="/pics/wlEmoticon-annoyed_2.png" alt="Annoyed" /> Ancak kaçınılmaz bir gerçek olduğunu da biliyoruz. Her şeye rağmen onunla yaşamak veya yaşamasını öğrenmek zorundayız.</p>
<p>Tabi <strong>Deadline</strong>’ lar her zaman için bir proje için söz konusu olmayabiliyorlar. Söz gelimi şu sıralar hazırlanmakta olduğum <strong>Microsoft Teknoloji Günler Akşam Sınıfı Asp.Net 4.0 </strong>eğitiminin 3 gün öncesi de benim için bir <strong>Deadline</strong><em>(Aslında ilke olarak her seminerin 3 gün öncesinden tam olarak hazır olmayı benimsemişimdir)</em> Bu deadline zamanına hızla yaklaştığım şu günlerde uykusuz geceler ile hazırlanmaya devam ediyorum. Ne varki oldukça yoğun ve zorlu bir projenin de içerisinde yer almaktayım. Ama ne demişler <strong>“No Sacrifice No Victory” </strong>Bakalım bu gece ki konumuz neymiş <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_8.png" alt="Winking smile" /></p>
<p><strong>Asp.Net </strong>tarafında performans tarafında göz önüne alınan kriterlerden birisi de ön bellekleme mekanizmalarının kullanılmasıdır. Özellikle nesne tabanlı ön bellekleme işlemlerinde <strong>Cache</strong> tipinden sıklıkla yararlanıldığını görürüz. Bu tip yardımıyla herhangibir nesne örneğinin, içeriği ile birlikte bellek üzerinde tamponlanması mümkündür. Bu kullanıma göre ön bellekleme seçeneklerini zaman bazlı olarak değerlendirebiliriz<strong><em>.(Absolute Expiration, Sliding Expiration)</em></strong>. Hatta özellikle <strong>SQL</strong> tarafında, tablo bazlı bağımlılıklar oluşturabilir ve buna göre ön bellekte tutulma sürelerini tablodaki değişikliklere bağımlı hale getirebiliriz<strong><em>(SqlCacheDependency)</em></strong>. Buna ilaveten dosya bazlı bağımlılıklar da oluşturmamız mümkündür<strong><em>(File Based Dependency)</em></strong>.</p>
<p>Tabi günümüzde ve öncesinde ön bellekleme için kullanılabilecek farklı alt yapılar da bulunmaktadır. Söz gelimi <strong>Enterprise Library</strong> ile birlikte gelen <strong>Caching Application Block </strong>ve hatta dağıtık mimari stratejilerini barındıran ve <strong>Clustered</strong> ön bellekleme modelini sunan <strong>Velocity</strong> kod adlı<strong> Windows Server AppFabric Distributed Caching</strong>. Ancak ön bellekleme işlemlerinde <strong>Asp.Net</strong> tarafından sunulan <strong>built-in</strong> ön bellekleme motoru en popüler olanlarından birisidir diyebiliriz.</p>
<p>Lakin özellikle <strong>Windows Forms, WPF, Console, Class Library</strong> gibi web dışında kalan projeler için enteresan bir durum da söz konusudur. Bu proje çeşitlerinde de istenirse <strong>Asp.Net Cache </strong>tipi kullanılabilir. Tek yapılması gereken söz konusu projeye <strong>System.Web.dll assembly</strong>’ ının referans edilmesidir <img class="wlEmoticon wlEmoticon-confusedsmile" style="border-style: none;" src="/pics/wlEmoticon-confusedsmile_4.png" alt="Confused smile" /></p>
<p>Cümlenin sonundaki<strong> Confused Simle Emoticon</strong>’ u mutlaka dikkatinizi çekmiştir. Evet gerçekten de <strong>Asp.Net</strong> tarafının <strong>System.Web.Caching</strong> isim alanında(Namespace) yer alan <strong>Cache </strong>tipini örneğin bir <strong>Windows Forms </strong>uygulamasında kullanabiliriz. Ancak burada ters olan durum aslında <strong>Web </strong>tarafı için tasarlanmış ve o mimari alana ait olan bir <strong>assembly</strong>’ ının<strong><em>(System.Web.dll)</em></strong>, konu ile pek alakası olmayan desktop tipinden bir uygulamaya referans edilmiş olmasıdır.</p>
<p>İşte bu genel sıkıntı nedeni ile <strong>.Net Framework 4.0</strong> sürümünde gelen yeni bir <strong>in-memory Caching</strong> alt yapısı mevcuttur<em>(In-Memory olsa da aslında özelleştirilebilir ve farklı provider’ lar ile farklı kaynaklara doğru ön bellekleme işlemleri yapılabilir)</em>. Bu yapı aynı zamanda<strong> Asp.Net 4.0</strong> yenilikleri arasında değerlendirilmektedir. Buna göre <strong>Asp.Net</strong>’ in<strong> Caching</strong> stratejisi, web bağımlılığından çıkartılmakta ve tüm <strong>.Net </strong>uygulamalarının kullanabileceği bir model haline getirilmektedir. Bu sayede, içerisinde barındırdığı temel tipler yardımıyla web programcılarının aşina olduğu ön bellekleme teknikleri, web ortamı dışındaki uygulama çeşitleri tarafından da kullanılabilir.</p>
<p>Yeni alt yapı(Infrastructure) <strong>System.Runtime.Caching.dll assembly</strong>’ ı içerisinde yer almaktadır. Dilerseniz bu <strong>Cache</strong> tipinin kullanımını örnek bir uygulama üzerinden değerlendirmeye çalışalım. Bu amaçla basit bir <strong>Windows Forms</strong> uygulaması geliştiriyor olacağız. İlk olarak söz konusu uygulamaya aşağıdaki ekran görüntüsünde yer aldığı üzere <strong>System.Runtime.Caching assembly</strong>’ ını referans ederek işe başlayabiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg238_Reference.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg238_Reference" src="/pics/blg238_Reference_thumb.gif" alt="blg238_Reference" width="261" height="257" border="0" /></a></p>
<p>Örnek uygulamamıza ait <strong>Windows Form</strong> tasarımı ise aşağıdaki gibi olabilir. Burada<strong> Add Cache Items</strong> isimli düğmeye basıldığında bazı nesne örneklerinin ön belleğe atılması işlemleri gerçekleştiriliyor olacaktır. Diğer taraftan <strong>Get Cache Items</strong> başlıklı düğmeye basıldığında ise, ön bellek üzerinde o anda durmakta olan nesne örneklerinin elde edilmesi işlemi gerçekleştirilecektir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg238_FormDesign.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg238_FormDesign" src="/pics/blg238_FormDesign_thumb.gif" alt="blg238_FormDesign" width="425" height="322" border="0" /></a></p>
<p><strong>Windows Forms</strong> uygulamasına ait kod içeriğini ise aşağıdaki gibi geliştirebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Runtime.Caching;
using System.Windows.Forms;
namespace NewCacheConcept
{
public partial class Form1 : Form
{
string filePath = Path.Combine(Environment.CurrentDirectory, "ASP_NET_4_and_Visual_Studio_2010_Web_Development_Overview.pdf");
// In-Memory Caching için kullanılacak olan nesne örneklenir
MemoryCache mCache = MemoryCache.Default;
public Form1()
{
InitializeComponent();
}
private void btnAddToCache_Click(object sender, EventArgs e)
{
// Dosya bazlı bir bağımlılık politikası üretimi
CacheItemPolicy policy = new CacheItemPolicy(); // Önce Policy tipi oluşturulur
// Policy tipi için yeni bir dosya değişikliğini takip eden monitor nesnesi eklenir
HostFileChangeMonitor cMonitor = new HostFileChangeMonitor(
new List<string>
{
filePath
}
);
// Monitor örneğin ilkelere eklenir
policy.ChangeMonitors.Add(cMonitor);
// Ön belleğe PDF tipinden olan dosya bir byte[] dizisi şeklinde eklenir. İkinci parametre de dependency belirtilir
mCache.Add("Aspnet40", File.ReadAllBytes(filePath), policy);
// Custom Type türevli bir örneğin ön belleğe eklenmesi
Person burak = new Person();
burak.Name = "Burak Selim";
burak.Surname = "Şenyurt";
burak.Salary = 1000.25;
burak.BirthDate = new DateTime(1976, 12, 4);
mCache.Add("Person", burak, DateTimeOffset.Now.AddSeconds(30)); // Eklenme anından itibaren 30 saniye süreyle ön bellekte tutulacağı DateTimeOffset yardımıyla belirtilir
// Ön belleğe DataTable türünden bir nesne örneğinin eklenmesi
DataTable table = new DataTable();
using (SqlConnection conn = new SqlConnection("data source=.;database=AdventureWorks;integrated security=SSPI"))
{
SqlDataAdapter adapter = new SqlDataAdapter("Select * From Production.Product", conn);
adapter.Fill(table);
}
mCache.Add("Products", table, DateTimeOffset.Now.AddMinutes(2)); // DataTable içeriği eklenme anından itibaren iki dakika süreyle ön bellekte tutulacaktır
}
private void btnGetCacheInfo_Click(object sender, EventArgs e)
{
GetDashboard();
}
private void GetDashboard()
{
// Fiziksel olarak ön bellek ile ilişkili bazı bilgilerin çekilmesi
lstHistories.Items.Add(String.Format("Cache için kullanılabilecek bellek oranı %{0} ", mCache.PhysicalMemoryLimit));
lstHistories.Items.Add(String.Format("Cache için kullanılabilecek bellek miktarı {0}", mCache.CacheMemoryLimit));
lstHistories.Items.Add(String.Format("Cache içerisindeki nesne sayısı {0}", mCache.GetCount()));
}
private void btnGetCacheItem_Click(object sender, EventArgs e)
{
GetDashboard();
// byte[] tipinden ön belleklenmiş olan PDF dosyasının elde edilişi
CacheItem cItem=mCache.GetCacheItem("Aspnet40");
if (cItem != null)
{
byte[] cValue = cItem.Value as byte[];
lstHistories.Items.Add(String.Format("Cache deki {0} key değerli nesnenin boyutu {1}", cItem.Key, cValue.Length));
}
// Person tipinden ön belleklenen nesnenin elde edilişi
CacheItem cItemPerson = mCache.GetCacheItem("Person");
if (cItemPerson != null)
{
Person cachedPerson = cItemPerson.Value as Person;
lstHistories.Items.Add(String.Format("Cache deki {0} key değerli nesnenin Name değeri {1}", cItemPerson.Key, cachedPerson.Name));
}
// DataTable tipinden ön belleklenen nesnenin elde edilişi
CacheItem cItemTable = mCache.GetCacheItem("Products");
if (cItemTable != null)
{
DataTable table = cItemTable.Value as DataTable;
lstHistories.Items.Add(String.Format("Cache deki {0} key değerli nesnenin toplam satır sayısı {1}", cItemTable.Key, table.Rows.Count));
}
// Belirli key bilgilerine ait değerlerin çekilmesi
var values = mCache.GetValues(keys: new string[] { "Person", "Products" });
}
}
}</pre>
<p>Olayın kalbi <strong>MemoryCache</strong> isimli nesne örneğidir. Bu nesne örneğinden yararlanılarak <strong>byte[], Person</strong> ve <strong>DataTable </strong>tipinden nesne örneklerinin ön belleğe atılması işlemleri gerçekleştirilmektedir. Özellikle <strong>PDF </strong>formatındaki dosya içeriğinin<em>(ki içerisinde Asp.Net 4.0 ile gelen yenilikler anlatılmaktadır <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_8.png" alt="Winking smile" /> )</em> <strong>byte[] </strong>dizisi olarak belleğe atılışı sırasında bir de <strong>ilke(Policy)</strong> uygulandığına dikkat edilmelidir. Öyleki bu ilkeye göre bir dosya bağımlılığı oluşturulmuştur. Gerçi <strong>PDF</strong> dosyasının değiştirilmesi pek söz konusu olmasa da önemli olan bağımlılığın nasıl yaratıldığıdır. Bu, tipik olarak dosya da değişiklik olması halinde ön bellekte duran nesnenin düşürülmesi anlamına gelmektedir. Yani web tarafından aşina olduğumuz <strong>File Dependency</strong> olayı <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_8.png" alt="Winking smile" /> </p>
<p><strong>Person </strong>ve <strong>DataTable </strong>tipinden olan nesne örneklerinin ön bellekte tutulması ise kesin süre sonlu olarak bildirilmiştir<strong><em>(Absolute Time Expiration)</em></strong>. Yani ön belleğe atılan nesne örnekleri belirlediğimiz süre kadar tutulacaklardır. Dikkat edilmesi gereken noktalardan bir diğeri de, uygulamanın çalıştığı makinenin ön bellekleme için kullanılabilecek alan değerlerinin yüzdesel ve miktarsal olarak elde edilebiliyor olmasıdır.</p>
<p>Şimdi örneğimizi çalıştırarak ilk testimizi yapalım.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg238_Runtime1.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg238_Runtime1" src="/pics/blg238_Runtime1_thumb.gif" alt="blg238_Runtime1" width="414" height="300" border="0" /></a></p>
<p>Dikkat edileceği üzere ön belleğe atılan <strong>3</strong> nesne örneği söz konusudur ve bunlara ait bir takım bilgilerde elde edilebilmektedir. Söz gelimi <strong>PDF dosyasının boyutu</strong>, <strong>Person nesne örneğinin Name değeri </strong>ve <strong>DataTable içerisinde duran toplam satır sayısı </strong>gibi. Diğer yandan önem arz eden konulardan biriside <strong>Absolute Expiration</strong> sürelerinin dolması halinde ne olacağıdır. Söz gelimi <strong>Person </strong>nesne örneği ön belleğe atıldıktan sonra <strong>30 saniye boyunca yaşayabilir</strong>. Eğer süre dolduktan sonra bellek üzerinde kalan nesnelere bakılırsa <strong>Person</strong> nesne örneğinin artık olmadığı rahatlıkla görülebilir.<em>(Diğer yandan uygulama kapatıldığı takdirde ön belleğe atılan tüm nesnelerin geçerliliği ortadan kalkacaktır. Bir başka deyişle süreleri veya bağımlılıkları bozulmasa dahi, uygulama açık iken eklenen ön bellek nesnelerine, uygulama tekrar açıldığında erişilemeyecektir)</em></p>
<p><a href="https://www.buraksenyurt.com/pics/blg238_Runtime2.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg238_Runtime2" src="/pics/blg238_Runtime2_thumb.gif" alt="blg238_Runtime2" width="414" height="300" border="0" /></a></p>
<p>Şu an itibariyle ön bellekte 2 nesne örneği yer almaktadır ve <strong>Person</strong> tipine ait örnek bunların arasında değildir. Nitekim kendisi için belirlenen ön bellekte yaşama süresi aşılmıştır.</p>
<p>Aslında ön bellekte duran n sayıda nesne olduğunda bunlardan sadece belirli <strong>Key </strong>adlarına sahip olanlarının elde edilmesi de bazı senaryolarda işimize yarayabilir. Bu noktada <strong>MemoryCache </strong>nesne örneği üzerinden çağırılabilen <strong>GetValues </strong>metodu oldukça işe yaramaktadır. Aşağıdaki <strong>Debug </strong>zamanı ekran görüntüsünde bu nesne örneği içerisinde <strong>Person </strong>ve <strong>Products </strong>adları ile işaret edilen nesne örneklerine erişilebildiği işaret edilmektedir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg238_DebugView.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg238_DebugView" src="/pics/blg238_DebugView_thumb.gif" alt="blg238_DebugView" width="523" height="121" border="0" /></a></p>
<p>Görüldüğü üzere <strong>MemoryCache</strong> nesne örneğinden yararlanarak <strong>Asp.Net</strong> tarafında sık kullanılan<strong> in-memory</strong> <strong>Caching</strong> tekniğini,<strong> System.Web.dll assembly</strong>’ ına bağımlı olmadan web harici herhangibir uygulamada ele alabilmekteyiz. <strong>MemoryCache</strong> nesnesinin kullanımı ile ilişkili olarak detaylı referans bilgisine <a href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx" target="_blank">MSDN</a> üzerinden ulaşabilirsiniz. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f11%2fASPNET40Ogreniyorum.rar">ASPNET40Ogreniyorum.rar (2,31 mb)</a> <strong>[Örnek Visual Studio 2010 Ultimate sürümü üzerinde geliştirilmiş ve test edilmiştir]</strong></p>2010-11-08T03:34:00+00:00asp.net 4.0cachingwindows formsmemorycachein memory cachingasp.net cachingbsenyurtAsp.Net tarafında performans tarafında göz önüne alınan kriterlerden birisi de ön bellekleme mekanizmalarının kullanılmasıdır. Özellikle nesne tabanlı ön bellekleme işlemlerinde Cache tipinden sıklıkla yararlanıldığını görürüz. Bu tip yardımıyla herhangibir nesne örneğinin, içeriği ile birlikte bellek üzerinde tamponlanması mümkündür. Bu kullanıma göre ön bellekleme seçeneklerini zaman bazlı olarak değerlendirebiliriz.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=649f1e76-d859-4664-83b2-cc97503978da1https://www.buraksenyurt.com/trackback.axd?id=649f1e76-d859-4664-83b2-cc97503978dahttps://www.buraksenyurt.com/post/Asp-Net-4-0-Memory-Cache#commenthttps://www.buraksenyurt.com/syndication.axd?post=649f1e76-d859-4664-83b2-cc97503978dahttps://www.buraksenyurt.com/post/Ufak-Session-IcerikleriMinicik Session İçeriği2010-11-02T15:10:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/blg237_Giris.jpg"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border-width: 0px;" title="blg237_Giris" src="/pics/blg237_Giris_thumb.jpg" alt="blg237_Giris" width="304" height="202" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>Bildiğiniz üzere bir süredir <strong>Microsoft Teknoloji Günleri Akşam Sınıfı </strong>etkinliklerini gerçekleştirmekteyiz. <strong>Kasım </strong>ayının konusu ise hemen her sürümünde köklü ve önemli yenilikler ile birlikte gelen <strong>Asp.Net' in 4.0 sürümü </strong><img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_7.png" alt="Winking smile" /> Web programlamanın gelişen ihtiyaçları nedeniyle <strong>Asp.Net </strong>alt yapısında da her major sürümde fazlasıyla yenilik bulunmakta.</p>
<p>Tabi yine bildiğiniz üzere bendeniz web konusunda uzman değilim. Ağırlıklı olarak <strong>servis yönelimli mimari(Service Oriented Architecture)</strong> tarafı ile ilgilenmekteyim. Ancak eğitmenlik yaptığım dönemlerden kalan bir hastalık olsa gerek, <strong>.Net</strong>' in diğer pek çok alanı ile de ilgilenmekteyim. Eğitime blog yazısını hazırladığım tarih itibaryile oldukça az bir süre kaldı. <strong>Çıraklık başvuruları, Microsoft gönüllü çalışmaları, 11 haftalık proje planı olup bizden 4 haftada bitirmemizi istedikleri kocaman POC çalışması, S(h)arp Efe</strong> derken zamanı etkin kullanmak konusunda sıkıntılar yaşamaya başladım <img class="wlEmoticon wlEmoticon-confusedsmile" style="border-style: none;" src="/pics/wlEmoticon-confusedsmile_3.png" alt="Confused smile" /></p>
<p>Aslında<strong> .Net Framework 4.0</strong> tarafını uzun süredir incelememe rağmen, en ince detaylarına kadar girmeden konuya hakim olmanın zor olacağını da gayet iyi biliyordum. Hatta bana göre yazarak anlatmak öğrenmenin en iyi yollarından birisi. İşte bu bi dolu düşünce altında başladığım gece çalışmasının sonucu olan küçük bir blog girdisi ile karşınızdayım. Bu yazımızda <strong>Asp.Net 4.0</strong> tarafında gelen önemli yeniliklerden birisi olan serileştirilebilir <strong>Session</strong> içeriğini ufaltmak<em>(daha teknik bir tabirle sıkıştırmak)</em> konusuna değiniyor olacağız.</p>
<p><strong>Session</strong> bildiğiniz üzere web çalışma modelinde önemli bir yere sahip. Özellikle oturum bazlı olarak veri tutulması istenen durumlarda, sunucu tarafında kullanılabilecek seçeneklerden birisi olduğunu ifade edebiliriz. <strong>Session</strong> içeriklerini kullanmak da son derece basit. Bu anlamda <strong>Asp.Net'</strong> in başlangıcından beri var olan <strong>HttpSessionState</strong> tipinden yararlanıldığını biliyoruz. Tabi <strong>Session</strong> kullanımında dikkat edilmesi gereken bazı hususlarda var. Söz gelimi varsayılan olarak<strong> In-Proc</strong> mod adı verilen ve çalışmakta olan <strong>Asp.Net</strong> uygulamasına ait<strong> Worker Process</strong> ile ilişkili bellek alanlarında tutulan <strong>Session</strong> içeriklerinin, <strong>SQL</strong> veritabanı veya farklı bir <strong>State Service'</strong> e ait process altında tutulması da söz konusu. Bu farklı tutuluş şekillerine ilaveten <strong>object</strong> tipi ile çalışabilen bir yapıdan bahsettiğimizi de ifade etmek isterim. Yani her tür .Net nesnesini atayabilirsiniz. Hımmm <img class="wlEmoticon wlEmoticon-sarcasticsmile" style="border-style: none;" src="/pics/wlEmoticon-sarcasticsmile_3.png" alt="Sarcastic smile" /></p>
<p>Şimdi <strong>In-Proc</strong> mod dışındaki modları göz önüne alalım. <strong>SQL</strong> sunucusu<em>(SQLServer modu)</em> veya <strong>State Service(StateServer modu)</strong> kullanan modları. Bu modlar kullanıldığında <strong>Session</strong> içerisine atılan verilerin serileştirilerek tutulması söz konusudur. Bu son derece mantıklıdır çünkü <strong>object</strong> tipi içerisine çok büyük boyutlu nesnelerin dahi atılması mümkündür. Ancak bu durum zamanla <strong>SQL </strong>veya <strong>State Service </strong>modlarının kullandığı alanların önemli ölçüde şişmesine de neden olabilir. İşte <strong>Asp.Net 4.0 </strong>ile <strong>Session </strong>ayarlamaları için eklenen <strong>compressionEnabled</strong> özelliği sayesinde, söz konusu <strong>Session</strong> içeriklerinin önemli ölçüde sıkıştırılması da mümkündür. Bu noktada <strong>System.IO.Compression.GZipStream</strong> tipinin büyük bir rol oynadığını ifade edebiliriz. Nitekim bu tip sayesinde serileştirilmiş olan verinin sıkıştırılması söz konusudur.</p>
<p>Yazımızın ilerleyen kısımlarında sıkıştırmanın bu noktadaki önemini vurgulamak açısından basit bir örnek üzerinden ilerlemeye çalışıyor olacağız. Örnek senaryomuzda <strong>Session</strong> bilgilerini <strong>SQL</strong> veritabanı üzerinde tutuyor olacağız. Bu amaçla bir ön hazırlık yapmamız gerekiyor. <strong>Visual Studio 2010 Command Prompt</strong> üzerinden <strong>aspnet_regsql</strong> aracını kullanarak söz konusu veritabanını<em>(Örneğimizde SessionDB)</em> oluşturabiliriz.</p>
<p><strong>Kurulum ;</strong></p>
<p>C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>aspnet_regsql -S localhost -E -ssadd -sstype c -d SessionDB</p>
<p>Start adding session state.</p>
<p>....</p>
<p>Finished.</p>
<p>To use this custom session state database in your web application, please specify it in the configuration file by using the 'allowCustomSqlDatabase' and 'sqlConnectionString' attributes in the <system.web>\<sessionState> section.</p>
<p>Bu kurulum işlemi sonrasında <strong>SessionDB</strong> içerisinde oluşturulan <strong>ASPStateTempSessions</strong> tablosunun yapısı da aşağıdaki gibi olacaktır. İki alan; <strong>SessionItemShort</strong> ve <strong>SessionItemLong </strong>şu an ki vakamız için önem arz etmektedir. Bu alanlarda serileştirilen <strong>Session</strong> içerikleri tutulmaktadır. Bununla birlikte serileşen içeriğin büyüklüğüne göre iki alandan bir tanesine veri eklenmesi de söz konusu olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg237_Table.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg237_Table" src="/pics/blg237_Table_thumb.gif" alt="blg237_Table" width="300" height="239" border="0" /></a></p>
<p>Şimdi basit bir <strong>Asp.Net Web Application</strong> üzerinden ilerleyelim. Söz konusu web uygulamasında en önemli nokta <strong>Session</strong> kullanımına ait konfigurasyon ayarlarıdır. <strong>Default.aspx</strong> sayfamız son derece basit bir fonksiyonelliğe sahiptir. <strong>Button</strong> kontrolüne basıldığında <strong>Logo</strong> isimli bir sınıf örneğinin <strong>Session</strong> nesnesi olarak atılması söz konusudur. İşte örnek uygulama kodlarımız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.IO;
using System.Data.SqlClient;
using System.Data;
namespace OldStyleSession
{
[Serializable]
public class Logo
{
public string FileContent { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
}
public partial class _Default
: System.Web.UI.Page
{
protected void btnAddToSession_Click(object sender, EventArgs e)
{
Logo lg = new Logo
{
Name="Car"
, CreationTime=DateTime.Now
, FileContent=File.ReadAllText(Server.MapPath("/Bilgiler.txt"))
};
Session.Add("Logo", lg);
}
protected void btnWriteSession_Click(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection("data source=.;database=SessionDB;integrated security=SSPI"))
{
SqlCommand cmd = new SqlCommand("Select SessionItemShort,SessionItemLong From AspStateTempSessions Where SessionId=@SessionId", conn);
cmd.Parameters.AddWithValue("@SessionId", txtSessionId.Text);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if (reader.Read())
{
var itemShort=reader.GetSqlBytes(0);
var itemLong = reader.GetSqlBytes(1);
Response.Write(
String.Format("SessionItemShort length = {0}, SessionItemLong length = {1}"
,itemShort.IsNull?0:itemShort.Length
,itemLong.IsNull?0:itemLong.Length
)
);
}
reader.Close();
}
}
}
}</pre>
<p>Görüldüğü üzere <strong>Session</strong> özelliğinin <strong>Add</strong> metodundan yararlanılarak <strong>lg</strong> isimli <strong>Logo</strong> nesne örneğinin ilave edilmesi söz konusudur. Diğer yandan bir de üretilen <strong>Session</strong> için yazılan <strong>SessionItemLong</strong> ve <strong>SessionItemShort</strong> alanlarının içeriklerinin <strong>byte</strong> cinsinden uzunluklarını ekrana yazdırabileceğimiz bir metodumuz da bulunmaktadır. Bu metodu üretilen <strong>Session</strong> içeriklerinin boyutlarını kıyaslamak için kullanıyor olacağız.</p>
<p>Örnekte kullanılan <strong>Bilgiler.txt</strong> isimli <strong>Text</strong> tabanlı dosya <strong>328 Kb</strong> büyüklüğünde anlamsız bir içeriğe sahiptir. Burada <strong>text</strong> tabanlı bir içerik söz konusu olduğundan sıkıştırma işlemini ele alacak olan algoritma çarpıcı sonuçlar üretecektir. İlk etapta <strong>web.config</strong> dosyasının içeriğini aşağıdaki gibi tasarladığımızı düşünebiliriz.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<system.web>
<sessionState allowCustomSqlDatabase="true" sqlConnectionString="data source=.;database=SessionDB;integrated security=sspi" mode="SQLServer" compressionEnabled="false"/>
<compilation debug="true" targetFramework="4.0">
</compilation>
</system.web>
</configuration></pre>
<p><strong>SessionDB</strong> isimli bir veritabanı adı kullandığımızdan <strong>allowCustomSqlDatabase</strong> özelliğine <strong>true</strong> değeri atanmıştır. <strong>sqlConnectionString</strong> bilgisinde ise <strong>Session</strong> bilgilerinin yazılacağı veritabanı bağlantısı belirtilmektedir. <strong>mode</strong> niteliğine atanan değer ile <strong>Session</strong> içeriklerinin <strong>SQL</strong> veritabanı üzerinde<em>(sqlConnectionString ile belirtilen bağlantıya doğru)</em> tutulacağı ifade edilmektedir. Şu an için <strong>compressionEnabled</strong> özelliğine <strong>false</strong> değeri atanmıştır. Bu şekilde aslında <strong>Asp.Net 4.0</strong> öncesinde olduğu gibi standart bir <strong>Session</strong> tutma işlemi yapılacağı belirtilmektedir. Örneği bu şekilde test ettiğimizde <strong>ASPStateTempSessions</strong> tablosunda üretilen <strong>Session</strong> satırına ait sorgulama sonucu, <strong>SessionItemLong</strong> alanına <strong>binary</strong> bir içeriğin serileştirilmiş olduğu görülecektir.</p>
<p><strong>Sql tarafında Compression kullanılmadığı haldeki durum;</strong></p>
<p>İlk olarak sıkıştırma işlemini <span style="text-decoration: underline;">kullanmadığımız</span> durumu ele alalım. <strong>Add Session</strong> işleminden sonra, üretilen <strong>SessionID</strong> değerini web sayfamız üzerinde kullanırsak aşağıdaki sonuçlar ile karşılaşırız.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg237_FirstCase.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg237_FirstCase" src="/pics/blg237_FirstCase_thumb.gif" alt="blg237_FirstCase" width="459" height="291" border="0" /></a></p>
<p>Dikkat edileceği üzere <strong>335141</strong> uzunluğunda bir byte içeriği söz konusudur.</p>
<p>Gelelim sıkıştırılma durumuna. Bu sefer <strong>compressionEnabled</strong> özelliğine <strong>true</strong> değerini vermemiz yeterli olacaktır.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<system.web>
<sessionState allowCustomSqlDatabase="true" sqlConnectionString="data source=.;database=SessionDB;integrated security=sspi" mode="SQLServer" compressionEnabled="true"/>
<compilation debug="true" targetFramework="4.0">
</compilation>
</system.web>
</configuration></pre>
<p><strong>Sql tarafında Compression kullanılması haldeki durum ise aşağıdaki gibi olacaktır;</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/blg237_SecondCase.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg237_SecondCase" src="/pics/blg237_SecondCase_thumb.gif" alt="blg237_SecondCase" width="444" height="287" border="0" /></a></p>
<p>Görüldüğü üzere bir önceki vakanın tersine serileştirilebilir içerik <strong>SessionItemLong</strong> alanı yerine <strong>SessionItemShort</strong> içerisine eklenmiştir. Bununla birlikte söz konusu sıkıştırılmış verinin içeriği <strong>3686 </strong>dır <img class="wlEmoticon wlEmoticon-disappointedsmile" style="border-style: none;" src="/pics/wlEmoticon-disappointedsmile_1.png" alt="Disappointed smile" /> </p>
<p>Sıkıştırma algoritmasının uygulanması sonucu neredeyse standart <strong>session</strong> içeriğinin <strong>%0,0109983559158682 üne </strong>kadar verinin küçültülmesi söz konusudur. Elbette burada <strong>text</strong> tabanlı bir içerik kullanıldığından söz konusu farkın oluşması doğaldır. Özellikle <strong>binary</strong> içerikten oluşan<em>(örneğin resim formatı gibi)</em> bir verinin serileştirilmesi esnasında veri boyutlarında önemli bir fark olmayadabilir. Bu durumu analiz etmek için aşağıdaki kod parçasını göz önüne alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">protected void btnAddDataTable_Click(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection("data source=.;database=AdventureWorks;integrated security=SSPI"))
{
using (SqlDataAdapter adapter = new SqlDataAdapter("select * from Production.ProductPhoto", conn))
{
DataSet ds = new DataSet("ProductPhoto");
adapter.Fill(ds);
Session.Add("ProductPhoto", ds);
}
}
}</pre>
<p>Bu kez <strong>ProductPhoto</strong> isimli tablonun tüm içeriğini çektiğimiz bir <strong>DataSet</strong> örneğini <strong>Session</strong>' a ekliyoruz. Yine ilk olarak <strong>compressionEnabled</strong> niteliğinin <strong>false</strong> değere sahip olduğu ve sonrasında <strong>true</strong> olması halini göz önüne alalım. Bu durumda aşağıdaki örnek çıktılar elde edilecektir.</p>
<p>Sıkıştırılma kapalı iken;</p>
<p><a href="https://www.buraksenyurt.com/pics/blg237_ThirdCase.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg237_ThirdCase" src="/pics/blg237_ThirdCase_thumb.gif" alt="blg237_ThirdCase" width="433" height="273" border="0" /></a></p>
<p>Sıkıştırılma açık iken;</p>
<p><a href="https://www.buraksenyurt.com/pics/blg237_FourthCase.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg237_FourthCase" src="/pics/blg237_FourthCase_thumb.gif" alt="blg237_FourthCase" width="435" height="276" border="0" /></a></p>
<p>Sıkıştırılmama durumunda <strong>2712191</strong> iken sıkıştırılma durumunda <strong>2711426</strong>. Yani sadece <strong>% 1,000282139361355 </strong>oradanın bir sıkıştırma söz konusu olmakta. <img class="wlEmoticon wlEmoticon-cryingface" style="border-style: none;" src="/pics/wlEmoticon-cryingface_1.png" alt="Crying face" /> Değer mi? Değmez. Buna göre serileştirilebilir içeriklerin sıkıştırılabilir olmaları da önem kazanmaktadır. <em>(Nitekim bir PDF veya JPEG dosyasını sıkıştırdığınızda önemli ölçüde bir sıkıştırma olmadığını görürüz)</em> İnandırıcı geldi mi? <img class="wlEmoticon wlEmoticon-annoyed" style="border-style: none;" src="/pics/wlEmoticon-annoyed_1.png" alt="Annoyed" /> </p>
<p><strong>Varsayımsal Yaklaşım</strong></p>
<p>Tahmini olarak<strong> Asp.Net</strong> çalışma zamanının <strong>Session</strong> verisini nasıl işlediğini düşünelim. Sıkıştırma modu kapalı iken, <strong>binary</strong> formatta serileştirme işlemi yapılıp verinin içeriye atılıyor olması söz konusu olabilir. Diğer yandan sıkıştırma modu açık olduğunda, serileşen içeriğin olduğu gibi yazılmadan önce belki de bir <strong>GZipStream</strong> işleminden geçirilmesi ve sıkıştırılmaya çalışılması söz konusu olabilir.</p>
<p>Bu varsayım altında şu şekilde ilerliyor olacağız. <strong>DataSet</strong> nesnesinin kendisini standart bir <strong>binary</strong> serileştirme işlemine tutacağız. Sonrasında ise bu içeriği <strong>GZipStream</strong> ile sıkıştırmayı deneyip içeriklerin boyutlarına bakacağız. İspat için bu tekniği kullanıyor olacağız. Haydi parmakları sıvayalım. İşte örnek kod parçamız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using (SqlConnection conn = new SqlConnection("data source=.;database=AdventureWorks;integrated security=SSPI"))
{
using (SqlDataAdapter adapter = new SqlDataAdapter("select * from Production.ProductPhoto", conn))
{
DataSet ds = new DataSet("ProductPhoto");
adapter.Fill(ds);
BinaryFormatter formatter = new BinaryFormatter();
FileStream fs= new FileStream("c:\\DataSet.bin",FileMode.OpenOrCreate,FileAccess.Write);
formatter.Serialize(fs, ds);
fs.Close();
FileStream fsZip= new FileStream("c:\\DataSetZip.bin",FileMode.OpenOrCreate,FileAccess.Write);
GZipStream gStream = new GZipStream(fsZip, CompressionMode.Compress);
byte[] dataSetBytes=File.ReadAllBytes("C:\\DataSet.bin");
gStream.Write(dataSetBytes, 0,dataSetBytes.Length);
gStream.Close();
fsZip.Close();
}
}</pre>
<p>Kod parçasından görüldüğü üzere ilk olarak Binary formatta bir <strong>DataSet</strong> içeriğini serlişetirmekteyiz. Bu zaten <strong>Session</strong> tarafında <strong>SessionItemLong</strong> veya <strong>SessionItemShort</strong> alanına atılan serileştirilebilir içeriktir. Kodun ilerleyen kısımlarında ise serileştirilen içeriği <strong>GZipStream</strong> sınıfından yararlanarak sıkıştırmaya çalışıyoruz. Bunun sonucu olarak üretilen dosya içeriklerinin boyutlarına baktığımızda ise aşağıdaki ekran görüntüsünde yer alan sonuçları elde ederiz.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg237_Last.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="blg237_Last" src="/pics/blg237_Last_thumb.gif" alt="blg237_Last" width="586" height="48" border="0" /></a></p>
<p>Dikkat edileceği üzere serileştirilmiş içerik ile serileştirilmiş içeriğin sıkıştırılmış versiyonları arasında boyut olarak pek bir fark yoktur. Bu da <strong>DataSet</strong> tipinin ve özellikle <strong>ProductionPhoto</strong> içerisindeki <strong>binary</strong> alanlarının iyi bir şekilde sıkıştırılamıyor olmalarından kaynaklanmaktadır. Son geliştirdiğimiz örnek tamamen ve tamamen <strong>GZipStream</strong> ile sıkıştırma tekniğinin her zaman işe yaramayacağını ve veri boyutunda daima önemli ölçüde bir değişikliğe neden olmayacağını göstermek üzere ele alınmıştır bunu unutmayalım. Sanıyorum ki iyi kullanıldığı takdirde <strong>Session</strong> sıkıştırması özellikle çok fazla oturumun açıldığı web uygulamalarında, <strong>SQL Server</strong> veya <strong>State Server</strong> mod kullanılması halinde önemli yer kazancı sağlayacak şekilde fayda getirmektedir. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f11%2fOldStyleSession.rar">OldStyleSession.rar (137,04 kb)</a></p>2010-11-02T15:10:00+00:00asp.netasp.net 4.0sessionsession compressionbsenyurtAslında .Net Framework 4.0 tarafını uzun süredir incelememe rağmen, en ince detaylarına kadar girmeden konuya hakim olmanın zor olacağını da gayet iyi biliyordum. Hatta bana göre yazarak anlatmak öğrenmenin en iyi yollarından birisi. İşte bu bi dolu düşünce altında başladığım gece çalışmasının sonucu olan küçük bir blog girdisi ile karşınızdayım. Bu yazımızda Asp.Net 4.0 tarafında gelen önemli yeniliklerden birisi olan serileştirilebilir Session içeriğini ufaltmak(daha teknik bir tabirle sıkıştırmak) konusuna değiniyor olacağız.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=68270ce8-4884-4031-a9cc-f073515ab8cd1https://www.buraksenyurt.com/trackback.axd?id=68270ce8-4884-4031-a9cc-f073515ab8cdhttps://www.buraksenyurt.com/post/Ufak-Session-Icerikleri#commenthttps://www.buraksenyurt.com/syndication.axd?post=68270ce8-4884-4031-a9cc-f073515ab8cd