TerryR เพื่อนของฉันคุณและฉันควรดื่ม เรามีปัญหาที่คล้ายกัน
1. โครงสร้างโครงการ:ฉันเห็นด้วยกับ Eduardo ว่าโครงสร้างโฟลเดอร์ในแอป MVC ทำให้บางสิ่งเป็นที่ต้องการ คุณมีโฟลเดอร์คอนโทรลเลอร์รุ่นและโฟลเดอร์มาตรฐาน แต่จากนั้นโฟลเดอร์ Views จะถูกแบ่งย่อยออกเป็นโฟลเดอร์ที่แตกต่างกันสำหรับคอนโทรลเลอร์แต่ละตัวรวมถึงโฟลเดอร์แชร์ และแต่ละ Views / ControllerName หรือ Views / Shared สามารถแบ่งออกเป็น EditorTemplates และ DisplayTemplates แต่ช่วยให้คุณตัดสินใจว่าจะจัดระเบียบโฟลเดอร์รุ่นของคุณได้อย่างไร (คุณสามารถทำได้โดยมีหรือไม่มีโฟลเดอร์ย่อย & การประกาศเนมสเปซเพิ่มเติม)
พระเจ้าห้ามไม่ให้คุณใช้พื้นที่ซึ่งซ้ำกับโครงสร้างโฟลเดอร์คอนโทรลเลอร์รุ่นและมุมมองสำหรับแต่ละพื้นที่
/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
ซึ่งหมายความว่าถ้าคุณทำงานกับ WidgetController คุณต้องมองหาโฟลเดอร์อื่น ๆ เพื่อค้นหา WidgetViewModels, WidgetViews, WidgetEditorTemplates, WidgetDisplayTemplates ฯลฯ ที่ยุ่งยากเช่นนี้ฉันติดกับมันและไม่เบี่ยงเบนไปจาก อนุสัญญา MVC เหล่านี้ เท่าที่วางโมเดลคอนโทรลเลอร์และมุมมองในโฟลเดอร์เดียวกัน แต่มีเนมสเปซที่แตกต่างกันฉันหลีกเลี่ยงสิ่งนี้เพราะฉันใช้ ReSharper มันจะขีดเส้นใต้เนมสเปซที่จะไม่ตรงกับโฟลเดอร์ที่คลาสนั้นอยู่ ฉันรู้ว่าฉันสามารถปิดคุณลักษณะ R # นี้ได้ แต่ช่วยในส่วนอื่น ๆ ของโครงการ
สำหรับไฟล์ที่ไม่ได้อยู่ในชั้นเรียน MVC จะมอบเนื้อหาและสคริปต์ให้กับคุณ เราพยายามเก็บไฟล์แบบคงที่ / ไม่ได้รวบรวมไว้ในที่เหล่านี้อีกครั้งเพื่อทำตามแบบแผน เมื่อใดก็ตามที่เรารวมไลบรารี js ที่ใช้ชุดรูปแบบ (รูปภาพและหรือ css) ไฟล์ชุดรูปแบบจะอยู่ใต้ / เนื้อหา สำหรับสคริปท์เราเพียงแค่ใส่ทั้งหมดลงใน / สคริปต์โดยตรง เดิมทีนี่คือการรับ JS Intellisense จาก VS แต่ตอนนี้เราได้รับ JS Intellisense จาก R # โดยไม่คำนึงถึงตำแหน่งใน / สคริปต์ฉันคิดว่าเราสามารถเบี่ยงเบนจากนั้นและแบ่งสคริปต์ตามโฟลเดอร์เพื่อจัดระเบียบได้ดีขึ้น คุณใช้ ReSharper หรือไม่ มันเป็นทองคำบริสุทธิ์ IMO
ทองคำอีกชิ้นเล็ก ๆ ที่ช่วยได้มากในการปรับสภาพคือ T4MVC เมื่อใช้สิ่งนี้เราไม่จำเป็นต้องพิมพ์เส้นทางสตริงสำหรับชื่อพื้นที่ชื่อตัวควบคุมชื่อการกระทำแม้แต่ไฟล์ในเนื้อหาและสคริปต์ T4MVC ขอพิมพ์สตริงมายากลทั้งหมดสำหรับคุณ นี่คือตัวอย่างเล็ก ๆ ของวิธีที่โครงสร้างโครงการของคุณไม่สำคัญหากคุณใช้ 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. การเข้าถึงข้อมูล:ฉันไม่มีประสบการณ์กับ PetaPoco แต่ฉันแน่ใจว่ามันคุ้มค่าที่จะเช็คเอาท์ สำหรับรายงานที่ซับซ้อนของคุณคุณได้พิจารณาบริการการรายงานของ SQL Server หรือไม่ หรือคุณกำลังรันบน db อื่นอยู่หรือไม่? ขอโทษฉันไม่ชัดเจนในสิ่งที่คุณขอ เราใช้ EF + LINQ แต่เรายังให้ความรู้เกี่ยวกับวิธีสร้างรายงานในคลาสโดเมน ดังนั้นเราจึงมีที่เก็บข้อมูลการโทรสำหรับโดเมนบริการคอนโทรลเลอร์แทนการมีที่เก็บข้อมูลการโทรของคอนโทรลเลอร์โดยตรง สำหรับรายงานเฉพาะกิจเราใช้บริการรายงานของ SQL ซึ่งไม่สมบูรณ์แบบอีกต่อไป แต่ผู้ใช้ของเราต้องการที่จะนำข้อมูลเข้าสู่ Excel ได้อย่างง่ายดายและ SSRS ทำให้เราง่ายขึ้น
3. การจัดการรหัสฝั่งไคลเอ็นต์และการแสดงผล UI:นี่คือที่ฉันคิดว่าฉันสามารถให้ความช่วยเหลือได้ นำหน้าจากหนังสือการตรวจสอบ MVC ที่ไม่เป็นการรบกวนและ AJAX ที่ไม่เป็นการรบกวน พิจารณาสิ่งนี้:
<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" />
ไม่สนใจฟังก์ชันความสำเร็จ ajax ในตอนนี้ (เพิ่มเติมในภายหลัง) คุณสามารถออกไปพร้อมกับสคริปต์เดียวสำหรับการกระทำบางอย่างของคุณ:
$('.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();
}
}
}
});
}
});
รหัสด้านบนจะดูแลการยืนยันการแสดงปินเนอร์แสดงข้อความรอและการซ่อนข้อความปินเนอร์ / รอหลังจากการโทร ajax เสร็จสมบูรณ์ คุณกำหนดค่าพฤติกรรมโดยใช้คุณลักษณะ data- * เช่นไลบรารีที่ไม่สร้างความรำคาญ
คำถามทั่วไป
- ไคลเอ็นต์ MVC กับเซิร์ฟเวอร์ MVC? ฉันไม่ได้พยายามทำให้การดำเนินการที่คุณทำไว้ในฟังก์ชั่นความสำเร็จเป็นจริงเพราะดูเหมือนว่าคอนโทรลเลอร์ของคุณจะส่งคืน JSON หากคอนโทรลเลอร์ของคุณส่งคืน JSON คุณอาจต้องการดู KnockoutJS สิ่งที่น่าพิศวง JS รุ่น 2.0 ได้รับการปล่อยตัวในวันนี้ มันสามารถเสียบลงใน JSON ของคุณเพื่อให้การคลิกที่สังเกตได้สามารถผูกข้อมูลกับแม่แบบจาวาสคริปต์ของคุณโดยอัตโนมัติ ในทางกลับกันถ้าคุณไม่คิดว่าการใช้วิธี ajax ของคุณคืนค่า HTML แทน JSON พวกมันสามารถคืนค่า UL ที่สร้างขึ้นแล้วพร้อมกับลูก ๆ ของ LI และคุณสามารถต่อท้ายองค์ประกอบนั้นโดยใช้ data-myapp-response = "ผล". ฟังก์ชั่นความสำเร็จของคุณจะมีลักษณะเช่นนี้:
success: function(html) {
var responseId = $(clicked).data('myapp-response');
if (responseId) {
$('#' + responseId).empty().html(html);
}
}
เพื่อสรุปคำตอบที่ดีที่สุดของฉันหากคุณต้องส่งคืน JSON จากวิธีการดำเนินการของคุณคุณกำลังข้ามมุมมองฝั่งเซิร์ฟเวอร์ดังนั้นนี่ไม่ใช่เซิร์ฟเวอร์ MVC - เป็นเพียง MC หากคุณส่งคืน PartialViewResult ด้วย html ถึง ajax call นี่คือเซิร์ฟเวอร์ MVC ดังนั้นหากแอปของคุณต้องส่งคืนข้อมูล JSON สำหรับการโทร ajax ให้ใช้ไคลเอนต์ MVVM เช่น KnockoutJS
ไม่ว่าด้วยวิธีใดฉันไม่ชอบ JS ที่คุณโพสต์เพราะมันผสมการจัดวาง (แท็ก html) กับพฤติกรรม (โหลดข้อมูลแบบอะซิงโครนัส) การเลือกเซิร์ฟเวอร์ MVC ด้วยมุมมอง html บางส่วนหรือไคลเอนต์ MVVM พร้อมด้วยข้อมูล JSON viewmodel ล้วนจะแก้ปัญหานี้ให้คุณ แต่การสร้าง DOM / HTML ด้วยตนเองใน javascript เป็นการละเมิดข้อกังวล
- การสร้างไฟล์จาวาสคริเห็นได้ชัดว่าคุณสมบัติ minification ที่มีมาใน .NET 4.5 หากคุณไปเส้นทางที่ไม่เป็นการรบกวนคุณไม่ควรหยุดการโหลดไฟล์ JS ใน 1 สคริปต์ทั้งหมด ฉันจะระมัดระวังเกี่ยวกับการสร้างไฟล์ JS ที่แตกต่างกันสำหรับแต่ละประเภทเอนทิตีคุณจะจบลงด้วยการกระจายไฟล์ JS โปรดจำไว้ว่าเมื่อโหลดไฟล์สคริปต์ของคุณแล้วเบราว์เซอร์ควรทำการแคชสำหรับคำขอในอนาคต
- ข้อความค้นหาที่ซับซ้อนฉันไม่คิดว่าจะมีฟีเจอร์เช่นเลขหน้าการเรียงลำดับ ฯลฯ ว่าซับซ้อน การตั้งค่าของฉันคือการจัดการกับตรรกะของ URL และฝั่งเซิร์ฟเวอร์เพื่อให้การสืบค้น db มี จำกัด ตามที่ต้องการ อย่างไรก็ตามเราถูกปรับใช้กับ Azure ดังนั้นการเพิ่มประสิทธิภาพข้อความค้นหาจึงเป็นสิ่งสำคัญสำหรับเรา ตัวอย่างเช่น/widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}
. EF และ LINQ to Entities สามารถจัดการการแบ่งหน้าและการเรียงลำดับด้วยวิธีการเช่น. Take (), .Skip (), .OrderBy (), และ. OrderByDescending () ดังนั้นคุณจะได้รับสิ่งที่คุณต้องการในระหว่างการเดินทาง db ฉันยังไม่พบความต้องการของลูกค้าดังนั้นฉันจึงไม่ทราบเกี่ยวกับพวกเขามากนัก ดูคำตอบอื่น ๆ สำหรับคำแนะนำเพิ่มเติมเกี่ยวกับเรื่องนั้น
- ไหมโครงการไม่เคยได้ยินเรื่องนี้จะต้องตรวจสอบออก ฉันเป็นแฟนตัวยงของ Steve Sanderson หนังสือของเขา BeginCollectionItem HtmlHelper และบล็อกของเขา ที่กล่าวว่าผมไม่ได้มีประสบการณ์กับ KnockoutJS ในการผลิต ฉันได้ดูบทแนะนำแล้ว แต่ฉันไม่พยายามที่จะผูกมัดอะไรจนกว่ามันจะเป็นเวอร์ชั่น 2.0 เป็นอย่างน้อย อย่างที่ฉันพูดถึง KnockoutJS 2.0 เพิ่งเปิดตัว
- N-tierถ้าตามระดับคุณหมายถึงเครื่องจักรที่แตกต่างกันจริง ๆ แล้วฉันไม่คิดว่าจะมีอะไรออกไปนอกหน้าต่าง โดยทั่วไป 3 ชั้นหมายถึงคุณมี 3 เครื่อง ดังนั้นคุณอาจมีลูกค้าที่อ้วนเป็นระดับการนำเสนอของคุณที่ทำงานบนเครื่องของผู้ใช้ ไคลเอ็นต์ไขมันอาจเข้าถึงระดับบริการที่ทำงานบนเซิร์ฟเวอร์แอปพลิเคชันและส่งคืน XML หรือสิ่งใด ๆ ไปยังลูกค้าระดับไขมัน และระดับบริการอาจได้รับข้อมูลจากเซิร์ฟเวอร์ SQL บนเครื่องที่ 3
MVC เป็นหนึ่งเลเยอร์ใน 1 ชั้น คอนโทรลเลอร์รุ่นและมุมมองของคุณเป็นส่วนหนึ่งของ Presentation Layer ของคุณซึ่งเป็น 1 Tier ในสถาปัตยกรรมฟิสิคัล MVC ใช้รูปแบบ Model-View-Controller ซึ่งเป็นที่ที่คุณอาจเห็นเลเยอร์เพิ่มเติม อย่างไรก็ตามอย่าพยายามคิดสามแง่มุมเหล่านี้ว่าเป็นเทียร์หรือเลเยอร์ ลองคิดถึงทั้งสามคนนี้เป็น Presentation Layer Concerns
อัปเดตหลังจากแสดงความคิดเห็นล่วงหน้า / บัส / ข้อมูล
โอเคคุณใช้เทียร์และเลเยอร์สลับกันได้ ฉันมักจะใช้คำว่า "เลเยอร์" สำหรับแผนกตรรกะ / โครงการ / แอสเซมบลีและชั้นสำหรับการแยกเครือข่ายทางกายภาพ ขอโทษสำหรับความสับสน.
คุณจะพบว่ามีคนไม่กี่คนในค่าย MVC ที่บอกว่าคุณไม่ควรใช้ "โมเดล" ใน MVC สำหรับโมเดลข้อมูลเอนทิตีของคุณและคุณไม่ควรใช้ตัวควบคุมสำหรับตรรกะทางธุรกิจ โดยที่แบบของคุณควรเป็น ViewModels ที่เฉพาะเจาะจงสำหรับการดู เมื่อใช้บางอย่างเช่น Automapper คุณจะนำเอนทิตีของคุณจากโมเดลโดเมนของคุณและ DTO ลงใน ViewModels ซึ่งสร้างขึ้นเป็นพิเศษเพื่อใช้ในมุมมอง
กฎเกณฑ์ทางธุรกิจใด ๆ ก็ควรเป็นส่วนหนึ่งของโดเมนของคุณและคุณสามารถนำไปใช้งานได้โดยใช้บริการโดเมน / รูปแบบโรงงาน / สิ่งที่เหมาะสมในเลเยอร์โดเมนของคุณไม่ใช่ในเลเยอร์การนำเสนอ MVC ผู้ควบคุมควรเป็นคนโง่แม้ว่าจะไม่ใช่คนโง่เท่ารุ่นและควรให้ความสำคัญกับโดเมนสำหรับทุกสิ่งที่ต้องการความรู้ทางธุรกิจ ผู้ควบคุมจัดการการไหลของคำร้องขอและการตอบกลับ HTTP แต่สิ่งใดก็ตามที่มีมูลค่าทางธุรกิจจริงควรอยู่เหนือระดับการจ่ายเงินของผู้ควบคุม
ดังนั้นคุณยังสามารถมีสถาปัตยกรรมแบบเลเยอร์โดยมี MVC เป็นเลเยอร์การนำเสนอ มันเป็นไคลเอนต์ของแอปพลิเคชันเลเยอร์เลเยอร์บริการหรือเลเยอร์โดเมนของคุณขึ้นอยู่กับวิธีที่คุณออกแบบ แต่ท้ายที่สุดโมเดลเอนทิตีของคุณควรเป็นส่วนหนึ่งของโดเมนไม่ใช่โมเดลใน MVC