"Enter"a basıp içeriğe geçin

Ef Core Migrations ile Veritabanı Objeleri Oluşturma

Entity Framework, projelerde büyük kolaylıklar sağlasa da çoğu zaman bir view veya stored procedur’a başvurmamız gerekebiliyor. Böyle bir durumla karşılaştığımda ilk başlarda gerekli olan view veya stored procedure’u veritabanı üzerinde oluşturup FromSql komutu ile oluşturmuş olduğum veritabanı objelerini çağırıyordum. Bu yolu izlemenin bir kaç sıkıntısı var. Birincisi, bir sebepten dolayı veritabanınızı sıfırlamanız gerektiğinde veritabanı objelerinizi baştan oluşturmanız gerekecek. Diğer bir sıkıntı ise projenizi yayınlayacağınız zaman uzak sunucuda view ve stored procedure’larınızı tekrar oluşturmanızın gerekmesi. Burada devreye “Migration” işlemi giriyor.

Migration işlemi, oluşturmuş olduğumuz entity ve context’imize göre veritabanında hangi işlemlerin gerçekleştirileceğini belirliyor. Entity ve Context dışında, Views, Stored Procedures ve Functions gibi veritabanı objelerini de migration işlemi ile oluşturabiliyoruz.

Örnek Uygulama

İlk olarak bir .net core 3.1 web projesi oluşturuyorum. Ben postgres kullandığım için nuget üzerinden npgsql sürücüsünü kuruyorum. Siz kullandığınız veritabanı için uygun olan paketi yükleyin.

dotnet add package Npgsql --version 3.1.0

.net core 3 ile aşağıdaki komutları kullanabilmek için Microsoft.EntityFrameworkCore.Tools nuget paketini yüklememiz gerekiyor.

Add-Migration
Drop-Database
Get-DbContext
Scaffold-DbContext
Script-Migrations
Update-Database

dotnet add package Microsoft.EntityFrameworkCore.Tools --version 3.1.0

Proje ana dizininde “Models” adında bir klasör oluşturup “Product” ve “Comment” entitylerini oluşturuyorum.

    
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public ICollection<Comment> Comments { get; set; } = new List<Comment>();
    }
       
    public class Comment
    {
        public int Id { get; set; }
        public string Content { get; set; }
        public int? ParentId { get; set; }
        public int ProductId { get; set; }
        public Product Product { get; set; }
    }

Proje ana dizininde “AppDbContext” adında context classımı oluşturuyorum.

       
    public class AppDbContext: DbContext
    {
        public AppDbContext(DbContextOptions options): base(options) { }
        public DbSet Products { get; set; }
        public DbSet Comments { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Seed();
            
        }
    }

“modelBuilder.Seed()” migration işlemi sırasında veritabanına örnek kayıtların eklenmesi için oluşturduğum bir extension method. Eğer extension method kullanmak istemiyorsanız OnModelCreating methodu üzerinde modelBuilder.Entity().HasData() ile seed işlemini gerçekleştirebilirsiniz.

Ana dizinde ModelBuilderExtensions classını oluşturuyorum.

       
    public static class ModelBuilderExtensions
    {
        public static void Seed(this ModelBuilder modelBuilder)    
        {
            modelBuilder.Entity()
                .HasData(
                    new Product
                    {
                        Id = 1,
                        Name = "Wd 40 Çok Amaçlı Pas Sökücü",
                        Price = 36,
                    }
                );
            modelBuilder.Entity()
                .HasData(
                     new Comment {Id = 1, Content = "Yorum 1", ParentId = null, ProductId = 1},
                     new Comment {Id = 2, Content = "Yorum 2", ParentId = null, ProductId = 1},
                     new Comment {Id = 3, Content = "Yorum 3", ParentId = 2, ProductId = 1},
                     new Comment {Id = 4, Content = "Yorum 4", ParentId = 2, ProductId = 1}
                );

        }
    }

