1- POCO (Plain Old CLR Objects) Nedir?
Entity Framework, ADO.NET Data Model'da yer alan varlıklarımız(entity) için kendi sınıflarımızı yazabilmemize destek verir. Bu yapıda, varlıklar herhangi bir sınıftan türetilmez, herhangi bir interface'i implemente etmez, veri en sade şekilde tutulur. Böylece projemizi otomatik olarak oluşturulan modelle birlikte gelen birçok koddan kurtarıp, daha basit bir model oluşmasını sağlayabiliriz.
2- POCO Nesnelerinin Oluşturulması
POCO ile modelimizi oluşturmak için, öncelikle projemize bir ADO.NET Entity Data Model ekliyoruz. Bunun için projeye sağ tıklayıp Add New Item -> ADO.NET Entity Data Model seçiyoruz. Karşımıza bir sihirbaz çıkıyor, burada "Generate from database" deyip ilerliyoruz. Çıkan ekranda veri tabanını seçiyoruz (Ben örneğimizde Misrosoft'un örnek database'lerinden biri olan AdventureWorks'u kullandım.). Sonraki ekranda veri tabanında bulunan tablo, stored procedure ve view'ların listesini görebiliriz. Örneği küçük tutmak adına, Tables sekmesinden sadece ProductCategory tablosunu seçtim.
POCO nesnelerimizi oluşturabilmek için, öncelikle, modelimizin Properties kısmındaki CodeGenerationStrategy özelliğinin değerini None yapıyoruz. Böylece modelimizin Designer kısmında, model üzerindeki nesnelerimiz ile ilgili kodların olması gerekirken aşağıdaki gibi bir bilgilendirme görürüz.
// Default code generation is disabled for model D:\Çalışmalarım\EF\POCONedir\POCONedir\AdventureWorks.edmx'.
// To enable default code generation, change the value of the 'Code Generation Strategy' designer
// property to an alternate value. This property is available in the Properties Window when the model is
// open in the designer.
Bu aşamada öncelikle, veri tabanındaki tablomuza karşılık gelen sınıfımızı ve sonrasında Context nesnemizi oluşturacağız.
public class ProductCategory
{
public int ProductCategoryID { get; set; }
public string Name { get; set; }
public Guid rowguid { get; set; }
public DateTime ModifiedDate { get; set; }
}
Burada dikkat etmemiz gereken nokta; kendi yazdığımız ProductCategory sınıfının adının ve özellik adlarının modeldeki isimlerle bire bir aynı olması gerektiğidir. Farklı isimlendirmeler yapmak isterseniz bu durumda çeşitli ayarlamalar yapmanız gerekir.
public class AdventureWorksContext : ObjectContext // Modelimizi ve sınıflarımızı barındıracak Context sınıfımızı yazıyoruz.
Entity Framework ile otomatik oluşturulan Context, ObjectContext sınıfından kalıtılır. Bunu, bir EF oluşturduğunuzda Designer.cs'ine bakarak da görebilirsiniz.
AdventureWorksContext sınıfımızda, ProductCategory sınıfımızı barındıran ObjectSet tipinden bir özelliğimizin olması gerekiyor. Bunu sağlamak için de, aşağıdaki tanımlamaları yapıyoruz.
private ObjectSet<ProductCategory> _productCategory;
public ObjectSet<ProductCategory> ProductCategory
{
get { return _productCategory ?? (_productCategory=CreateObjectSet<ProductCategory>()); }
}
CreateObjectSet() metodu ile yaptığımız örnekleme işlemini Context'imizin Constructor'ında da yapabiliriz. Şu an, sadece tek bir tablo üzerinde çalışıyoruz. O yüzden, bu işlemi Constructor'da yapmak performans ve bellek kullanımı konusunda sorun olmaz ama gerçek hayatta modellerimiz birçok tablodan oluşacak ve bu durumda Constructor'da gereksiz yere yaptığımız örnekleme, çok da optimum bir işlem olmayacaktır. Bu nedenle, en makul yöntem ObjectSet'imizi ihtiyaç duyduğumuzda oluşturmak.
Bu aşamada, Context'imiz hala hangi bağlantı cümleciğini kullandığını bilmiyor. O nedenle Constructor'da bunu bildirmemiz gerekiyor.
public AdventureWorksContext():base("name=AdventureWorksEntities","AdventureWorksEntities"){ }
Buradaki ilk parametre entity data modelimizi oluştururken App.Config dosyasına kaydettiğimiz bağlantı cümlesi(ConnectionString)nin adı, ikinci parametre ise Entity Container'ın adı(). Bu adı, Model'e sağ tıklayıp Model Browser dediğimizde açılan pencereden, AdventureWorksModel'ın altındaki EntityContainer'dan öğrenebilirsiniz.
3- POCO Nesnelerinde CRUD İşlemleri
Veriye erişimi sağlamak için ProductCategoryDataAccess adında yeni bir sınıf oluşturuyoruz ve kodlarımızı bu sınıf içine yazıyoruz.
public ProductCategory Create(ProductCategory category)
{
using (AdventureWorksContext ctx = new AdventureWorksContext())
{
ctx.ProductCategory.AddObject(category);
ctx.SaveChanges();
}
return category;
}
Dikkat edecek olursanız ctx.ProductCategory aslında bizim ObjectSet özelliğimiz. Burada SaveChanges() metodunu çağırmadığımız sürece, veri tabanında herhangi bir işlem gerçekleştirilmez.
public ProductCategory Update(ProductCategory category)
{
EntityKey key = null;
using (AdventureWorksContext ctx = new AdventureWorksContext())
{
//EntitySetBase itemEs = ctx.GetEntitySet(category);
//key = ctx.CreateEntityKey(itemEs.Name, category);
key = ctx.CreateEntityKey(ProductCategory.EntitySetName, category);
ctx.GetObjectByKey(key);
ctx.ApplyCurrentValues(ProductCategory.EntitySetName, category);
ctx.SaveChanges();
}
return category;
}
Context'teki ProductCategory sınıfımızın Model'deki hangi tabloya karşılık geldiğini bulmak için Context sınıfına aşağıdaki gibi GetEntitySet() adında bir metot yazabilir ya da ProductCategory sınıfımızda public const string EntitySetName = "ProductCategory"; şeklinde tanımlayıp direkt kullanabiliriz.
public EntitySetBase GetEntitySet(Object entityType)
{
EntityContainer container = this.MetadataWorkspace.GetEntityContainer(this.DefaultContainerName, DataSpace.CSpace);
EntitySetBase entitySet = container.BaseEntitySets.SingleOrDefault(es => es.ElementType.Name == entityType.GetType().Name);
return entitySet;
}
public void Delete(ProductCategory category)
{
using (AdventureWorksContext ctx = new AdventureWorksContext())
{
ctx.ProductCategory.Attach(category);
ctx.ProductCategory.DeleteObject(category);
ctx.SaveChanges();
}
}
Öncelikle, sileceğimiz category nesnesini Context'imize ekliyoruz. DeleteObject() metodunu çağırdığımızda aslında silme işlemi gerçekleşmiyor; sadece nesnemiz silinecek olarak işaretleniyor, SaveChanges() dediğimizde siliniyor.
public List List<ProductCategory> GetProductCategories()
{
List<ProductCategory> list = null;
using (AdventureWorksContext ctx = new AdventureWorksContext())
{
list = ctx.ProductCategory.ToList();
}
return list;
}
Son olarak da uygulamayı test edelim. WindowsForms uygulamamıza Combobox ekleyip aşağıdaki metodu yazalım. Yazdığımız metodu Constructor'da çağırdığımızda aşağıdaki gibi bir ekran görüntüsü alırız.
void Bind_lstCategory()
{
ProductCategoryDataAccess da = new ProductCategoryDataAccess();
lstCategory.DataSource = da.GetProductCategories();
lstCategory.DisplayMember = "Name";
lstCategory.ValueMember = "ProductCategoryID";
}
Kaynaklar:
- Entity Framework 4.0 Recipes - Larry Tenny, Zeeshan Hirani