Fluent Api Nedir ?
13 Jan 2017Fluent api’yi basitçe tanımlamak gerekirse (code first yaklaşımı ile) veri tabanı sınıflarını ve ilişkilerini yapılandırabilmenin bir yoludur. Veri tabanları bağlamında bir ilişki, bir tablodaki primary key alanının diğer tablodaki foreign key alanına karşılık gelmesi durumudur. İlişkiler (relations), ilişkisel veri tabanların farklı tablolara bölünmesini ve depolanmasını sağlarken, birbirinden farklı veri öğelerini de birbirine bağlar. Böylece, RDBMS yani ilişkisel veri tabanı yönetimi sistemlerinin de tanımını yapmış olduk. Örneğin bir müşteri ve onun siparişleri hakkında bilgi depolamak istersek, biri müşteri (Customer) diğeri sipariş (Order) için iki tablo oluşturmamız gerekir. Bir müşterinin birden fazla siparişi olabileceği için one-to-many yani bire çok ilişki kurduğumuzda kolayca siparişlerine ulaşabiliyor olacağız.
Code First yaklaşımında domain sınıflarına yani database modellerimize yapılandırma eklemenin iki yolu vardır. Biri DataAnnotations diye tabir ettiğimiz property’lere attribute olarak eklemek, diğeri ise (yazımızın da konusu olan) kodda kural olarak tanımlayabilmemizi sağlayan Code First yaklaşımının Fluent API’sini kullanarak.. Farkındayım fazla teknik bir cümle kurdum ama bazı noktalarda gerçekten terimlerin tam olarak Türkçe karşılığı olmayabiliyor. :(
Bu makalede örnek olması için önceden yazmış olduğum bir projem üzerinden Fluent API’yi anlatmaya çalışacağım. Github üzerinden inceleyebilmeniz için yazının sonunda linkini paylaştım. Walltage.Domain adında bir class library projesi oluşturuyoruz. (Siz projenize başka isim verebilirsiniz, buradan kodları kopyalamanız durumunda namespace’lere dikkat etmeniz gerekiyor) Oluşturduğumuz bu projeye içinde entity’lerimizin yer alacağı Entities adında bir klasör ekliyoruz.
İlk olarak BaseEntity adında her tablonun ihtiyacı olacağı (kalıtılabileceği) bir sınıf ekliyoruz.
Bazı tablolarımıza sadece id alanı yeterli olmayabilir, oluşturma ya da düzenleme tarihi gibi ortak kullanılabilecek alanlara da ihtiyacımız olabilir. Bunun için AuditableEntity adında base bir sınıf daha oluşturuyoruz. Fazla detaylı oldu sanki ama olsun fazla bilginin zararı olmaz. ;)
Şimdi Entities klasörü altına User ve UserRole entity’lerimizi ekliyoruz.
Yukarıdaki örnekte Foreign Key ilişkisini DataAnnotations attribute’u (niteliği diye çevirebiliriz) olarak tanımladık ve key olarak UserRoleId alanının kullanılacağını parantez içinde belirttik. Buraya şimdilik çok takılmayalım, daha sonra User entity sınıfımızı aşağıda DataAnnotation yöntemiyle tekrar ele alacağız. UserRole.cs
Görmüş olduğunuz gibi her iki tablonunda birbirleri arasında kolayca gezinebilmesi için ilişkileri mevcut. Şimdi Fluent Api ile bu tablolarımızın ilk defa oluşturulurken dikkate alınması gereken yapılandırmasını tanımlayabiliriz. Domain katmanımıza Mappings adında klasörümüzü ekliyoruz. İlk olarak yine ortak kullanılacak ve içinde Id alanımızın da olduğu bir BaseEntityMap sınıfı oluşturuyoruz. Bu yapılandırmaları context’imizin oluştuğu sırada OnModelCreate methodunu override edebileceğimiz EntityTypeConfiguration sınıfından yararlanıyoruz. BaseEntityMap.cs
HasKey, veri tabanımızda primary key olarak tanımladığımız alan ve TEntity’ye hangi entity’i verirsek o tabloya Id alanını primary key olarak atama yapacak. Diğer özelliğimiz HasDatabaseGeneratedOption ise veri tabanımızda id alanının nasıl oluşturulacağını belirtmemizi sağlar. Burada auto-increment şeklide id’lerimizi artmasını istediğimiz için DatabaseGeneratedOption.Identity seçeneğini kullanıyoruz. UserMap.cs
Yukarıdaki UserMap constructor’ı ile tabloda özellikleri eşleştirmek ve yapılandırmak için Fluent Api’yi kullandık, bu yapılandırmadaki ayarları birer birer görelim. HasKey() : Tabloda Primary key ataması yapılandırmanızı sağlar. Property() : Belirli bir alana tablodaki durumu için özellik atamak için kullanılır. Buradaki örnekte IsRequired(); ilgili alanın zorunlu oluğunu, IsOptional(); ilgili alanın zorunlu olmadığını, opsiyonel olduğunu, HasMaxLength(); ilgili alanın maksimum karakter sayısı parantez içinde belirtilmiştir. ToTable() : İlgili entity’nin tablo adını yapılandırmanızı sağlar. Genelde entity adı ile aynı kullanılır. HasRequired() : İlgili entity’nin gerekli olan ilişkisini yapılandırmanızı sağlar. Örnekte User tablosunda UserRole foreign key ilişkisinin UserRoleId alanı ile zorunlu olduğunu diğer bir deyişle bu alan olmadan kaydedilemeyeceğini belirttik. WillCascadeOnDelete() ise ilgili UserRole’un silinmesi durumunda kullanıcının silinmemesi yapılandırmasıdır(Sql’deki constraint). UserRoleMap.cs
Buraya kadar entity’lerimizi ve Fluent Api ile yapılandırmalarımızı tamamladık, artık context sınıfımızı oluşturabiliriz. Oluşturacağımız sınıf EntityFramework ile gelen DbContext sınıfından türetilmeli, böylelikle yukarıda da bahsettiğimiz gibi yapılandırmalarımızın da geçerli olabilmesi için OnModelCreating() methodunu override edebileceğiz. WalltageDbContext.cs
OnModelCreating() methodunda DbModelBuilder nesnesi ile yapılandırmalarımızı ekleyerek model oluştuğunda dikkate alınmasını sağlamış olduk. Structure görünümün ü merak ediyorsanız şu şekilde; Yazıya son vermeden önce User entity’mizin DataAnnotation yöntemi ile yapılandırılmış haline bakalım.
Bu şekilde kullanım diğer yönteme göre daha kolay gelebilir. Tamamen tercihinize bağlı fakat şu detayı belirtmeliyim ki yukarıda alan özelliklerinin yanı sıra sadece foreign key ilişkisini belirtebildik. Fluent Api ile foreign key ilişkisinin ne şekilde olacağını ve hatta constraint tanımını bile ekleyebiliyoruz. Her iki yönteminde kendine göre avantaj ve dezavantajları var. Bu konuyu yorumlarda tartışabiliriz. Yeni bir yazı da tekrar görüşmek üzere, kendinize iyi bakın ! :)
Örnek Proje Repo: https://github.com/abdurrahman/walltage
Kaynaklar;
- https://msdn.microsoft.com/tr-tr/data/jj591617
- https://msdn.microsoft.com/tr-tr/data/jj591620
- https://www.tutorialspoint.com/entity_framework/entity_framework_fluent_api.htm
- http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-first-approach-w/