Entity Framework Core dan Beberapa Penyedia Basis Data

.NET Tools Data ReSharper Platform Rider

Sementara banyak pengembang perangkat lunak lebih suka menjual perangkat lunak sebagai layanan (SaaS), ada pasar untuk menawarkan solusi yang di-hosting-sendiri kepada pelanggan Anda yang disesuaikan dengan pilihan infrastruktur mereka. Tentu saja, salah satu elemen penting dari setiap solusi perangkat lunak modern adalah database, tetapi pada tahun 2022 ada ratusan solusi penyimpanan yang telah teruji dalam pertempuran. Misalnya, pustaka Entity Framework Core mendukung lebih dari sepuluh mesin basis data populer, termasuk Microsoft SQL Server, Oracle, PostgreSQL, MySQL, dan SQLite. Terkadang, Anda mungkin ingin menggunakan lebih dari satu penyedia database yang disebutkan.

Posting ini akan melihat konfigurasi proyek .NET Anda untuk mendukung beberapa penyedia database saat menggunakan model yang sama untuk mengakses database yang mendasari Anda.

Mengapa Saya Ingin Banyak Penyedia Basis Data?

Ada banyak alasan untuk bekerja dengan beberapa penyedia database di aplikasi Anda.

Mungkin Anda sedang membangun aplikasi yang didistribusikan sebagai produk Software-as-a-Service (SaaS) dan produk mandiri yang dapat diinstal pelanggan Anda di pusat data mereka. Misalnya, SaaS Anda mungkin menggunakan PostgreSQL, sementara pelanggan Anda mungkin memiliki standar pada Microsoft SQL Server dan mengharuskan Anda untuk mengirimkan versi yang sesuai dengan lanskap teknologi mereka.

Mungkin pelanggan Anda ingin menerapkan aplikasi Anda pada database pilihan mereka karena alasan lain, seperti tidak memiliki kapasitas untuk mengelola jenis server database tambahan, lisensi, atau persyaratan hukum dan kepatuhan. Beberapa pelanggan menginginkan pilihan untuk membuat keputusan mereka, dan kurangnya dukungan untuk beberapa opsi basis data mungkin pada akhirnya menjadi pemecah kesepakatan.

Persyaratan Untuk Memulai

Sebelum melompat ke kode, Anda memerlukan yang berikut ini. Juga, saya telah mendorong sampel lengkap ke repositori GitHub untuk mereka yang terburu-buru.

  1. JetBrains Rider atau Visual Studio dengan ReSharper
  2. .NET 7 pratinjau
  3. Instance PostgreSQL (lokal, jarak jauh, atau wadah)

Pendekatan yang dijelaskan dalam artikel ini akan menggunakan pratinjau Entity Framework Core 7, tetapi pendekatan ini juga harus bekerja dengan Entity Framework Core 6 dan .NET 6. Anda dapat memodifikasi proyek sampel untuk menargetkan versi sebelumnya.

Solusi, Proyek, dan Referensi

Migrasi database adalah salah satu fitur terbaik dari Entity Framework Core. Migrasi database memungkinkan Anda mengubah model objek dan menghasilkan langkah-langkah untuk mengubah database Anda. Kami perlu membuat migrasi untuk setiap penyedia basis data baru yang kami sertakan dalam solusi kami. Oleh karena itu, Anda harus menyimpan migrasi setiap penyedia database dalam proyek independen.

Dalam contoh ini, kita akan menggunakan dua penyedia database yang berbeda: SQLite dan PostgreSQL. Tentu saja, Anda dapat memilih penyedia basis data yang didukung oleh Entity Framework Core, tetapi perlu diingat bahwa semakin banyak fitur yang tumpang tindih antar penyedia, semakin mudah untuk mempertahankan solusi Anda.

Solusi dua penyedia akan membutuhkan empat proyek dalam kasus penggunaan yang paling mudah.

  1. Proyek Host (ASP.NET Core dalam kasus ini)
  2. Proyek Model (DbContext dan model)
  3. Proyek Migrasi PostgreSQL
  4. Proyek Migrasi SQLite

Menggunakan diagram ketergantungan proyek JetBrains Rider, kita dapat melihat bagaimana solusi akhir akan terlihat.

Diagram ketergantungan proyek solusi

Seperti yang Anda lihat, host kami bergantung pada setiap proyek migrasi, sementara setiap proyek merujuk pada proyek model. Dengan Entity Framework Core, proyek host juga akan berfungsi ganda sebagai titik masuk kami untuk alat Entity Framework Core yang diperlukan untuk membangun, menjalankan, dan menghasilkan migrasi kami.

Jika Anda mengikuti, harap buat solusi yang meniru diagram sebelumnya sebelum melanjutkan ke bagian berikutnya. Setelah selesai, Anda harus memiliki solusi dengan empat proyek. Saya telah menyertakan proyek migrasi saya di a Migrasi folder solusi.

