Kiat untuk bekerja dengan EntityFramework Core

Halo.



Baru-baru ini saya menemukan bahwa tidak semua orang yang bekerja dengan EF tahu cara memasaknya. Apalagi mereka tidak ingin mengerti. Menghadapi masalah pada tahap paling awal - pengaturan.

Bahkan setelah konfigurasi berhasil, masih ada masalah dengan permintaan data. Bukan karena orang tidak tahu LINQ, tapi karena tidak semuanya bisa dipetakan dari objek ke model relasional. Karena saat bekerja dengan tautan, orang berpikir dalam tabel. Gambar kueri SQL dan coba terjemahkan ke dalam LINQ.



Ini dan, mungkin, hal lain yang ingin saya bicarakan di artikel.



Pengaturan



Seseorang datang ke proyek, melihat EF di sana untuk pertama kalinya, kagum pada keajaiban seperti itu, belajar menggunakannya, memutuskan untuk menggunakannya untuk tujuan pribadi. Membuat proyek baru, ... Lalu apa yang harus dilakukan?



Anda mulai googling, trial, error. Untuk menghubungkan EF ke proyek, pengembang dihadapkan pada jenis masalah yang tidak bisa dipahami.



1. Dan bagaimana mengkonfigurasinya untuk bekerja dengan DBMS yang dibutuhkan?

2. Dan bagaimana mengkonfigurasi pekerjaan migrasi?



Saya yakin ada lebih banyak masalah, tetapi ini mungkin yang paling sering terjadi. Mari kita bicarakan semuanya secara berurutan.



1. Nah ini dia untuk google. :) Tugas Anda adalah menemukan penyedia Inti EntityFramework untuk DBMS Anda.



Juga dalam deskripsi penyedia Anda akan menemukan instruksi untuk mengatur.



Untuk PostgreSQL, misalnya, Anda perlu menginstal paket Nuget Npgsql.EntityFrameworkCore.PostgreSQL

Buat konteks Anda sendiri dengan menerima DbContextOptions dan buat semuanya seperti ini



var opts = new DbContextOptionsBuilder<MyDbContext>()
        .UseNpgsql(constring);

var ctx = new MyDbContext(opts.Options);




Jika Anda memiliki aplikasi ASP .NET Core, konteksnya terdaftar di wadah secara berbeda. Tapi Anda sudah bisa melihat ini di proyek kerja Anda. Atau google.



2. Jika tidak ada pengasuh di perusahaan Anda, kemungkinan besar Anda tahu cara memasang alat yang diperlukan untuk bekerja dengan migrasi. Baik untuk manajer paket atau NET Core CLI.



Tetapi yang mungkin tidak Anda sadari adalah bahwa proyek startup yang dipilih (--startup-project) dimulai saat bekerja dengan migrasi. Ini berarti bahwa jika proyek awal untuk migrasi adalah proyek yang sama yang meluncurkan aplikasi Anda dan ketika Anda memulai karena suatu alasan Anda menggulung migrasi melalui ctx.Database.Migrate (), maka ketika Anda mencoba membuat hapus migrasi yang dibuat sebelumnya atau buat yang lain, maka migrasi yang terakhir dibuat akan bergulir ke pangkalan. MENGHERANKAN!



Tapi, mungkin, saat mencoba membuat migrasi pertama, Anda akan menangkap sesuatu seperti ini



Tidak ada penyedia database yang telah dikonfigurasi untuk DbContext ini. Penyedia dapat dikonfigurasi dengan mengganti metode DbContext.OnConfiguring atau dengan menggunakan AddDbContext pada penyedia layanan aplikasi. Jika AddDbContext digunakan, pastikan juga bahwa tipe DbContext Anda menerima objek DbContextOptions <TContext> dalam konstruktornya dan meneruskannya ke konstruktor dasar untuk DbContext.


Ini karena alat yang bekerja dengan migrasi membuatnya berdasarkan konteks Anda, tetapi untuk ini diperlukan sebuah contoh. Dan untuk menyediakannya, Anda perlu mengimplementasikan antarmuka IDesignTimeDbContextFactory di project starter. Ini tidak sulit, hanya ada satu metode yang harus mengembalikan instance konteks Anda.



Menyiapkan model



Anda telah membuat migrasi pertama Anda, tetapi karena alasan tertentu migrasi itu kosong. Meskipun Anda memiliki model.



Intinya adalah Anda belum mengajarkan konteks Anda untuk menerjemahkan model Anda ke dalam database Anda.



