TerryR мій друг, ми з вами повинні випити. У нас є деякі подібні проблеми.
1. Структура проекту: Я погоджуюся з Едуардо, що структура папок у додатку MVC залишає бажати кращого. У вас є стандартні папки Контролери, Моделі та Перегляди. Але потім папка Views розбивається на іншу папку для кожного контролера, а також загальну папку. І кожне View / 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? ІМО чистого золота.
Ще один невеликий шматочок золота, який дуже допомагає при рефакторингу, - це 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. Організація коду на стороні клієнта та надання інтерфейсу користувача: саме там, на мою думку, я можу запропонувати допомогу. Візьміть сторінку з книги ненав’язливої перевірки 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. Сьогодні вийшов Knockout JS версії 2.0 . Він може підключатися прямо до вашого JSON, так що помітний клацання може автоматично прив’язувати дані до ваших шаблонів javascript. З іншого боку, якщо ви не заперечуєте проти того, щоб ваші методи дій 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, це сервер MVC. Тож якщо ваша програма повинна повертати дані JSON для дзвінків Ajax, використовуйте клієнтський MVVM, як KnockoutJS.
Так чи інакше, мені не подобається розміщений вами JS, оскільки він змішує ваш макет (html-теги) з поведінкою (асинхронна навантаження даних). Вибір сервера MVC з частковими переглядами html або клієнтського MVVM з чистими даними перегляду JSON viewmodel вирішить цю проблему для вас, але побудова DOM / HTML у JavaScript вручну порушує розділення проблем.
- Створення файлів Javascript. Очевидно, що функції мінімізації надходять у .NET 4.5 . Якщо ви проходите ненав’язливим шляхом, це не повинно перешкоджати завантаженню всіх ваших JS в 1 файл сценарію. Я буду обережно створювати різні файли JS для кожного типу об'єкта, ви закінчите вибух файлів JS. Пам'ятайте, щойно ваш файл сценарію завантажується, браузер повинен кешувати його для майбутніх запитів.
- Складні запити, які я не вважаю такими, як складова сторінки, сортування, тощо, як складні. Моя перевага - це обробляти це за допомогою URL-адрес та логіки на стороні сервера, щоб максимально обмежити запити db. Однак ми розміщені в Azure, тому для нас важлива оптимізація запитів. Наприклад: /widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}
. EF та LINQ для сутностей можуть обробляти пагинацію та сортування за допомогою таких методів, як. Я ще не знайшов потреби у клієнтськійлібі, тому я, чесно кажучи, не знаю багато про них. Подивіться на інші відповіді, щоб отримати більше порад щодо цього.
- Шовк проекту Ніколи про це не чув, доведеться його перевірити. Я великий шанувальник Стіва Сандерсона, його книг, його BeginCollectionItem HtmlHelper та його блогу. Це означає, що я не маю досвіду роботи з KnockoutJS у виробництві . Я перевірив його підручники, але намагаюся не брати на себе зобов’язання, поки це не принаймні версія 2.0. Як я вже згадував, KnockoutJS 2.0 щойно вийшов.
- N-ярус Якщо під рівнем ви маєте на увазі різні фізичні машини, то ні, я не думаю, що нічого не виходить із вікон. Як правило, 3-х рівневий означає, що у вас є 3 машини. Таким чином, у вас може бути жирний клієнт, як ваш рівень презентації, який працює на машині користувача. Клієнт жиру може отримати доступ до рівня обслуговування, який працює на сервері додатків і повертає XML або будь-який інший товстий клієнт. І сервісний рівень може отримувати свої дані з SQL-сервера на 3-й машині.
MVC - це один шар, на 1 ярусі. Ваші контролери, моделі та види - це частина вашого шару презентації, який є 1 рівнем фізичної архітектури. MVC реалізує модель Model-View-Controller, де ви можете бачити додаткові шари. Однак намагайтеся не розглядати ці 3 аспекти як яруси або шари. Спробуйте подумати про всі 3 з них як про ступінь презентаційного рівня.
Оновлення після прес / шини / коментаря до даних
Гаразд, значить, ви використовуєте рівень і шар безперервно. Зазвичай я використовую термін "шар" для розділів логічного / проекту / складання, а рівень для фізичного розділення мережі. Вибачте за непорозуміння.
У таборі MVC ви знайдете досить багато людей, які кажуть, що не слід використовувати "Моделі" в MVC для вашої моделі даних, а також не використовувати контролери для бізнес-логіки. В ідеалі ваші моделі повинні бути ViewModels, орієнтовані на перегляд. Використовуючи щось на кшталт Automapper, ви берете свої об'єкти з вашої доменної моделі та переносите їх у ViewModels, скульптурну спеціально для використання у представленні.
Будь-які бізнес-правила також повинні бути частиною вашого домену, і ви можете їх реалізувати, використовуючи доменні служби / заводський зразок / все, що підходить у вашому доменному шарі, а не в шарі презентації MVC. Контролери повинні бути тупими, хоча і не настільки німими, як моделі, і повинні відповідати домену за все, що вимагає ділових знань. Контролери управляють потоком HTTP-запитів та відповідей, але все, що має реальну ділову цінність, повинно бути вище рівня оплати контролера.
Таким чином, ви все ще можете мати шарувату архітектуру, з MVC як презентаційний шар. Це клієнт рівня вашого додатка, рівня обслуговування або доменного рівня, залежно від способу його архітектури. Але в кінцевому рахунку модель вашої сутності повинна бути частиною домену, а не моделями в MVC.