Struktur solusi akhir dari proyek sampel

Perkakas Inti Kerangka Entitas

Elemen penting untuk bekerja dengan Entity Framework Core adalah perkakas baris perintah. Untuk menginstal perkakas, jalankan perintah berikut di terminal JetBrains Rider.

dotnet new tool-manifest 
dotnet tool install dotnet-ef --prerelease 

Itu --prerelease flag hanya diperlukan jika Entity Framework Core 7 masih belum dirilis saat Anda membaca posting ini.

Jika Anda telah berhasil menginstal perkakas Entity Framework Core, jalankan dotnet ef –version perintah terminal di root solusi Anda akan menghasilkan nomor versi yang saat ini diinstal untuk Entity Framework Core.

Entity Framework Core .NET Command-line Tools
7.0.0-preview.6.22329.4

Saya sangat merekomendasikan menginstal alat secara lokal. Ini dapat membantu Anda menjaga perkakas Anda tetap sinkron dengan solusi Anda.

Ketergantungan NuGet

Mari pasang semua dependensi NuGet Anda di setiap proyek. Kita akan mulai dengan model project, karena project dependen dapat secara transitif merujuk dependensi tersebut dalam project migrasi kami. Yang mengatakan, tidak ada salahnya untuk memiliki dependensi yang lebih eksplisit untuk yang sangat berhati-hati.

di kamu model proyek, pastikan Anda memiliki Ketergantungan Inti Kerangka Entitas berikut, pastikan untuk mencocokkan versi yang sama dengan alat.

  • Microsoft.EntityFrameworkCore.Relational
  • Microsoft.EntityFrameworkCore

Karena setiap proyek migrasi menargetkan penyedia basis data yang berbeda, kita memerlukan paket khusus basis data.

  • Microsoft.EntityFrameworkCore.Relational
  • Microsoft.EntityFrameworkCore.Design
  • Npgsql.EntityFrameworkCore.PostgreSQL (Proyek migrasi Postgres)
  • Microsoft.EntityFrameworkCore.Sqlite (proyek migrasi SQLite)

Secara opsional, Anda juga dapat secara eksplisit mereferensikan paket yang sama ini di proyek host Anda, tetapi seperti yang disebutkan sebelumnya, .NET akan mereferensikannya secara implisit.
Paket penting untuk menghasilkan migrasi adalah Microsoft.EntityFrameworkCore.Desain. Ini membantu alat Entity Framework Core CLI menghasilkan migrasi yang sesuai. Jika Anda lupa untuk mereferensikannya, output CLI akan cukup baik untuk mengingatkan Anda untuk menambahkannya.

Model Basis Data

Jika Anda terbiasa dengan Entity Framework Core, Anda mungkin sudah tahu cara mendefinisikan dan mengimplementasikan a DbContext kelas. Namun, saya akan menunjukkan kepada Anda implementasi dasar bagi mereka yang tidak begitu akrab dengan perpustakaan.

using Microsoft.EntityFrameworkCore;

namespace BoxedSoftware.Models;

public class VehiclesContext : DbContext

    public VehiclesContext(DbContextOptions<VehiclesContext> options)
        : base(options)
    
    
    
    public DbSet<Vehicle> Vehicles  get; set; 


public class Vehicle

    public int Id  get; set; 
    public string VehicleIdentificationNumber  get; set;  = "";
    public string Model  get; set;  = "";
    public string Type  get; set;  = "";
    public string Fuel  get; set;  = "";

Anda dapat mengganti model apa pun yang Anda inginkan sebagai pengganti ini DbContext. Namun, Anda perlu menambahkan kode ke model proyek.

Saya juga menyertakan IntializeAsync implementasi untuk menyemai database dengan data, dan saya menggunakan Palsu perpustakaan untuk menghasilkan dataset acak.

   public static async Task InitializeAsync(VehiclesContext db)
    
        await db.Database.MigrateAsync();
        
        // already seeded
        if (db.Vehicles.Any())
            return;
        
        // sample data will be different due
        // to the nature of generating data
        var fake = new Faker<Vehicle>()
            .Rules((f, v) => v.VehicleIdentificationNumber = f.Vehicle.Vin())
            .Rules((f, v) => v.Model = f.Vehicle.Model())
            .Rules((f, v) => v.Type = f.Vehicle.Type())
            .Rules((f, v) => v.Fuel = f.Vehicle.Fuel());

        var vehicles = fake.Generate(100);
        
        db.Vehicles.AddRange(vehicles);
        await db.SaveChangesAsync();
    