Agar EF membuat migrasi untuk membuat tabel untuk model Anda dalam database, Anda setidaknya harus membuat properti tipe DbSet <MyEntity> dalam konteks Anda.



misalnya, dengan properti ini

publik DbSet <MyEntity> MyEntities {get; set; }



Tabel MyEntity akan dibuat dengan bidang yang sesuai dengan properti dari entitas MyEntity.



Jika Anda tidak ingin memiliki DbSet, atau jika Anda ingin mempengaruhi aturan pembuatan tabel default untuk entitas, Anda perlu mengganti metode

protected override void OnModelCreating(ModelBuilder modelBuilder)

konteks Anda.



Bagaimana melakukan ini, kemungkinan besar Anda tahu. Selain itu, Anda mungkin sudah familiar dengan atribut yang memungkinkan Anda mengontrol aturan pemetaan. Tapi inilah masalahnya. Atribut tidak memberikan kontrol penuh atas pemetaan, yang berarti Anda akan mendapatkan perbaikan di OnModelCreating. Artinya, Anda akan memiliki aturan untuk memetakan entitas dalam bentuk anotasi dan dalam bentuk fluent api. Pergi kemudian cari mengapa bidang memiliki nama yang salah, yang Anda harapkan, atau batasan yang salah.



- Baiklah, semuanya sederhana, - Anda katakan - Saya akan mengkonfigurasi semuanya melalui OnModelCreating



Dan kemudian, setelah menyiapkan entitas ke-20 dari 10 bidang, mata Anda mulai bergetar. Anda mencoba menemukan pengaturan bidang untuk beberapa entitas, tetapi semuanya mengambang di depan mata Anda dari blok seragam sepanjang 200-500 baris.



Ya, Anda dapat memecah blok ini menjadi 20 metode dan itu akan menjadi sedikit lebih mudah. Tetapi penting untuk mengetahui bahwa ada antarmuka <TEntity> IEntityTypeConfiguration, dengan mengimplementasikan yang mana untuk entitas tertentu, Anda dapat menjelaskan dalam implementasi ini aturan pemetaan untuk entitas tertentu. Nah, agar konteksnya dapat diambil, di OnModelCreating Anda perlu menulis

modelBuilder.ApplyConfigurationsFromAssembly (assemblyWithConfigurations);



Dan, tentu saja, dimungkinkan untuk mentransfer perilaku umum ke OnModelCreating yang sama. Misalnya, jika Anda memiliki aturan umum untuk pengenal dari semua atau banyak entitas, Anda dapat mengonfigurasinya seperti ini



foreach (var idEntity in modelBuilder.Model.GetEntityTypes()
    .Where(x => typeof(BaseIdEntity).IsAssignableFrom(x.ClrType))
    .Select(x => modelBuilder.Entity(x.ClrType)))
{
    idEntity.HasKey(nameof(BaseIdEntity.Id));
}


Membangun kueri



Nah, kami menertibkan, ini menjadi sedikit lebih menyenangkan.



Sekarang tinggal mengulang query dari proyek rumah kita dari SQL ke Linq. Saya sudah menulis permintaan di tempat kerja, semudah

mengupas pir ctx.MyEntities.Where (kondisi). Pilih (peta) .GroupBy (ekspresi) .OrderBy (ekspresi)… kemudahan.



Jadi, apa permintaan kita?



SELECT bla bla bla FROM table
RIGHT JOIN....... 


ya



