Merhaba Arkadaşlar,
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 Güzel bir çorba hazırlamaya ne dersiniz?
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 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”
Pekala bu iş için hemen bir Web User Control geliştirebilir ve Load 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 Bunun yerine aşağıdaki grafikte yer alan Solution içeriğini üreterek ilerleyeceğiz. (Dilerseniz yazının son satırından Solution’ ı indirip inceleyin)
İlk olarak AdventureWorks veritabanındaki bazı tabloları içerecek olan Entity Model’ imizi tasarlayarak işe başlayalım. Söz konusu Class Library projesi içerisinde kullanacağımız Entity Model diagramının içeriğini ise başlangıçta aşağıdaki gibi oluşturabiliriz.
Şimdilik Product ve ProductSubCategory tablolarının karşılığı olan Entity içeriklerinin yer aldığı bir diagram ile karşı karşıyayız. Diğer yandan Web User Control’ ümüzü geliştirene kadar kat etmemiz gereken daha çok yolumuz olacak. Öncelikle bir Product 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 Class Library olacak. Bu katmanda ekstradan bir Business Object daha kullanıyor olacağız. Çünkü Product tipinin tüm içeriğini dış ortama sunmak gibi bir niyetimiz olmadığını düşünmekteyiz. Bu sebepten Product tipi yerine geçecek olan bir Surrogate Type kullanımını ele alacağız. Dolayısıyla Company.Operations isimli Class Library içeriğini aşağıdaki sınıf çizelgesinde olduğu gibi tasarlayarak devam ediyoruz.
Burada görülen ProductBusinessObject aslında dış ortama sunulacak olan Product içeriğini taşıyan bir POCO nesnesidir. Aslında bu tip Business Object türlerini başka bir katman içerisinde tutmayı da düşünebiliriz
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; }
}
}
ProductionOperations isim sınıfımız ise şu an için tek bir operasyon sunmaktadır ve ProductId değerine göre ProductBusinessObject tipinden bir referans döndürmek üzere tasarlanmıştır.
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;
}
}
}
Örneğimizi EF 4.0 versiyonu ile geliştirdiğimiz için bir Lazy Loading durumunun da söz konusu olduğunu ifade edelim. SubCategoryName özelliğine değer atanan satıra dikkat edin
Artık söz konusu iş operasyonunu dış dünyaya sunacak bir uygulamayı da geliştirebiliriz. Bu uygulama favorimiz olan WCF tabanlı bir servis olarak düşünülmektedir. Böylece Entity modelimiz, Business Object’ lerimiz ve Business Operasyonlarımızın 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.
IProductionService isimli servis sözleşmemiz(Service Contract) şu şekildedir;
using System.ServiceModel;
using Company.Operations;
namespace AdventureWorksService
{
[ServiceContract]
public interface IProductionService
{
[OperationContract]
ProductBusinessObject GetProduct(int ProductId);
}
}
Sözleşmemiz senaryomuza göre bir ürünün elde edilmesi ve ProductBusinessObject tipinden geriye döndürülmesi işlemini üstlenen basit bir operasyon sunmaktadır. Bu operasyonun uyarlaması ise ProductionService sınıfı içerisinde gerçekleştirilmektedir.
using Company.Operations;
namespace AdventureWorksService
{
public class ProductionService
: IProductionService
{
ProductionOperations productionOperator = new ProductionOperations();
public ProductBusinessObject GetProduct(int ProductId)
{
return productionOperator.GetProduct(ProductId);
}
}
}
Servis uygulamamızda dikkat edilmesi gereken noktalardan birisi de, AdventureWorks entity modelini kullanabilmesi için gerekli Connection String bilgisine sahip olma zorunluluğudur. Bildiğiniz üzere Entity Modelimizi bir Class Library 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 AdventureWorksEntity kütüphanesinde otomatik olarak üretilen connection string bilgisinin servis uygulamasındaki web.config dosyasında yer alması şart.
<?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>
Çorbamıza yeni baharatlar katarak devam edelim
Artık servis uygulamamız da hazır olduğuna göre bunu kullanacak olan Asp.Net Web Uygulamasını geliştirerek yol alabiliriz. Tahmin edileceği üzere Asp.Net Web uygulamamız, AdventureWorksService isimli WCF Servisini referans etmelidir.
Yazımızın başında belirttiğimiz senaryomuzdan hatırlayacağınız üzere bir Web User Control’ den bahsediyorduk. Bu Web User Control’ ün Business Object’ imize göre tasarlanması gerekiyor. Bu amaçla aşağıdaki gibi bir tasarım gerçekleştirebiliriz.
<%@ 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>
Kod tarafında ise aşağıdaki işlemleri gerçekleştirebiliriz.
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;
}
}
}
}
Dikkat edileceği üzere Web User Control herhangibir ürün numarasına bağlı bir ProductBusinessObject içeriğini dış ortama özellikler(Properties) yardımıyla da sunmaktadır. Ayrıca içerisinde yer alan Label kontrollerinin çalışma zamanında yüklenmesi için LoadContent isimli bir metoddan yararlanılmaktadır.
Artık Web User Control bileşenimizi örnek bir aspx 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.
<%@ 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>
ve kod içeriği;
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);
}
}
}
}
}
Sayfamızda sembolik olarak TextBox kontrolüne girilen sayısal değere göre Product bilgisinin çekilmesi ve bulunan ürüne ait bazı bilgilerin dinamik olarak yüklenen Web User Control içerisinde gösterilmesi sağlanmaktadır. Söz gelimi çalışma zamanında 774 numaralı ürüne ait bilgileri çekmek istersek aşağıdaki ekran görüntüsünde yer alan sonucu elde ederiz
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
Son olarak Solution içerisinde yer alan Assembly’ lar arasındaki ilişkiyi göstererek yazımızı yavaş yavaş sonlandıralım.
Ö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 ProductBusinessObject tipinden çekip her biri için dinamik olarak ProductControl 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
Tekrardan görüşünceye dek hepinize mutlu günler dilerim.
Company.rar (153,21 kb)