Metode ini juga akan menjalankan migrasi kami untuk tujuan demo. Strategi migrasi yang Anda pilih sepenuhnya terserah Anda. Anda dapat menjalankan migrasi secara terprogram, membuat skrip, atau bahkan membuat migrasi yang baru ditambahkan yang dapat dieksekusi.

Menambahkan Migrasi Untuk Setiap Penyedia

Kami telah mencapai bagian paling penting dari keseluruhan artikel ini, bagian di mana kami dapat menambahkan migrasi ke setiap penyedia basis data. Alat Entity Framework Core CLI membutuhkan proyek host untuk dibangun dan dijalankan untuk menghasilkan migrasi kami. Dalam hal artikel ini, kami memiliki aplikasi ASP.NET Core.

Langkah pertama adalah menambahkan string koneksi ke . kami pengaturan aplikasi.json file untuk setiap database ke proyek host kami.

 "ConnectionStrings": 
        "Sqlite" : "Data Source=vehicles.db",
        "Postgres" : "User ID=postgres;Password=Pass123!;Server=localhost;Port=5432;Database=vehicles;"
 

Ingat, penyedia konfigurasi ASP.NET Core menumpuk. Anda dapat menempatkan nilai di beberapa lokasi dan mengganti nilai yang ditentukan sebelumnya bergantung pada urutan penyedia. Jadi sementara untuk artikel ini, kami menggunakan pengaturan aplikasi.jsonAnda dapat dengan mudah menambahkan nilai string koneksi ke variabel lingkungan atau rahasia pengguna.

Kami sekarang membutuhkan proyek host kami untuk memahami penyedia basis data mana yang ingin kami gunakan saat menjalankan aplikasi. Pendekatan ini adalah salah satu dari banyak, tetapi bekerja untuk demo kami. Jika Anda ingin mengubah atau men-tweak strategi, jangan ragu untuk melakukannya.

Anda akan menambahkan kode berikut di kami Program.cs file sebelum menelepon builder.Build().

// include this namespace
// using static BoxedSoftware.Provider;

var config = builder.Configuration;

builder.Services.AddDbContext<VehiclesContext>(options =>

    var provider = config.GetValue("provider", Sqlite.Name);

    if (provider == Sqlite.Name)
    
        options.UseSqlite(
            config.GetConnectionString(Sqlite.Name)!,
            x => x.MigrationsAssembly(Sqlite.Assembly)
        );
    

    if (provider == Postgres.Name) 
        options.UseNpgsql(
            config.GetConnectionString(Postgres.Name)!,
            x => x.MigrationsAssembly(Postgres.Assembly)
        );
    
);

Anda juga memerlukan kelas pembantu ini, tetapi Anda dapat menggunakan string konstan dengan mudah.

public record Provider(string Name, string Assembly) 

    public static Provider Sqlite = new (nameof(Sqlite), typeof(Sqlite.Marker).Assembly.GetName().Name!);
    public static Provider Postgres = new (nameof(Postgres), typeof(Postgres.Marker).Assembly.GetName().Name!);

Itu Marker kelas adalah tipe kosong di dalam masing-masing Migrasi proyek digunakan untuk mendapatkan nama Majelis lebih konsisten dan menghindari kesalahan refactoring nanti.

public abstract class Marker 

Selanjutnya, mari kita jalankan beberapa perintah CLI untuk menghasilkan migrasi untuk setiap database. Sekali lagi, Anda akan menjalankan perintah ini dari folder solusi root.

dotnet ef migrations add <Migration Name> --project ./Migrations/BoxedSoftware.Sqlite -- --provider Sqlite

dotnet ef migrations add <Migration Name> --project ./Migrations/BoxedSoftware.Postgres -- --provider Postgres

Perhatikan penggunaan “–” untuk memasukkan nama penyedia. Parameter CLI apa pun setelah “–” akan diabaikan oleh perintah saat ini dan dikirim ke aplikasi Anda. Anda kemudian dapat meneruskan argumen ke panggilan WebApplicationBuilder.CreateBuilder untuk mengurai dan menambahkan nilai-nilai itu ke IConfiguration contoh. Misalnya, kode sebelumnya yang kami tambahkan ke Program.csAnda dapat melihat panggilan berikut ke konfigurasi kami untuk mengambil nilai penyedia sambil menawarkan default yang aman dari “sqlite”.

var provider = config.GetValue("provider", Sqlite.Name);

Setelah menjalankan perintah, Anda akan melihat Migrasi folder di setiap proyek khusus database. Kami sekarang siap untuk menjalankan aplikasi kami.

Menjalankan Aplikasi Kami dengan Penyedia Basis Data

JetBrains Rider menawarkan cara langsung menjalankan aplikasi Anda dengan argumen program tambahan. Anda akan menggunakan konfigurasi run untuk meneruskan penyedia yang benar untuk setiap instance aplikasi khusus penyedia.