ctx.LeftEntities.RightJoin(.... f@#$


Saya telah menggunakan Linq dan metode ekstensi jauh sebelum saya mengenal EF. Dan saya tidak pernah punya pertanyaan, tapi di mana RIGHT JOIN, LEFT JOIN di dalamnya ...



Apa LEFT JOIN dari sudut pandang objek?



Itu




class LeftEntity 
{
    public List<RightEntity> RightEntities { get; set; }
}


Ini bahkan bukan sihir. Kami hanya memiliki entitas tertentu yang merujuk ke daftar entitas lain, tetapi mungkin tidak memilikinya.



Artinya, ini



ctx.LeftEntities.Include(x => x.RightEntities)


Apa itu RIGHT JOIN? Ini adalah LEFT JOIN terbalik, artinya kita baru saja mulai dengan entitas yang berbeda.



Tetapi apa pun yang terjadi, terkadang Anda memerlukan kontrol atas setiap bundel sebagai entitas terpisah, bahkan jika entitas kiri tidak terkait dengan yang benar (yang kanan adalah NULL). Karenanya, secara eksplisit LEFT JOIN bisa dilakukan seperti ini



ctx.LeftEntities.SelectMany(x => x.RightEntities.DefaultIfEmpty(), (l, r) => new { Left = l, Right = r })


Ini memberi kita kendali atas pengikatan, seperti dalam model relasional. Mengapa Anda membutuhkannya? Katakanlah pelanggan perlu menampilkan data dalam bentuk tabel, dan pengurutan dan / atau penomoran halaman diperlukan.



Saya juga tidak suka ide ini, tetapi setiap orang memiliki kebiasaan mereka sendiri dan cinta tanpa akhir di Excel. Jadi kami akan menutup matras dengan batuk, mengumpat, dan terus bekerja. Apa selanjutnya untuk kita?



FULL JOIN


Jadi, saya sudah mengerti, kami tidak berpikir dengan SQL, kami berpikir dengan objek, seperti ini, dan sekarang seperti ini ... sialan. Bagaimana menggambarkan ini?



Tanpa tupel, tidak ada tempat.



Secara umum, ketika kita harus bekerja dengan tupel, kita kehilangan pemahaman tentang jenis koneksi (1-1, 1-n, nn) ​​dan secara umum, secara teoritis, kita dapat menyilangkan lalat dan gajah. Ini sihir hitam.

Ayo lakukan!



Mari kita ambil banyak-ke-banyak sebagai dasar.



Jadi, kami memiliki 3 jenis entitas



LeftEntity

RightEntity

LeftRight (untuk mengatur koneksi)



LeftRight yang akan saya buat dengan kunci komposit. Saya tidak memerlukan akses langsung ke entitas ini, referensi eksternal tidak diperlukan, jadi saya tidak akan membuat bidang dan indeks yang tidak perlu.



Sebagai hasil dari kueri, saya ingin mendapatkan satu set tupel, yang akan berisi entitas kiri dan entitas kanan. Selain itu, jika entitas perawan tidak ada hubungannya dengan entitas yang benar, maka objek yang tepat akan menjadi nol. Hal yang sama berlaku untuk entitas kanan.



Ternyata LeftRight tidak cocok untuk kita sebagai keluaran, keterbatasannya tidak memungkinkan kita untuk membuat bundel tanpa salah satu entitas. Jika Anda berlatih DDD, Anda akan menemukan ketidakkonsistenan.

Bahkan jika Anda tidak berlatih, Anda seharusnya tidak melakukannya. Mari buat tipe baru untuk keluaran

LeftRightFull, yang masih berisi referensi ke entitas kiri dan kanan.



Jadi, kita harus



Kiri

L1

L2



Kanan

R1

R2



LeftRight

L2 R2



Kami ingin pada output

L1 n

L2 R2

n R1



Mari kita mulai dengan koneksi kiri



var query = ctx.LeftEntities
    .SelectMany(x => x.RightLinks.DefaultIfEmpty(), (l, lr) => new LeftRightFull
    {
        LeftEntity = l,
        RightEntity = lr.RightEntity
    })




Sekarang, kita sudah memiliki

L1 n

L2 R2



Apa selanjutnya? Stick RightEntity melalui SelectMany? Jadi kita dapatkan

L1 n R1 n

the L1 n R2 the L2

the L2 R2 R1 n

the L2 R2 R2 the L2



weed out the superfluous (L2 R2 R1 n and L1 n R2 L2) on the condition we will be can, bagaimanapun, masih belum jelas bagaimana L1 n R1 n turn di

L1 n

n R1



Mungkin penggalian membawa kami ke stepa yang salah. Mari kita hubungkan kueri serupa ini dengan gabungan kiri untuk entitas kiri dan kanan melalui Union.

L1 n

L2 R2

UNION

L2 R2

n R1



Di sini Anda mungkin memiliki pertanyaan tentang kinerja. Saya tidak akan menjawabnya, karena semuanya akan tergantung pada DBMS spesifik dan ketelitian pengoptimalnya.



Alternatifnya, Anda dapat membuat Tampilan dengan kueri yang Anda inginkan. Union tidak berfungsi di EF Core 2, jadi saya harus membuat View. Namun, saya sarankan untuk tidak melupakannya. Tiba-tiba, Anda memutuskan untuk menerapkan penghapusan lunak entitas melalui bendera IsDeleted. Dalam pandangannya, Anda perlu mendukung ini. Jika Anda lupa, tidak diketahui kapan Anda akan menerima laporan bug.



Ngomong-ngomong, bagaimana cara menerapkan penghapusan lunak tanpa menulis ulang seluruh kode? Saya akan membicarakannya lain kali. Mungkin sesuatu yang lain.



Selamat tinggal semuanya.



All Articles