TerryR teman saya, Anda dan saya harus minum. Kami memiliki beberapa masalah serupa.
1. Struktur Proyek: Saya setuju dengan Eduardo bahwa struktur folder dalam aplikasi MVC meninggalkan sesuatu yang diinginkan. Anda memiliki folder Pengontrol, Model, dan Tampilan standar Anda. Tetapi kemudian folder Views dipecah menjadi folder yang berbeda untuk masing-masing Pengendali, ditambah folder Bersama. Dan setiap Views / ControllerName atau Views / Shared dapat dipecah menjadi EditorTemplates dan DisplayTemplates. Tapi itu memungkinkan Anda memutuskan bagaimana mengatur folder Model Anda (Anda dapat melakukannya dengan atau tanpa subfolder & deklarasi namespace tambahan).
Tuhan melarang Anda menggunakan Area, yang menduplikasi struktur folder Controller, Model, dan Views untuk masing-masing area.
/Areas
/Area1Name
/Controllers
FirstController.cs
SecondController.cs
ThirdController.cs
/Models
(can organize all in here or in separate folders / namespaces)
/Views
/First
/DisplayTemplates
WidgetAbc.cshtml <-- to be used by views in Views/First
/EditorTemplates
WidgetAbc.cshtml <-- to be used by views in Views/First
PartialViewAbc.cshtml <-- to be used by FirstController
/Second
PartialViewDef.cshtml <-- to be used by SecondController
/Third
PartialViewMno.cshtml <-- to be used by ThirdController
/Shared
/DisplayTemplates
WidgetXyz.cshtml <-- to be used by any view in Area1
/EditorTemplates
WidgetXyz.cshtml <-- to be used by any view in Area1
PartialViewXyz.cshtml <-- to be used anywhere in Area1
_ViewStart.cshtml <-- area needs its own _ViewStart.cshtml
Web.config <-- put custom HTML Helper namespaces in here
Area1NameRegistration.cs <-- define routes for area1 here
/Area2Name
/Controllers
/Models
/Views
Area2NameRegistration.cs <-- define routes for area2 here
/Controllers
AccountController.cs
HomeController.cs
/Models
/Views
/Account
/DisplayTemplates
WidgetGhi.cshtml <-- to be used views in Views/Account
/EditorTemplates
WidgetGhi.cshtml <-- to be used views in Views/Account
PartialViewGhi.cshtml <-- to be used by AccountController
/Home
(same pattern as Account, views & templates are controller-specific)
/Shared
/DisplayTemplates
EmailAddress.cshtml <-- to be used by any view in any area
Time.cshtml <-- to be used by any view in any area
Url.cshtml <-- to be used by any view in any area
/EditorTemplates
EmailAddress.cshtml <-- to be used by any view in any area
Time.cshtml <-- to be used by any view in any area
Url.cshtml <-- to be used by any view in any area
_Layout.cshtml <-- master layout page with sections
Error.cshtml <-- custom page to show if unhandled exception occurs
_ViewStart.cshtml <-- won't be used automatically in an area
Web.config <-- put custom HTML Helper namespaces in here
Ini berarti jika Anda bekerja dengan sesuatu seperti WidgetController, Anda harus mencari di folder lain untuk menemukan WidgetViewModels, WidgetViews, WidgetEditorTemplates terkait, WidgetDisplayTemplates terkait, dll. Seperti rumitnya, saya tetap menggunakannya dan jangan menyimpang dari konvensi MVC ini. Sejauh meletakkan model, pengontrol, dan tampilan di folder yang sama tetapi dengan ruang nama yang berbeda, saya menghindari ini karena saya menggunakan ReSharper. Ini akan secara singkat menggarisbawahi namespace yang tidak cocok dengan folder di mana kelas berada. Saya tahu saya bisa mematikan fitur R # ini, tetapi membantu di bagian lain dari proyek.
Untuk file non-kelas, MVC memberi Anda Konten dan Skrip di luar kotak. Kami mencoba menyimpan semua file statis / tidak dikompilasi di tempat-tempat ini, sekali lagi, untuk mengikuti konvensi. Setiap kali kami menggabungkan perpustakaan js yang menggunakan tema (gambar dan atau css), file tema semua pergi ke suatu tempat di bawah / konten. Untuk skrip, kami langsung memasukkan semuanya ke / skrip. Awalnya ini adalah untuk mendapatkan JS intellisense dari VS, tetapi sekarang kita mendapatkan JS intellisense dari R # terlepas dari penempatan di / skrip, saya kira kita bisa menyimpang dari itu dan membagi skrip dengan folder untuk mengatur lebih baik. Apakah Anda menggunakan ReSharper? Itu adalah IMO emas murni.
Sepotong emas kecil lain yang banyak membantu dalam refactoring adalah T4MVC. Dengan menggunakan ini, kita tidak perlu mengetikkan jalur string untuk nama area, nama controller, nama tindakan, bahkan file dalam konten & skrip. T4MVC sangat mengetik semua string ajaib untuk Anda. Berikut adalah contoh kecil tentang bagaimana struktur proyek Anda tidak terlalu penting jika Anda menggunakan T4MVC:
// no more magic strings in route definitions
context.MapRoutes(null,
new[] { string.Empty, "features", "features/{version}" },
new
{
area = MVC.PreviewArea.Name,
controller = MVC.PreviewArea.Features.Name,
action = MVC.PreviewArea.Features.ActionNames.ForPreview,
version = "december-2011-preview-1",
},
new { httpMethod = new HttpMethodConstraint("GET") }
);
@* T4MVC renders .min.js script versions when project is targeted for release *@
<link href="@Url.Content(Links.content.Site_css)?r=201112B" rel="stylesheet" />
<script src="@Url.Content(Links.scripts.jquery_1_7_1_js)" type="text/javascript">
</script>
@* render a route URL as if you were calling an action method directly *@
<a href="@Url.Action(MVC.MyAreaName.MyControllerName.MyActionName
(Model.SomeId))">@Html.DisplayFor(m => m.SomeText)</a>
// call action redirects as if you were executing an action method
return RedirectToAction(MVC.Area.MyController.DoSomething(obj1.Prop, null));
2. Akses data: Saya tidak punya pengalaman dengan PetaPoco, tapi saya yakin ini layak untuk dicoba. Untuk laporan kompleks Anda, sudahkah Anda mempertimbangkan layanan Pelaporan SQL Server? Atau, apakah Anda menjalankan db yang berbeda? Maaf saya tidak jelas apa yang sebenarnya Anda minta. Kami menggunakan EF + LINQ, tetapi kami juga menaruh pengetahuan tertentu tentang cara membuat laporan di kelas domain. Dengan demikian, kami memiliki repositori panggilan layanan panggilan pengontrol, alih-alih memiliki repositori panggilan pengontrol secara langsung. Untuk laporan ad-hoc kami menggunakan Layanan Pelaporan SQL, yang sekali lagi tidak sempurna, tetapi pengguna kami ingin dapat membawa data ke Excel dengan mudah, dan SSR membuatnya mudah bagi kami.
3. Organisasi Kode Side-Klien dan Rendering UI: Di sinilah saya pikir saya dapat menawarkan bantuan. Ambil satu halaman dari buku MVC validasi yang tidak mencolok dan AJAX yang tidak mencolok. Pertimbangkan ini:
<img id="loading_spinner" src="/path/to/img" style="display:none;" />
<h2 id="loading_results" style="display:none;">
Please wait, this may take a while...
</h2>
<div id="results">
</div>
<input id="doSomethingDangerous" class="u-std-ajax"
type="button" value="I'm feeling lucky"
data-myapp-confirm="Are you sure you want to do this?"
data-myapp-show="loading_spinner,loading_results"
data-myapp-href="blah/DoDangerousThing" />
Abaikan fungsi sukses ajax untuk saat ini (lebih lanjut tentang ini nanti). Anda dapat lolos dengan satu skrip untuk beberapa tindakan Anda:
$('.u-std-ajax').click(function () {
// maybe confirm something first
var clicked = this;
var confirmMessage = $(clicked).data('myapp-confirm');
if (confirmMessage && !confirm(confirmMessage )) { return; }
// show a spinner? something global would be preferred so
// I dont have to repeat this on every page
// maybe the page should notify the user of what's going on
// in addition to the dialog?
var show = $(clicked).data('myapp-show');
if (show) {
var i, showIds = show.split(',');
for (i = 0; i < showIds.length; i++) {
$('#' + showIds[i]).show();
}
}
var url = $(clicked).data('myapp-href');
if (url) {
$.ajax({
url: url,
complete: function () {
// Need to hide the spinner, again would prefer to
// have this done elsewhere
if (show) {
for (i = 0; i < showIds.length; i++) {
$('#' + showIds[i]).hide();
}
}
}
});
}
});
Kode di atas akan menangani konfirmasi, menampilkan pemintal, menampilkan pesan tunggu, dan menyembunyikan pesan pemintal / tunggu setelah panggilan ajax selesai. Anda mengonfigurasi perilaku menggunakan atribut data- *, seperti pustaka yang tidak mengganggu.
Pertanyaan Umum
- Klien MVC vs server MVC? Saya tidak mencoba untuk menjelaskan tindakan yang Anda ambil dalam fungsi sukses karena sepertinya controller Anda mengembalikan JSON. Jika pengendali Anda mengembalikan JSON, Anda mungkin ingin melihat KnockoutJS. Knockout JS versi 2.0 dirilis hari ini . Itu dapat menyambungkan langsung ke JSON Anda, sehingga klik yang dapat diamati dapat secara otomatis mengikat data ke templat javascript Anda. Di sisi lain jika Anda tidak keberatan memiliki metode aksi ajax Anda mengembalikan HTML alih-alih JSON, mereka dapat mengembalikan UL yang sudah dibangun dengan anak-anak LI-nya, dan Anda dapat menambahkannya ke elemen dengan menggunakan data-myapp-response = "hasil". Fungsi kesuksesan Anda kemudian akan terlihat seperti ini:
success: function(html) {
var responseId = $(clicked).data('myapp-response');
if (responseId) {
$('#' + responseId).empty().html(html);
}
}
Untuk meringkas jawaban terbaik saya untuk ini, jika Anda harus mengembalikan JSON dari metode tindakan Anda, Anda melewatkan Tampilan sisi server, jadi ini sebenarnya bukan server MVC - ini hanya MC. Jika Anda mengembalikan PartialViewResult dengan html ke panggilan ajax, ini adalah server MVC. Jadi, jika aplikasi Anda harus mengembalikan data JSON untuk panggilan ajax, gunakan MVVM klien seperti KnockoutJS.
Either way, saya tidak suka JS yang Anda posting karena mencampur tata letak Anda (tag html) dengan perilaku (memuat data asinkron). Memilih server MVC dengan tampilan html parsial atau klien MVVM dengan data model tampilan JSON murni akan menyelesaikan masalah ini untuk Anda, tetapi membuat DOM / HTML secara manual dalam javascript melanggar pemisahan kekhawatiran.
- Pembuatan file Javascript Tampaknya fitur minifikasi datang dalam .NET 4.5 . Jika Anda memilih rute yang tidak mencolok, seharusnya tidak ada yang menghentikan Anda dari memuat semua JS Anda dalam 1 file skrip. Saya akan berhati-hati tentang membuat file JS yang berbeda untuk setiap jenis entitas, Anda akan berakhir dengan ledakan file JS. Ingat, setelah file skrip Anda dimuat, browser harus menyimpannya untuk permintaan di masa mendatang.
- Pertanyaan kompleks Saya tidak menganggap memiliki fitur seperti pagination, sorting, dll, sebagai yang kompleks. Preferensi saya adalah untuk menangani ini dengan URL dan logika sisi server, untuk membuat permintaan db sebatas yang diperlukan. Namun kami dikerahkan ke Azure, jadi optimisasi kueri penting bagi kami. Sebagai contoh: /widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}
. EF dan LINQ ke Entitas dapat menangani pagination dan pengurutan dengan metode seperti. Take (), .Skip (), .OrderBy (), dan .OrderByDescending (), sehingga Anda mendapatkan apa yang Anda butuhkan selama perjalanan db. Saya belum menemukan kebutuhan untuk clientlib, jadi saya jujur tidak tahu banyak tentang mereka. Lihat jawaban lain untuk saran lebih lanjut tentang itu.
- Proyek sutera Belum pernah mendengar yang satu ini, harus memeriksanya. Saya penggemar berat Steve Sanderson, buku-bukunya, BeginCollectionItem HtmlHelper-nya, dan blog-nya. Yang mengatakan, saya tidak punya pengalaman dengan KnockoutJS dalam produksi . Saya telah memeriksa tutorialnya, tetapi saya mencoba untuk tidak melakukan sesuatu sampai setidaknya versi 2.0. Seperti yang saya sebutkan, KnockoutJS 2.0 baru saja dirilis.
- N-tier Jika menurut Anda maksud mesin fisik yang berbeda, maka tidak, saya tidak berpikir ada yang keluar dari jendela. Umumnya 3-tier berarti Anda memiliki 3 mesin. Jadi, Anda mungkin memiliki klien gemuk sebagai tingkat presentasi Anda, yang berjalan pada mesin pengguna. Klien gemuk mungkin mengakses tingkat layanan, yang berjalan di server aplikasi dan mengembalikan XML atau apa pun ke klien gemuk. Dan tingkat layanan mungkin mendapatkan datanya dari server SQL pada mesin ke-3.
MVC adalah satu lapisan, pada 1 tingkat. Kontroler, model, dan tampilan Anda adalah bagian dari Lapisan Presentasi Anda, yang merupakan 1 tingkat dalam arsitektur fisik. MVC mengimplementasikan pola Model-View-Controller, di mana Anda mungkin melihat lapisan tambahan. Namun, cobalah untuk tidak menganggap 3 aspek ini sebagai tingkatan atau lapisan. Cobalah untuk memikirkan ketiganya sebagai Presentation Layer Concerns.
Perbarui setelah komentar pres / bus / data
Oke, jadi Anda menggunakan tier dan layer secara bergantian. Saya biasanya menggunakan istilah "layer" untuk divisi logical / project / assembly, dan tier untuk pemisahan jaringan fisik. Maaf bila membingungkan.
Anda akan menemukan beberapa orang di kamp MVC yang mengatakan Anda tidak boleh menggunakan "Model" di MVC untuk model data entitas Anda, Anda juga tidak boleh menggunakan Pengontrol Anda untuk logika bisnis. Idealnya model Anda harus ViewModels khusus tampilan. Menggunakan sesuatu seperti Automapper, Anda mengambil entitas Anda dari model domain Anda dan mengubahnya menjadi ViewModels, diukir secara khusus untuk digunakan oleh tampilan.
Setiap aturan bisnis juga harus menjadi bagian dari domain Anda, dan Anda dapat menerapkannya menggunakan layanan domain / pola pabrik / apa pun yang sesuai di lapisan domain Anda, bukan di lapisan presentasi MVC. Pengontrol harus bodoh, meskipun tidak sebodoh model, dan harus memberikan tanggung jawab kepada domain untuk apa pun yang membutuhkan pengetahuan bisnis. Pengontrol mengelola aliran permintaan dan respons HTTP, tetapi apa pun dengan nilai bisnis nyata harus di atas nilai gaji pengontrol.
Jadi, Anda masih dapat memiliki arsitektur berlapis, dengan MVC sebagai lapisan presentasi. Ini adalah klien dari lapisan aplikasi Anda, lapisan layanan, atau lapisan domain, tergantung pada bagaimana Anda mendesainnya. Tetapi pada akhirnya model entitas Anda harus menjadi bagian dari domain, bukan model dalam MVC.