Starup.cs üzerinde DbContextimi kaydedip connection stringini belirtiyorum.

       
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext(options =>
            {
                options.UseNpgsql("server=localhost;database=efdatabaseobjects;User Id=postgres;Password=123").UseSnakeCaseNamingConvention();
            });
        }

Postgres kullanırken tablo ve sütun isimlerinde büyük harf kullanırsanız, sql komutları yazarken tablo isimlerini tırnak işaretleri arasında yazmanız gerekiyor bu yüzden UseSnakeNamingConvention ile tablo ve sütun isimlerini küçük harfler kullanarak oluşturuyorum. UseSnakeNamingConvention kullanabilmeniz için EfCore.NamingConvention paketini yüklemeniz gerek. SQL Server kullanıyorsanız yukarıdaki adımı yapmanıza gerek yok.
dotnet add package EFCore.NamingConventions --version 1.1.0

Şimdi sıra migration oluşturup veritabanını oluşturmada.

Init adında bir migration oluşturuyorum.
dotnet ef migrations add Init

Projeye “Migrations” adında bir klasör oluştu. Klasör içinde de (YYYYMMDDHHMMSS)_Init.cs adında migration dosyası bulunuyor. Stored Procedure ve View’larınızı bu migration dosyası içinde oluşturabilirsiniz.

       
 migrationBuilder.sql(@"sql kodu");

Migration dosyaları çoğalınca karışıklık olacağından ben boş bir Migration oluşturup, “SQL” geçen bir ad veriyorum ki daha sonra karışıklık olmasın.

Migration’ı uyguluyorum.

dotnet ef database update

Tablolarım ve örnek verilerim oluştu şimdi sıra Migration ile View ve Stored Procedure oluşturmada

Boş bir migration oluşturuyorum.
dotnet ef migrations add SQL_Objects

    public partial class Sql_Objects : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {

        }
    }

Up ve Down methodlarına direkt olarak kodları eklemektense bir helper class oluşturup kodları eklersek, migration’ın silinmesi veya baştan oluşturulması gerektiğinde kayıp yaşamayız.

Ana dizin üzerinde “MigrationHelpers” adında bir klasör oluşturuyorum. Ardından ViewHelper ve SPHelper adında iki class oluşturuyorum.

ViewHelper

    public static class ViewHelper
    {
        public static void CreateGetCommentsView(MigrationBuilder builder)
        {
            builder.Sql(@"create view get_comments as
            select products.name, comments.content from products
            inner join comments on comments.product_ıd = products.ıd
            ");
        }
        public static void DropGetCommentsView(MigrationBuilder builder)
        {
            builder.Sql("drop view get_comments");
        }
    }

SPHelper

    public class SPHelper
    {
        public static void CreateNewProductSP(MigrationBuilder builder)
        {
            builder.Sql(@"create or replace procedure new_product_sp(
                           ıd int,
                           name text, 
                           price decimal)
                            language plpgsql    
                            as $$
                            begin
	                            insert into products values (ıd, name, price);
                                commit;
                            end;$$
            ");
        }
        public static void DropNewProductSP(MigrationBuilder builder)
        {
            builder.Sql("drop procedure new_product_sp");
        }
    }

Helper classları oluşturduktan sonra oluşturmuş olduğum boş migration dosyasının up ve down methodlarını aşağıdaki gibi dolduruyorum.

    public partial class SQL_Objects : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            ViewHelper.CreateGetCommentsView(migrationBuilder);
            SPHelper.CreateNewProductSP(migrationBuilder);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            ViewHelper.DropGetCommentsView(migrationBuilder);
            SPHelper.DropNewProductSP(migrationBuilder);
            
        }
    }

dotnet ef database update SQL_Objects
Migrationımı uyguluyorum.

View ve Procedure oluştu.

İlk Yorum Sizden Gelsin

Bir cevap yazın

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