Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Entity Framework Core – Keyless Entity Types

Merhaba,

Uygulamalarda kullanılan ORM’ler her ne kadar bizlerin yerine sorgu üretip, çalıştırsa da bazen sunucu tarafında hazır bir query barındıran view yahut stored procedure gibi yapılanmalardan gelen result’ların da ORM vasıtasıyla Entity Type misali kullanılması ihtiyaç olabilmektedir. İşte bu durumda, Entity Framework Core 2.1 ile Query Types olarak hayatımıza giren ve 3.1 sürümünde Keyless Entity Types olarak tekrar isimlendirilen özellik ile bu ihtiyaç desteklenmiş ve rahatça çözülebilir hale getirilmiştir.

Keyless Entity Types; normal entity type’lara ek olarak, primary key içermeyen query’lere karşı veritabanı sorguları yürütmek için kullanılabilmektedir.

Genellikle group by, pivot vs. gibi işlemlerle yapılan istatistiksel çalışmalar neticesinde üretilen veriler primary key barındırmayan sorgu sonuçları doğurmaktadırlar. Bizler bu tarz sorguları Keyless Entity Types özelliği ile sanki bir entity’e karşılık geliyormuş gibi Entity Framework Core yapılanmasına dahil edebiliyor ve çalışmalarımızı gerçekleştirebiliyoruz.

İlgili özelliği örneklendirebilmek için direkt bir senaryo üzerinden pratikleştirelim. Şöyle ki; ‘Employees’ ve ‘Sales’ olmak üzere aralarında bire çok ilişki olan iki adet tablo ve bir adet, sunucuda hangi employee’un kaç adet satış yaptığını hesaplayan bir group by işlemi yapan view tasarlayalım.

Buyrun başlayalım…

Entityler;

    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Position { get; set; }
        public ICollection<Sale> Sales { get; set; }
    }
    public class Sale
    {
        public int Id { get; set; }
        public int EmployeeId { get; set; }
        public string SaleExplanation { get; set; }
        public Employee Employee { get; set; }
    }

Context nesnesi;

    public class CompanyContext : DbContext
    {
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Sale> Sales { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Employee>().HasData(
                new Employee { Id = 1, Name = "Hilmi", Surname = "Celayir" },
                new Employee { Id = 2, Name = "Hüseyin", Surname = "Sümer" },
                new Employee { Id = 3, Name = "Şuayip", Surname = "Aldatmaz" }
                );
            modelBuilder.Entity<Sale>().HasData(
                new Sale { Id = 1, EmployeeId = 1 },
                new Sale { Id = 2, EmployeeId = 2 },
                new Sale { Id = 3, EmployeeId = 3 },
                new Sale { Id = 4, EmployeeId = 1 },
                new Sale { Id = 5, EmployeeId = 2 },
                new Sale { Id = 6, EmployeeId = 3 },
                new Sale { Id = 7, EmployeeId = 3 },
                new Sale { Id = 8, EmployeeId = 3 },
                new Sale { Id = 9, EmployeeId = 1 }
                );

            base.OnModelCreating(modelBuilder);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=.;Database=CompanyDb;Trusted_Connection=True;");
            base.OnConfiguring(optionsBuilder);
        }
    }

Bu tasarımlardan sonra Package Manager Console üzerinden add-migration mig_1 talimatını verelim ve oluşturulan migration’ın ‘Up’ fonksiyonunun en son satırına aşağıdaki ‘View_EmployeeSales’ isimli view’i oluşturan kodları yerleştirelim.

using Microsoft.EntityFrameworkCore.Migrations;

namespace KeylessEntityTypes.Migrations
{
    public partial class mig_1 : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            .
            .
            .
            migrationBuilder.Sql(@"CREATE VIEW View_EmployeeSales
                                   AS
                                   SELECT e.Name, COUNT(*) [Total Sales] FROM Employees e
                                   JOIN Sales s
                                   ON e.Id = s.EmployeeId
                                   GROUP BY e.Name
                                ");
        }
    }
}

