Migrasi Pertama Kode di Lingkungan Tim
Catatan
Artikel ini mengasumsikan Anda tahu cara menggunakan Migrasi Pertama Kode dalam skenario dasar. Jika tidak, Maka Anda harus membaca Migrasi Pertama Kode sebelum melanjutkan.
Ambil kopi, Anda perlu membaca seluruh artikel ini
Masalah di lingkungan tim sebagian besar sekeliling menggabungkan migrasi ketika dua pengembang telah menghasilkan migrasi di basis kode lokal mereka. Meskipun langkah-langkah untuk menyelesaikannya cukup sederhana, mereka mengharuskan Anda untuk memiliki pemahaman yang kuat tentang cara kerja migrasi. Jangan hanya melompat ke depan ke akhir - luangkan waktu untuk membaca seluruh artikel untuk memastikan Anda berhasil.
Beberapa pedoman umum
Sebelum kami menggali cara mengelola penggabungan migrasi yang dihasilkan oleh beberapa pengembang, berikut adalah beberapa panduan umum untuk menyiapkan Anda agar sukses.
Setiap anggota tim harus memiliki database pengembangan lokal
Migrasi menggunakan tabel __MigrationsHistory untuk menyimpan migrasi apa yang telah diterapkan ke database. Jika Anda memiliki beberapa pengembang yang menghasilkan migrasi yang berbeda saat mencoba menargetkan database yang sama (dan dengan demikian berbagi tabel __MigrationsHistory ) migrasi akan menjadi sangat bingung.
Tentu saja, jika Anda memiliki anggota tim yang tidak menghasilkan migrasi, tidak ada masalah meminta mereka berbagi database pengembangan pusat.
Hindari migrasi otomatis
Intinya adalah bahwa migrasi otomatis awalnya terlihat baik di lingkungan tim, tetapi pada kenyataannya mereka tidak berfungsi. Jika Anda ingin tahu alasannya, terus baca - jika tidak, maka Anda dapat melompat ke bagian berikutnya.
Migrasi otomatis memungkinkan Anda memperbarui skema database agar sesuai dengan model saat ini tanpa perlu membuat file kode (migrasi berbasis kode). Migrasi otomatis akan bekerja dengan sangat baik di lingkungan tim jika Anda hanya pernah menggunakannya dan tidak pernah menghasilkan migrasi berbasis kode apa pun. Masalahnya adalah bahwa migrasi otomatis terbatas dan tidak menangani sejumlah operasi – ganti nama properti/kolom, memindahkan data ke tabel lain, dll. Untuk menangani skenario ini, Anda akhirnya menghasilkan migrasi berbasis kode (dan mengedit kode perancah) yang dicampur di antara perubahan yang ditangani oleh migrasi otomatis. Ini membuatnya mendekati tidak mungkin untuk menggabungkan perubahan ketika dua pengembang memeriksa migrasi.
Memahami cara kerja migrasi
Kunci untuk berhasil menggunakan migrasi di lingkungan tim adalah pemahaman dasar tentang cara migrasi melacak dan menggunakan informasi tentang model untuk mendeteksi perubahan model.
Migrasi pertama
Saat menambahkan migrasi pertama ke proyek, Anda menjalankan sesuatu seperti Add-Migration First di Package Manager Console. Langkah-langkah tingkat tinggi yang dilakukan perintah ini digambakan di bawah ini.
Model saat ini dihitung dari kode Anda (1). Objek database yang diperlukan kemudian dihitung oleh model yang berbeda (2) - karena ini adalah migrasi pertama yang berbeda model hanya menggunakan model kosong untuk perbandingan. Perubahan yang diperlukan diteruskan ke generator kode untuk membangun kode migrasi yang diperlukan (3) yang kemudian ditambahkan ke solusi Visual Studio Anda (4).
Selain kode migrasi aktual yang disimpan dalam file kode utama, migrasi juga menghasilkan beberapa file kode-di belakang tambahan. File-file ini adalah metadata yang digunakan oleh migrasi dan bukan sesuatu yang harus Anda edit. Salah satu file ini adalah file sumber daya (.resx) yang berisi rekam jepret model pada saat migrasi dibuat. Anda akan melihat bagaimana ini digunakan pada langkah berikutnya.
Pada titik ini Anda mungkin akan menjalankan Update-Database untuk menerapkan perubahan Anda ke database, lalu mulai menerapkan area lain dari aplikasi Anda.
Migrasi berikutnya
Kemudian Anda kembali dan membuat beberapa perubahan pada model Anda - dalam contoh kami kami akan menambahkan properti Url ke Blog. Anda kemudian akan mengeluarkan perintah seperti Add-Migration AddUrl untuk membuat perancah migrasi untuk menerapkan perubahan database yang sesuai. Langkah-langkah tingkat tinggi yang dilakukan perintah ini digambakan di bawah ini.
Sama seperti terakhir kali, model saat ini dihitung dari kode (1). Namun, kali ini ada migrasi yang ada sehingga model sebelumnya diambil dari migrasi terbaru (2). Kedua model ini berbeda untuk menemukan perubahan database yang diperlukan (3) dan kemudian proses selesai seperti sebelumnya.
Proses yang sama ini digunakan untuk migrasi lebih lanjut yang Anda tambahkan ke proyek.
Mengapa repot-repot dengan rekam jepret model?
Anda mungkin bertanya-tanya mengapa EF mengganggu rekam jepret model - mengapa tidak hanya melihat database. Jika demikian, bacalah. Jika Anda tidak tertarik maka Anda dapat melewati bagian ini.
Ada sejumlah alasan EF menyimpan rekam jepret model:
- Ini memungkinkan database Anda untuk menyimpang dari model EF. Perubahan ini dapat dilakukan langsung dalam database, atau Anda dapat mengubah kode perancah dalam migrasi Anda untuk membuat perubahan. Berikut adalah beberapa contoh ini dalam praktiknya:
- Anda ingin menambahkan kolom Disisipkan dan Diperbarui ke ke satu atau beberapa tabel Tetapi Anda tidak ingin menyertakan kolom ini dalam model EF. Jika migrasi melihat database, migrasi akan terus mencoba menghilangkan kolom ini setiap kali Anda membuat perancah migrasi. Dengan menggunakan rekam jepret model, EF hanya akan mendeteksi perubahan yang sah pada model.
- Anda ingin mengubah isi prosedur tersimpan yang digunakan untuk pembaruan untuk menyertakan beberapa pengelogan. Jika migrasi melihat prosedur tersimpan ini dari database, migrasi akan terus mencoba dan mengatur ulang kembali ke definisi yang diharapkan EF. Dengan menggunakan rekam jepret model, EF hanya akan pernah membuat perancah kode untuk mengubah prosedur tersimpan saat Anda mengubah bentuk prosedur dalam model EF.
- Prinsip yang sama ini berlaku untuk menambahkan indeks tambahan, termasuk tabel tambahan dalam database Anda, memetakan EF ke tampilan database yang berada di atas tabel, dll.
- Model EF berisi lebih dari sekadar bentuk database. Memiliki seluruh model memungkinkan migrasi untuk melihat informasi tentang properti dan kelas dalam model Anda dan bagaimana mereka memetakan ke kolom dan tabel. Informasi ini memungkinkan migrasi menjadi lebih cerdas dalam kode yang perancahnya. Misalnya, jika Anda mengubah nama kolom yang dipetakan properti ke migrasi dapat mendeteksi penggantian nama dengan melihat bahwa itu adalah properti yang sama - sesuatu yang tidak dapat dilakukan jika Anda hanya memiliki skema database.
Apa yang menyebabkan masalah di lingkungan tim
Alur kerja yang tercakup di bagian sebelumnya berfungsi dengan baik ketika Anda adalah pengembang tunggal yang mengerjakan aplikasi. Ini juga berfungsi dengan baik di lingkungan tim jika Anda adalah satu-satunya orang yang membuat perubahan pada model. Dalam skenario ini Anda dapat membuat perubahan model, menghasilkan migrasi, dan mengirimkannya ke kontrol sumber Anda. Pengembang lain dapat menyinkronkan perubahan Anda dan menjalankan Update-Database agar perubahan skema diterapkan.
Masalah mulai muncul ketika Anda memiliki beberapa pengembang yang membuat perubahan pada model EF dan mengirimkan ke kontrol sumber secara bersamaan. Apa yang tidak memiliki EF adalah cara kelas pertama untuk menggabungkan migrasi lokal Anda dengan migrasi yang telah dikirimkan pengembang lain ke kontrol sumber sejak terakhir kali Anda sinkronkan.
Contoh konflik penggabungan
Pertama-tama mari kita lihat contoh konkret dari konflik penggabungan tersebut. Kita akan melanjutkan dengan contoh yang kita lihat sebelumnya. Sebagai titik awal mari kita asumsikan perubahan dari bagian sebelumnya diperiksa oleh pengembang asli. Kami akan melacak dua pengembang saat mereka membuat perubahan pada basis kode.
Kami akan melacak model EF dan migrasi melalui sejumlah perubahan. Untuk titik awal, kedua pengembang telah disinkronkan ke repositori kontrol sumber, seperti yang digambarkan dalam grafik berikut.
Pengembang #1 dan pengembang #2 sekarang membuat beberapa perubahan pada model EF di basis kode lokal mereka. Pengembang #1 menambahkan properti Peringkat ke Blog - dan menghasilkan migrasi AddRating untuk menerapkan perubahan pada database. Pengembang #2 menambahkan properti Pembaca ke Blog - dan menghasilkan migrasi AddReaders yang sesuai. Kedua pengembang menjalankan Update-Database, untuk menerapkan perubahan pada database lokal mereka, lalu terus mengembangkan aplikasi.
Catatan
Migrasi diawali dengan tanda waktu, sehingga grafik kami mewakili bahwa migrasi AddReaders dari Pengembang #2 datang setelah migrasi AddRating dari Pengembang #1. Apakah pengembang #1 atau #2 menghasilkan migrasi terlebih dahulu tidak membuat perbedaan dengan masalah bekerja dalam tim, atau proses untuk menggabungkannya yang akan kita lihat di bagian berikutnya.
Ini adalah hari keberuntungan bagi Pengembang #1 karena mereka kebetulan mengirimkan perubahan mereka terlebih dahulu. Karena tidak ada orang lain yang check-in sejak menyinkronkan repositori mereka, mereka hanya dapat mengirimkan perubahan mereka tanpa melakukan penggabungan apa pun.
Sekarang saatnya Pengembang #2 untuk mengirimkan. Mereka tidak begitu beruntung. Karena orang lain telah mengirimkan perubahan sejak disinkronkan, mereka harus menurunkan perubahan dan penggabungan. Sistem kontrol sumber kemungkinan akan dapat secara otomatis menggabungkan perubahan di tingkat kode karena sangat sederhana. Status repositori lokal Pengembang #2 setelah sinkronisasi digambarkan dalam grafik berikut.
Pada tahap ini Pengembang #2 dapat menjalankan Update-Database yang akan mendeteksi migrasi AddRating baru (yang belum diterapkan ke database Pengembang #2) dan menerapkannya. Sekarang kolom Peringkat ditambahkan ke tabel Blog dan database sinkron dengan model.
Namun, ada beberapa masalah:
- Meskipun Update-Database akan menerapkan migrasi AddRating , itu juga akan memunculkan peringatan: Tidak dapat memperbarui database agar sesuai dengan model saat ini karena ada perubahan yang tertunda dan migrasi otomatis dinonaktifkan... Masalahnya adalah bahwa rekam jepret model yang disimpan dalam migrasi terakhir (AddReader) kehilangan properti Peringkat di Blog (karena bukan bagian dari model saat migrasi dibuat). Kode Pertama mendeteksi bahwa model dalam migrasi terakhir tidak cocok dengan model saat ini dan menaikkan peringatan.
- Menjalankan aplikasi akan menghasilkan InvalidOperationException yang menyatakan bahwa "Model yang mendukung konteks 'BloggingContext' telah berubah sejak database dibuat. Pertimbangkan untuk menggunakan Migrasi Pertama Kode untuk memperbarui database..." Sekali lagi, masalahnya adalah rekam jepret model yang disimpan dalam migrasi terakhir tidak cocok dengan model saat ini.
- Terakhir, kami berharap menjalankan Add-Migration sekarang akan menghasilkan migrasi kosong (karena tidak ada perubahan yang diterapkan pada database). Tetapi karena migrasi membandingkan model saat ini dengan model dari migrasi terakhir (yang kehilangan properti Peringkat) itu sebenarnya akan membuat perancah panggilan AddColumn lain untuk ditambahkan di kolom Peringkat. Tentu saja, migrasi ini akan gagal selama Update-Database karena kolom Peringkat sudah ada.
Mengatasi konflik penggabungan
Kabar baiknya adalah bahwa tidak terlalu sulit untuk menangani penggabungan secara manual - asalkan Anda memiliki pemahaman tentang cara kerja migrasi. Jadi jika Anda telah melompat ke depan ke bagian ini... maaf, Anda perlu kembali dan membaca sisa artikel terlebih dahulu!
Ada dua opsi, yang paling mudah adalah menghasilkan migrasi kosong yang memiliki model saat ini yang benar sebagai rekam jepret. Opsi kedua adalah memperbarui rekam jepret dalam migrasi terakhir untuk memiliki rekam jepret model yang benar. Opsi kedua sedikit lebih sulit dan tidak dapat digunakan dalam setiap skenario, tetapi juga lebih bersih karena tidak melibatkan penambahan migrasi tambahan.
Opsi 1: Tambahkan migrasi 'gabungkan' kosong
Dalam opsi ini, kami menghasilkan migrasi kosong semata-mata untuk tujuan memastikan migrasi terbaru memiliki rekam jepret model yang benar yang disimpan di dalamnya.
Opsi ini dapat digunakan terlepas dari siapa yang menghasilkan migrasi terakhir. Dalam contoh kami telah mengikuti Pengembang #2 sedang mengurus penggabungan dan mereka kebetulan menghasilkan migrasi terakhir. Tetapi langkah-langkah yang sama ini dapat digunakan jika Pengembang #1 menghasilkan migrasi terakhir. Langkah-langkah ini juga berlaku jika ada beberapa migrasi yang terlibat - kami baru saja melihat dua untuk membuatnya sederhana.
Proses berikut dapat digunakan untuk pendekatan ini, mulai dari saat Anda menyadari bahwa Anda memiliki perubahan yang perlu disinkronkan dari kontrol sumber.
- Pastikan setiap perubahan model yang tertunda di basis kode lokal Anda telah ditulis ke migrasi. Langkah ini memastikan Anda tidak melewatkan perubahan yang sah ketika tiba saatnya untuk menghasilkan migrasi kosong.
- Sinkronkan dengan kontrol sumber.
- Jalankan Update-Database untuk menerapkan migrasi baru yang telah diperiksa oleh pengembang lain. Catatan: jika Anda tidak mendapatkan peringatan apa pun dari perintah Update-Database, maka tidak ada migrasi baru dari pengembang lain dan tidak perlu melakukan penggabungan lebih lanjut.
- Jalankan Add-Migration <pick_a_name> –IgnoreChanges (misalnya, Add-Migration Merge –IgnoreChanges). Ini menghasilkan migrasi dengan semua metadata (termasuk rekam jepret model saat ini) tetapi akan mengabaikan perubahan apa pun yang dideteksinya saat membandingkan model saat ini dengan rekam jepret dalam migrasi terakhir (artinya Anda mendapatkan metode Atas dan Bawah kosong).
- Jalankan Update-Database untuk menerapkan kembali migrasi terbaru dengan metadata yang diperbarui.
- Lanjutkan pengembangan, atau kirim ke kontrol sumber (setelah menjalankan pengujian unit Anda tentu saja).
Berikut adalah status basis kode lokal Pengembang #2 setelah menggunakan pendekatan ini.
Opsi 2: Memperbarui rekam jepret model di migrasi terakhir
Opsi ini sangat mirip dengan opsi 1 tetapi menghapus migrasi kosong ekstra - karena mari kita hadapi, yang menginginkan file kode tambahan dalam solusinya.
Pendekatan ini hanya layak jika migrasi terbaru hanya ada di basis kode lokal Anda dan belum dikirimkan ke kontrol sumber (misalnya, jika migrasi terakhir dihasilkan oleh pengguna yang melakukan penggabungan). Mengedit metadata migrasi yang mungkin telah diterapkan pengembang lain ke database pengembangan mereka - atau bahkan lebih buruk diterapkan ke database produksi - dapat mengakibatkan efek samping yang tidak terduga. Selama proses, kita akan mengembalikan migrasi terakhir di database lokal kita dan menerapkannya kembali dengan metadata yang diperbarui.
Meskipun migrasi terakhir hanya perlu berada di basis kode lokal tidak ada batasan untuk jumlah atau urutan migrasi yang melanjutkannya. Mungkin ada beberapa migrasi dari beberapa pengembang yang berbeda dan langkah-langkah yang sama berlaku - kami baru saja melihat dua untuk membuatnya sederhana.
Proses berikut dapat digunakan untuk pendekatan ini, mulai dari saat Anda menyadari bahwa Anda memiliki perubahan yang perlu disinkronkan dari kontrol sumber.
- Pastikan setiap perubahan model yang tertunda di basis kode lokal Anda telah ditulis ke migrasi. Langkah ini memastikan Anda tidak melewatkan perubahan yang sah ketika tiba saatnya untuk menghasilkan migrasi kosong.
- Sinkronkan dengan kontrol sumber.
- Jalankan Update-Database untuk menerapkan migrasi baru yang telah diperiksa oleh pengembang lain. Catatan: jika Anda tidak mendapatkan peringatan apa pun dari perintah Update-Database, maka tidak ada migrasi baru dari pengembang lain dan tidak perlu melakukan penggabungan lebih lanjut.
- Jalankan Update-Database –TargetMigration <second_last_migration> (dalam contoh yang telah kita ikuti ini adalah Update-Database –TargetMigration AddRating). Ini mengembalikan database ke status migrasi terakhir kedua – secara efektif 'tidak menerapkan' migrasi terakhir dari database. Catatan: Langkah ini diperlukan untuk membuatnya aman untuk mengedit metadata migrasi karena metadata juga disimpan di __MigrationsHistoryTable database. Inilah sebabnya mengapa Anda hanya boleh menggunakan opsi ini jika migrasi terakhir hanya ada di basis kode lokal Anda. Jika database lain memiliki migrasi terakhir yang diterapkan, Anda juga harus mengembalikannya dan menerapkan kembali migrasi terakhir untuk memperbarui metadata.
- Jalankan full_name_including_timestamp_of_last_migration> Add-Migration <(dalam contoh yang telah kami ikuti, ini akan menjadi sesuatu seperti Add-Migration 201311062215252_AddReaders). Catatan: Anda perlu menyertakan tanda waktu sehingga migrasi tahu Bahwa Anda ingin mengedit migrasi yang ada daripada perancah yang baru. Ini akan memperbarui metadata untuk migrasi terakhir agar sesuai dengan model saat ini. Anda akan mendapatkan peringatan berikut saat perintah selesai, tetapi itulah yang Anda inginkan. "Hanya Kode Perancang untuk migrasi '201311062215252_AddReaders' yang dibuat ulang. Untuk membuat perancah ulang seluruh migrasi, gunakan parameter -Force."
- Jalankan Update-Database untuk menerapkan kembali migrasi terbaru dengan metadata yang diperbarui.
- Lanjutkan pengembangan, atau kirim ke kontrol sumber (setelah menjalankan pengujian unit Anda tentu saja).
Berikut adalah status basis kode lokal Pengembang #2 setelah menggunakan pendekatan ini.
Ringkasan
Ada beberapa tantangan saat menggunakan Migrasi Pertama Kode di lingkungan tim. Namun, pemahaman dasar tentang cara kerja migrasi dan beberapa pendekatan sederhana untuk menyelesaikan konflik penggabungan memudahkan untuk mengatasi tantangan ini.
Masalah mendasar adalah metadata yang salah yang disimpan dalam migrasi terbaru. Ini menyebabkan Kode Pertama salah mendeteksi bahwa model saat ini dan skema database tidak cocok dan salah perancah kode dalam migrasi berikutnya. Situasi ini dapat diatasi dengan menghasilkan migrasi kosong dengan model yang benar, atau memperbarui metadata dalam migrasi terbaru.