Buka opsi konfigurasi run Anda dengan mengklik elipsis di widget run Anda di bagian atas JetBrains Rider. Setelah dibuka, Anda harus menambahkan “provider=Sqlite” untuk satu konfigurasi run dan “provider=Postgres” ke duplikat dari konfigurasi run asli.

Jalankan konfigurasi untuk SQLite dan PostgreSQL

Terakhir, mari tambahkan beberapa titik akhir ASP.NET Core ke aplikasi kita untuk menguji fungsionalitasnya. Saya telah menyertakan seluruh aplikasi untuk kejelasan di bawah ini.

using BoxedSoftware.Models;
using Microsoft.EntityFrameworkCore;
using static BoxedSoftware.Provider;

var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;

builder.Services.AddDbContext<VehiclesContext>(options =>

    var provider = config.GetValue("provider", Sqlite.Name);

    if (provider == Sqlite.Name)
    
        options.UseSqlite(
            config.GetConnectionString(Sqlite.Name)!,
            x => x.MigrationsAssembly(Sqlite.Assembly)
        );
    

    if (provider == Postgres.Name) 
        options.UseNpgsql(
            config.GetConnectionString(Postgres.Name)!,
            x => x.MigrationsAssembly(Postgres.Assembly)
        );
    
);

var app = builder.Build();

// initialize database
using (var scope = app.Services.CreateScope()) 
    var db = scope.ServiceProvider.GetRequiredService<VehiclesContext>();
    await VehiclesContext.InitializeAsync(db);


app.MapGet("/", (VehiclesContext db) => 
    db.Vehicles.OrderBy(x => x.Id).Take(10).ToList()
);

app.Run();

Jika Anda telah menggunakan kode inisialisasi saya, ketika Anda menjalankan aplikasi, Anda akan melihat daftar 10 kendaraan acak dari penyedia database pilihan Anda.

Hasil dari ASP.NET Core API yang didukung oleh beberapa penyedia basis data

Kesimpulan & Pikiran Akhir

Meskipun fungsional, pendekatan ini memiliki banyak ruang untuk perbaikan. Pertama, Anda dapat mengubah mekanisme switching untuk memenuhi kebutuhan Anda. Misalnya, saat pendekatan ini menetapkan penyedia saat start-up, Anda dapat mengganti penyedia per pengguna atau per permintaan. Saya tidak tahu mengapa Anda menginginkannya, tetapi jika Anda melakukannya, beri tahu saya di komentar. Untuk proyek lapangan hijau, Anda juga dapat menambahkan metode pembantu untuk menghasilkan skrip untuk semua penyedia secara bersamaan.

Sementara kami memulai solusi baru dengan beberapa penyedia basis data, Anda dapat dengan mudah mengonversi solusi yang ada untuk menggunakan pendekatan yang sama. Ingatlah bahwa setiap penyedia baru yang ditambahkan akan memiliki migrasi yang berbeda dari penyedia sebelumnya. Setiap penyedia baru yang ditambahkan akan menyertakan semua perubahan hingga saat itu dalam migrasi menyeluruh. Meskipun tidak selalu merupakan hasil yang “buruk”, melihat satu penyedia memiliki migrasi tambahan kecil mungkin tampak aneh sementara yang lain memiliki lebih sedikit migrasi besar.

Penting juga untuk dicatat bahwa menggunakan beberapa penyedia basis data berarti Anda harus menyusun strategi kapan dan di mana harus menggunakan fitur tertentu dari setiap penyedia. Misalnya, Anda dapat memilih hanya untuk menggunakan serangkaian fitur standar, memiliki logika percabangan dalam kode, atau menggunakan arahan praprosesor untuk memiliki beberapa pembuatan perangkat lunak. Strateginya benar-benar terserah Anda dan kemampuan Anda untuk memelihara perangkat lunak Anda.

Menggunakan beberapa penyedia basis data Entity Framework Core adalah pilihan yang sangat baik untuk orang-orang yang menjual perangkat lunak di tempat di mana pelanggan menuntut solusi yang sesuai dengan keahlian organisasi mereka. Anda dapat memperluas basis pelanggan potensial Anda sambil membatasi jumlah permutasi basis kode Anda. Menggunakan penyedia LINQ untuk menghasilkan kueri khusus basis data dan mengoptimalkan bila diperlukan dapat membantu mengurangi biaya operasional Anda dan memberikan solusi kepada pelanggan Anda lebih cepat.

Seperti biasa, terima kasih telah membaca. Jika Anda memiliki komentar atau pertanyaan, jangan ragu untuk meninggalkannya di bawah. Jika Anda menikmati artikel ini, saya akan sangat menghargai jika Anda membaginya dengan teman dan kolega Anda.

* kredit gambar unggulan untuk Alp Duran

Pemrograman