Rumah >pembangunan bahagian belakang >C++ >Akses data dalam kod, menggunakan repositori, walaupun dengan ORM
Dalam dunia .NET, salah satu kaedah yang paling banyak digunakan untuk mengakses pangkalan data ialah dengan Rangka Kerja Entiti (EF), Object Relational Mapper (ORM) yang disepadukan rapat dengan sintaks bahasa. Menggunakan Pertanyaan Bersepadu Bahasa (LINQ) asli kepada bahasa .NET, ia menjadikan akses data terasa seperti bekerja dengan koleksi .NET biasa, tanpa banyak pengetahuan tentang SQL. Ini mempunyai kebaikan dan keburukan yang saya akan cuba untuk tidak bercakap tentang di sini. Tetapi salah satu isu yang ia wujudkan secara konsisten ialah kekeliruan mengenai struktur projek perisian, tahap abstraksi dan akhirnya ujian unit.
Siaran ini akan cuba menerangkan sebab abstraksi repositori SENTIASA berguna. Ambil perhatian bahawa ramai orang menggunakan repositori sebagai istilah untuk capaian data abstrak, sementara terdapat juga corak perisian repositori yang berkaitan dengan perkara yang serupa, tetapi ia bukan perkara yang sama. Di sini, saya akan memanggil repositori satu siri antara muka yang mengabstrakkan butiran pelaksanaan akses data dan mengabaikan corak reka bentuk sepenuhnya.
Jangan teragak-agak untuk melangkau ini jika anda menyedarinya, tetapi saya perlu terlebih dahulu menangani bagaimana kita mendapat idea repositori untuk bermula.
Dalam prasejarah, kod hanya ditulis seperti sedia ada, tanpa struktur, segala-galanya, melakukan apa yang anda mahu ia lakukan atau sekurang-kurangnya berharap. Tiada ujian automatik, hanya penggodaman dan ujian manual sehingga ia berjaya. Setiap aplikasi ditulis dalam apa sahaja yang ada, dengan kebimbangan tentang keperluan perkakasan yang lebih penting daripada struktur kod, penggunaan semula atau kebolehbacaan. Itulah yang membunuh dinosaur! Fakta yang benar.
Perlahan-lahan, corak mula muncul. Untuk aplikasi perniagaan khususnya, terdapat pemisahan yang jelas bagi kod perniagaan, ketekunan data dan antara muka pengguna. Ini dipanggil lapisan dan tidak lama lagi dipisahkan kepada projek yang berbeza, bukan sahaja kerana ia merangkumi kebimbangan yang berbeza, tetapi juga kerana kemahiran yang diperlukan untuk membinanya adalah berbeza. Reka bentuk UI sangat berbeza daripada kerja logik kod dan sangat berbeza daripada SQL atau apa sahaja bahasa atau sistem yang digunakan untuk mengekalkan data.
Oleh itu, interaksi antara perniagaan dan lapisan data dilakukan dengan mengabstrakkannya ke dalam antara muka dan model. Sebagai kelas perniagaan, anda tidak akan meminta senarai entri dalam jadual, anda memerlukan senarai objek kompleks yang ditapis. Adalah menjadi tanggungjawab lapisan data untuk mengakses apa sahaja yang berterusan dan memetakannya kepada sesuatu yang boleh difahami oleh perniagaan. Abstraksi ini mula dipanggil repositori.
Pada lapisan bawah akses data, corak seperti CRUD cepat mengambil alih: anda menentukan bekas kegigihan berstruktur seperti jadual dan anda akan membuat, membaca, mengemas kini atau memadam rekod. Dalam kod, logik jenis ini akan diabstraksikan kepada koleksi, seperti Senarai, Kamus atau Tatasusunan. Oleh itu, terdapat juga pendapat semasa bahawa repositori harus berkelakuan seperti koleksi, malah mungkin cukup generik untuk tidak mempunyai kaedah lain selain daripada mencipta, membaca, mengemas kini dan memadam.
Namun, saya sangat tidak bersetuju. Sebagai abstraksi capaian data daripada perniagaan, ia harus dijauhkan sejauh mungkin daripada corak untuk akses data, sebaliknya dimodelkan berdasarkan keperluan perniagaan. Di sinilah pemikiran Rangka Kerja Entiti khususnya, tetapi banyak ORM lain, mula bercanggah dengan idea asal repositori, yang memuncak dengan panggilan untuk tidak menggunakan repositori dengan EF, memanggilnya sebagai antipattern.
Banyak kekeliruan dijana oleh hubungan ibu bapa-anak antara model. Seperti entiti Jabatan dengan Orang di dalamnya. Sekiranya repositori jabatan mengembalikan model yang mengandungi orang? Mungkin tidak. Jadi bagaimana pula dengan kita memisahkan repositori kepada jabatan (tanpa orang) dan orang, kemudian mempunyai abstraksi berasingan untuk dipetakan kemudian kepada model perniagaan?
Kekeliruan sebenarnya bertambah apabila kita mengambil lapisan perniagaan dan memisahkannya kepada sublapisan. Sebagai contoh, apa yang kebanyakan orang panggil perkhidmatan perniagaan ialah abstraksi terhadap penggunaan logik perniagaan tertentu hanya pada jenis model perniagaan tertentu. Katakan apl anda berfungsi dengan orang, jadi anda mempunyai model yang dipanggil Person. Kelas untuk mengendalikan orang ialah PeopleService, yang akan mendapatkan model perniagaan daripada lapisan kegigihan melalui PeopleRepository, tetapi juga melakukan perkara lain, termasuk pemetaan antara model data dan model perniagaan atau kerja khusus yang hanya berkaitan dengan orang, seperti mengira mereka gaji. Walau bagaimanapun, kebanyakan logik perniagaan menggunakan berbilang jenis model, jadi perkhidmatan akhirnya menjadi pembalut pemetaan ke atas repositori, dengan sedikit tanggungjawab tambahan.
Sekarang bayangkan anda menggunakan EF untuk mengakses data. Anda sudah perlu mengisytiharkan kelas DbContext yang mengandungi koleksi entiti yang anda petakan ke jadual SQL. Anda mempunyai LINQ untuk mengulang, menapis dan memetakannya, yang ditukar dengan cekap kepada arahan SQL di latar belakang dan memberikan anda apa yang anda perlukan, lengkap dengan struktur induk-anak berhierarki. Penukaran itu juga menjaga pemetaan jenis data perniagaan dalaman, seperti enum tertentu atau struktur data pelik. Jadi mengapa anda memerlukan repositori, mungkin juga perkhidmatan?
Saya percaya bahawa walaupun lebih banyak lapisan abstrak mungkin kelihatan seperti overhead yang sia-sia, ia meningkatkan pemahaman manusia tentang projek dan meningkatkan kelajuan dan kualiti perubahan. Terdapat keseimbangan, jelas sekali, saya telah melihat sistem yang direka bentuk dengan keperluan yang jelas bahawa semua corak reka bentuk perisian digunakan di mana-mana. Abstraksi hanya berguna jika ia meningkatkan kebolehbacaan kod dan pengasingan kebimbangan.
Salah satu konteks di mana EF menjadi menyusahkan ialah ujian unit. DbContext ialah sistem yang rumit, dengan banyak kebergantungan yang seseorang itu perlu mengejek secara manual dengan usaha yang gigih. Oleh itu Microsoft datang dengan idea: dalam pembekal pangkalan data memori. Jadi untuk menguji apa-apa, anda hanya menggunakan pangkalan data dalam memori dan selesai dengannya.
Perhatikan bahawa pada halaman Microsoft kaedah ujian ini kini ditandai dengan "tidak disyorkan". Juga ambil perhatian bahawa walaupun dalam contoh tersebut, EF diabstraksikan oleh repositori.
Semasa dalam ujian pangkalan data memori berfungsi, mereka menambah beberapa isu yang tidak mudah untuk ditangani:
Oleh itu, apa yang akhirnya berlaku ialah orang menyediakan segala-galanya dalam pangkalan data dalam kaedah "pembantu", kemudian mencipta ujian yang bermula dengan kaedah yang sukar difahami dan kompleks ini untuk menguji kefungsian yang paling kecil sekalipun. Mana-mana kod yang mengandungi kod EF tidak boleh diuji tanpa persediaan ini.
Jadi satu sebab untuk menggunakan repositori adalah untuk mengalihkan abstraksi ujian di atas DbContext. Kini anda tidak memerlukan pangkalan data sama sekali, hanya olok-olok repositori. Kemudian uji repo anda sendiri dalam ujian integrasi menggunakan pangkalan data sebenar. Pangkalan data dalam ingatan sangat hampir dengan pangkalan data sebenar, tetapi ia juga sedikit berbeza.
Sebab lain, yang saya akui jarang saya lihat mempunyai nilai sebenar dalam kehidupan sebenar, ialah anda mungkin mahu mengubah cara anda mengakses data. Mungkin anda ingin menukar kepada NoSql, atau beberapa sistem cache teragih memori. Atau, yang lebih berkemungkinan besar, anda bermula dengan struktur pangkalan data, mungkin pangkalan data monolitik, dan kini anda ingin memfaktorkannya semula ke dalam berbilang pangkalan data dengan struktur jadual yang berbeza. Izinkan saya memberitahu anda dengan segera bahawa ini adalah MUSTAHIL tanpa repositori.
Dan khusus untuk Rangka Kerja Entiti, entiti yang anda dapat ialah rekod aktif, dipetakan ke pangkalan data. Anda membuat perubahan dalam satu dan menyimpan perubahan untuk yang lain dan anda tiba-tiba mendapat entiti pertama dikemas kini dalam db juga. Atau mungkin anda tidak, kerana anda tidak memasukkan sesuatu, atau konteksnya telah berubah.
Penyokong EF sentiasa menggembar-gemburkan penjejakan entiti sebagai perkara yang sangat positif. Katakan anda mendapat entiti daripada pangkalan data, anda kemudian menjalankan perniagaan, kemudian anda mengemas kini entiti dan menyimpannya. Dengan repo anda akan mendapat data, kemudian menjalankan perniagaan, kemudian mendapatkan data sekali lagi untuk melakukan sedikit kemas kini. EF akan menyimpannya dalam ingatan, tahu ia tidak dikemas kini sebelum perubahan anda, jadi ia tidak akan membacanya dua kali. Itu benar. Mereka menerangkan cache memori untuk pangkalan data yang entah bagaimana menyedari perubahan pangkalan data dan menjejaki semua yang anda kendalikan daripada pangkalan data, melainkan jika diarahkan sebaliknya, dua hala memetakan entri pangkalan data ke entiti C# yang kompleks dan menjejaki perubahan berulang-alik, sambil dibenamkan secara mendalam dalam kod perniagaan. Secara peribadi, saya percaya banyak tanggungjawab dan kekurangan pengasingan kebimbangan ini jauh lebih merosakkan daripada prestasi yang diperoleh dengan menggunakannya. Selain itu, dengan beberapa usaha awal, semua fungsi itu masih boleh diabstraksikan dalam repositori, atau mungkin satu lagi lapisan cache memori untuk repositori, sambil mengekalkan sempadan yang jelas antara perniagaan, cache dan akses data.
Malah, kesukaran sebenar dalam semua ini ialah menentukan sempadan antara sistem yang sepatutnya mempunyai kebimbangan yang berasingan. Sebagai contoh, seseorang boleh memperoleh banyak prestasi dengan memindahkan logik penapisan ke prosedur tersimpan dalam pangkalan data, tetapi itu kehilangan kebolehujian dan kebolehbacaan algoritma yang digunakan. Sebaliknya, memindahkan semua logik ke kod, menggunakan EF atau beberapa mekanisme lain, adalah kurang berprestasi dan kadangkala tidak boleh dilaksanakan. Atau di manakah titik di mana entiti data menjadi entiti perniagaan (lihat contoh di atas dengan Jabatan dan Orang)?
Mungkin strategi terbaik adalah dengan mentakrifkan sempadan ini dahulu, kemudian memutuskan teknologi dan reka bentuk mana yang akan sesuai dengannya.
Saya percaya bahawa abstraksi perkhidmatan dan repositori harus sentiasa digunakan, walaupun repositori menggunakan Rangka Kerja Entiti atau ORM lain di bawahnya. Semuanya bermuara kepada pengasingan kebimbangan. Saya tidak akan menganggap Rangka Kerja Entiti sebagai abstraksi perisian yang berguna kerana ia disertakan dengan banyak bagasi, oleh itu repositori banyak digunakan untuk mengabstrakkannya dalam kod. EF ialah abstraksi yang berguna, tetapi untuk akses pangkalan data, bukan dalam perisian.
Falsafah penulisan perisian saya ialah anda bermula dengan keperluan aplikasi, anda mencipta komponen untuk keperluan tersebut dan mengabstrak mana-mana fungsi peringkat rendah dengan antara muka. Anda kemudian mengulangi proses pada peringkat seterusnya, sentiasa memastikan kod itu boleh dibaca dan ia tidak memerlukan pemahaman tentang komponen yang digunakan atau yang digunakan pada tahap semasa. Jika itu tidak berlaku, anda telah memisahkan kebimbangan dengan teruk. Oleh itu, memandangkan tiada aplikasi perniagaan pernah mempunyai keperluan untuk menggunakan pangkalan data atau ORM tertentu, abstraksi lapisan data harus menyembunyikan semua pengetahuan mengenainya.
Apa yang perniagaan mahukan? Senarai orang yang ditapis? var people = service.GetFilteredListOfPeople(filter); tidak kurang, tidak lebih. dan kaedah perkhidmatan hanya akan mengembalikan mapPeople(repo.GetFilteredListOfPeople(mappedFilter)); sekali lagi tidak kurang atau lebih. Bagaimana repo itu mendapatkan orang ramai, menyelamatkan orang ramai atau melakukan apa-apa lagi bukanlah kebimbangan perkhidmatan itu. Anda mahu caching, kemudian laksanakan beberapa mekanisme caching yang melaksanakan IPeopleRepository dan mempunyai pergantungan pada IPeopleRepository. Anda mahu pemetaan, laksanakan antara muka IMapper yang betul. Dan seterusnya.
Saya harap saya tidak terlalu verbose dalam artikel ini. Saya secara khusus menyimpan contoh kod daripadanya, kerana ini lebih kepada isu konseptual, bukan perisian. Rangka Kerja Entiti mungkin menjadi sasaran kebanyakan aduan saya di sini, tetapi ini terpakai pada mana-mana sistem yang ajaib membantu anda dalam perkara kecil, tetapi memecahkan yang penting.
Semoga ia membantu!
Atas ialah kandungan terperinci Akses data dalam kod, menggunakan repositori, walaupun dengan ORM. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!