Bazı sebeplerden ötürü DbContext nesneninizi Webapi içerisinde değil de bir classlibrary projesi içerisinde tutmak isteyebilirsiniz, eğer böyle bir projeniz var ise Migration alma ve Veri tabanı güncelleme işlemlerinde DesignTimeDbContext ile ilgili bir hata alırsınız.
Şimdi bu projenin bir örneğini yapmak için öncelikle promizi oluşturalım aşağıdaki kodları sırayla çalıştıralım.
1 2 3 4 5 6 7 |
mkdir efcoredesign cd efcoredesign dotnet new classlib -n Domain dotnet new classlib -n Infrastructure dotnet new webapi -n MyApi dotnet add ./Infrastructure/Infrastructure.csproj reference ./Domain/Domain.csproj dotnet add ./MyApi/MyApi.csproj reference ./Infrastructure/Infrastructure.csproj |
Kodları sırayla çalıştırdığınızda bir adet klasör klasör altında 3 adet proje (2si classlibrary ve 1i webapi) oluşturulacak sonrasında ise referans olarak ekleme işlemleri mevcut
Domain projesine herhangi bir referans eklemiyoruz Infrastructure projesine Domain projesini referans olarak ekliyoruz sonrasında ise MyApi projesine de Infrastructure projesini referans olarak ekliyoruz. Sıra geldi veri tabanı ve tablomuza karşılık gelecek olan classlar’ı oluşturmaya.
Domain altında bir adet Catalog adlı klasör oluşturdum ve bu klasör altında Blog adlı bir sınıf bu sınıf aşağıdaki şekildedir.
1 2 3 4 5 6 7 8 |
namespace Domain.Catalog; public class Blog { public int Id { get; set; } public string Title { get; set; } = null!; public string? Content { get; set; } } |
Şimdi de DbContext’imizi oluşturmamız gerekiyor.
Devam etmeden önce Microsoft.EntityFrameworkCore ve Microsoft.EntityFrameworkCore.Design ve SqlServer paketlerini Infrastructure projemize ekleyelim.
1 2 3 |
dotnet add ./Infrastructure/Infrastructure.csproj package Microsoft.EntityFrameworkCore dotnet add ./Infrastructure/Infrastructure.csproj package Microsoft.EntityFrameworkCore.Design dotnet add ./Infrastructure/Infrastructure.csproj package Microsoft.EntityFrameworkCore.SqlServer |
Aşağıdaki kodlar Infrastructure projesine gerekli paketleri kuracaktır.
Sonrasında Infrastructure projesi içerisinde Persistence adlı bir klasör oluşturdum ve bu klasörün altında AppDbContext adlı bir sınıf oluşturdum bu sınıf aşağıdaki şekildedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using Domain.Catalog; using Microsoft.EntityFrameworkCore; namespace Infrastructure.Persistence; public class AppDbContext : DbContext { public DbSet<Blog> Blogs { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } } |
Burada DbSet property’sini eklemeyi unutmayın eğer eklemezseniz veri tabanında projeniz oluşmaz.
Şimdi gelelim veri tabanımızı kullanmaya bildiğiniz gibi veri tabanını WebApi içerisindeki servislerimize ekleyelim.
Şimdi veri tabanımızı servislerimize ekledik hadi gelelim ve migration almaya çalışalım.
1 |
dotnet ef migrations add InitMigration --project ./Infrastructure/Infrastructure.csproj |
Bu kodu çalıştırdıktan sonra ekrana hata almamız lazım yukarıda bahsettiğim hata aşağıdaki şekildedir
Unable to create an object of type ‘AppDbContext’. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
Burada Infrastructure projesi içerisindeki DbContext’imizi tasarım esnasında oluşturamadığından bahsediyor buradaki DbContext’i design esnasında yani projeyi çalıştırmadan oluşturabilmesi için bir adet işlem gerekli.
Persistence altında IDesignTimeDbContext interface’inden türetilmiş bir adet class oluşturuyorum benim oluşturduğum class aşağıdadır.
1 2 3 4 5 6 7 8 9 10 11 |
using Microsoft.EntityFrameworkCore.Design; namespace Infrastructure.Persistence; public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> { public AppDbContext CreateDbContext(string[] args) { throw new NotImplementedException(); } } |
EFCore burada şunu demek istiyor IDesignTimeDbContextFactory interface’inden bir adet sınıf oluştur ve bu sınıftan bana AppDbContext dönsün.
Burada dikkat etmemiz gereken bir durum mevcut 1.si bizim DbContext sınıfımızı set etmemiz için ihtiyacımız olan şey nedir?
1 2 3 4 5 6 7 8 |
public class AppDbContext : DbContext { public DbSet<Blog> Blogs { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } } |
4.Satıra baktığınızda Constructor’ımızın DbContextOptions aldığını görebilirsiniz yani doğrudan geriye return new AppDbContext() diyemiyoruz. Bunun için DbcontextOptions’ını build etmemiz gerekiyor. Build etmek derken oluşturmamız ve new AppDbContext(options) şeklinde geçmemiz gerekiyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; namespace Infrastructure.Persistence; public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> { public AppDbContext CreateDbContext(string[] args) { var dbContextOptionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); dbContextOptionsBuilder.UseSqlServer("//Bağlantı dizeniz"); return new AppDbContext(dbContextOptionsBuilder.Options); } } |
yukarıda sınıfın güncellenmiş satırlarını işaretledim bu satırlara dikkatli bir şekilde bakarsanız geriye bir AppDbContext sınıfını döndüğümü görmüşsünüzdür bu sınıfı kaydettikten sonra tekrar deneyelim ?
1 |
dotnet ef migrations add InitMigration --project ./Infrastructure/Infrastructure.csproj |
Vola! kodu çalıştırdıktan sonra eğer tüm adımları doğru bir şekilde takip ettiyseniz Infrastructure projesi altında Migrations klasörünün oluştuğunu göreceksiniz. Migration işlemini alabildik.Eğer sqlServer bağlantı metnini de verirseniz database update diyerek veri tabanını da oluşturabilirsiniz.
Sadece hata gidermek için gelenler buraya kadar okusa yeter, dahasını öğrenmek istiyorsanız buyrun devam edelim.
AppDbContextFactory classımıza dikakt ettiyseniz içerisinde string[] args parametrelerini alan bir fonksiyon mevcut, eğer bir üniversitede nesne tabalı programlamaya giriş dersi aldıysanız static void main(string[] args) ifadesi ile tanışıksınızdır, aslında buradaki string[] args ifadesi de aynı şeyi tetikliyor.
Şimdi biz migration eklerken konsolda neler dönüyor bir bakalım.
burada 1ve 2. satırlarda projeyi build aldığında görebilirsiniz eğer build işlemi başarılıysa devam ediyor.
3 ile işaretlediğim satırı lütfen es geçin benim yüklemiş olduğum dotnet ef tool’u 7.0.5 fakat proje içerisindeki eklemiş olduğumuz paket ef 7.0.13 bu uyarının sebebi bu bilseniz yeterli.
4 ile işaretlediğim alanda ise Done. ifadesini görmekteyiz yani işlem tamam. Peki ben bu AppDbContext sınıfındaki CreateDbContext(string[] args) ifadesi içerisinde Console.WriteLine() ile bir şey yazdırırsam ne olur? bakalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; namespace Infrastructure.Persistence; public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> { public AppDbContext CreateDbContext(string[] args) { Console.WriteLine("Veri tabanı objesi oluşturuluyor"); var dbContextOptionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); dbContextOptionsBuilder.UseSqlServer("//Bağlantı dizeniz"); return new AppDbContext(dbContextOptionsBuilder.Options); } } |
Yukarıda işaretlediğim satırda eklediğim işlemi görebilirsiniz.
Kırmızı ok ile işaretlediğim alanda bu çıktıyı görüyoruz. Buradan şunu söyleyebiliriz dotnet ef arka tarafta bir console uygulaması çalıştırıyor.
Peki string[] args ne ola ki?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; namespace Infrastructure.Persistence; public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> { public AppDbContext CreateDbContext(string[] args) { Console.WriteLine("args değerleri yazdırılıyor..."); foreach (var arg in args) { Console.WriteLine(arg); } System.Console.WriteLine("args değerleri yazdırıldı."); var dbContextOptionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); dbContextOptionsBuilder.UseSqlServer("Server=.;Database=MyBlogDb;Trusted_Connection=True;"); return new AppDbContext(dbContextOptionsBuilder.Options); } } |
Sınıfı aşağıdaki gibi güncelledim ve args içerisindeki arg’ları ekrana yazdırdım =)
Çalıştırdığım kod ve aldığım çıktı aşağıdaki gibidir.
Gördüğünüz gibi args değerli bomboş olduğu için sadece üstündeki ve altındaki ifadeleri yazdırdı. Peki nasıl args değerleri geçebilirim?
Eğer dotnet ef kodunu çalıştırırken işaretlediğim kutudaki gibi — ifadesinden sonra bir metin veya metinler verirseniz bunlar projenize args olarak geçecektir.
Ve gördüğünüz gibi bu sefer mySqlServer şeklinde ekrana da yazdırdı.
Şimdi bunu niye anlattığıma gelecek olursak =)
Aşağıdaki gibi bir kullanım ile veri tabanınızın bağlantı dizesini parametre ile geçebileceğinizi göstermek istedim. Çoğu zaman bağlantı dizeleri appsettings.json altında tutulur fakat birden fazla kişi projeyi kullandığı zaman herkesin local environment’ındaki sql bağlantı dizesi farklı olabilir ; Örneğin ben Integrated Security=true kullanmıyorum bir başkası kullanabilir bunun için class’ımızı aşağıdaki gibi güncelleyebiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; namespace Infrastructure.Persistence; public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> { public AppDbContext CreateDbContext(string[] args) { var firstArgConnectionString = args.First(); if (string.IsNullOrWhiteSpace(firstArgConnectionString)) { throw new ArgumentNullException(nameof(firstArgConnectionString)); } var dbContextOptionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); dbContextOptionsBuilder.UseSqlServer(firstArgConnectionString); return new AppDbContext(dbContextOptionsBuilder.Options); } } |
Artık bağlantı dizesi vermeden dotnet ef migrations add işlemi yapmaya çalıştığımızda şununla karşılaşacağız..
Ve tabiki de migration alma işlemimiz başarısız olacak.
doğru kullanım ise örnek için aşağıdaki gibi olabilir.
1 |
dotnet ef database update --project ./Infrastructure/Infrastructure.csproj -- "Server=localhost;User Id=sa;Password=P4$$W0Rd;Trust Server Certificate=true" |
Burada Server ila başlayıp true ile biten ifadede kendi bağlantı metninizi verebilirsiniz ve veri tabanınızı güncelleyebilirsiniz.
Bu yazım da bu kadar olsun sağlıcakla kalın.