Ve son olarak update-database talimatıyla tasarlanan yapıyı migrate edelim.

Şimdi oluşturulan veritabanında ‘View_EmployeeSales’ view’ini sorgulayalım ve nasıl bir sonuç geldiğini hep beraber gözlemleyelim.
Entity Framework Core - Keyless Entity Types
Evet… Görüldüğü üzere bu sonucu Entity Framework Core ORM’sinin ‘Keyless Entity Types’ özelliği sayesinde bir entity ile eşleştirecek ve daha rahat sorgulayabileceğiz.

Bunun için devam edelim…

Öncelikle sorgu sonucuna uygun modelde bir entity tasarlayalım. Misal ‘EmployeeSale’ isminde olabilir…

    public class EmployeeSale
    {
        public string Name { get; set; }
        public int TotalSales { get; set; }
    }

Ardından bu entity’i context nesnesine hem DbSet olarak ekleyelim hem de override edilen ‘OnModelCreating’ metodu içerisinde entity’e uygun aşağıdaki konfigürasyonları gerçekleştirelim.

    public class CompanyContext : DbContext
    {
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Sale> Sales { get; set; }
        public DbSet<EmployeeSale> EmployeeSales { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<EmployeeSale>(es =>
                es.HasNoKey()
                  .ToView("View_EmployeeSales")
                  .Property(p => p.TotalSales).HasColumnName("Total Sales"));
            .
            .
            .

Yukarıdaki kod bloğunu incelerseniz eğer, 5. satırda ilgili entity DbSet olarak eklenmekte ve 8. satırda ise eklenen entity’nin konfigürasyonları gerçekleştirilmektedir. 9. satırda ‘HasNoKey’ fonksiyonu ile ilgili entity’nin bir primary key’i olmadığını, 10. satırda sunucudaki ‘View_EmployeeSales’ isimli view’e karşılık geldiğini ve 11. satırda ise ‘Total Sales’ olarak gelen kolonun ‘TotalSales’ propertysi ile eşleşmesi gerektiğini ifade etmekteyiz.

Sonuç olarak yapılan bu inşa neticesinde ilgili DbSet’i sorgularsak eğer;
Entity Framework Core - Keyless Entity Types
şeklinde sonuçları başarıyla alabilmekteyiz.

Ayrıca burada primary key olmayan veri illa ki bir view’den değil, LINQ sorgusundan da geliyor olabilir… Evet, yanlış okumadınız 🙂 Keyless Entity Types özelliğini kullanabilmek için arkada çalışacak olan query bir view olacağı gibi bir LINQ sorgusuda olabilir.

Bunun için ‘OnModelCreating’ metodunda yapılan konfigürasyonun aşağıdaki gibi değiştirilmesi yeterli olacaktır.

            modelBuilder.Entity<EmployeeSale>(es =>
                es.HasNoKey()
                  .ToQuery(() => Sales.GroupBy(s => s.Employee.Name)
                                      .Select(es => new EmployeeSale
                                      {
                                          Name = es.Key,
                                          TotalSales = es.Count()
                                      })));

Görüldüğü üzere ‘ToQuery’ fonksiyonu sayesinde herhangi bir view’e bağımlılık olmaksızın LINQ ile sorgu oluşturulabilmektedir.

[Keyless] Attribute’u

Primary key’i olmayan entity’ler de ‘OnModelCreating’ üzerinden ‘HasNoKey’ fonksiyonu ile bildirim yapılmasına alternatif olarak ‘Keyless’ attribute’u üretilmiştir. Bu attribute sayesinde primary key’i olmayan entity direkt işaretlenerek bildiride bulunulabilmektedir.

    [Keyless]
    public class EmployeeSale
    {
        public string Name { get; set; }
        public int TotalSales { get; set; }
    }

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*