TerryR мой друг, мы с тобой должны выпить. У нас есть похожие проблемы.
1. Структура проекта. Я согласен с Eduardo в том, что структура папок в приложении MVC оставляет желать лучшего. У вас есть стандартные папки Controllers, Models и Views. Но затем папка «Виды» разбивается на разные папки для каждого контроллера и общую папку. И каждое Views / ControllerName или Views / Shared можно разбить на EditorTemplates и DisplayTemplates. Но это позволяет вам решить, как организовать вашу папку Models (вы можете обойтись с или без подпапок и дополнительных объявлений пространства имен).
Не дай Бог, вы используете Области, которые дублируют структуру папок Controllers, Models и Views для каждой области.
/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), все файлы тем попадают куда-то в / content. Для сценария мы просто помещаем их все непосредственно в / scripts. Первоначально это было для получения IntelliSense JS от VS, но теперь, когда мы получаем IntelliSense JS от R # независимо от размещения в / scripts, я полагаю, мы могли бы отклониться от этого и разделить сценарии по папкам для лучшей организации. Вы используете 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? Или вы работаете на другой БД? Извините, мне не ясно, о чем именно вы спрашиваете. Мы используем EF + LINQ, но мы также вкладываем определенные знания о том, как генерировать отчеты в классах домена. Таким образом, мы имеем хранилище вызовов службы домена вызова контроллера вместо прямого хранилища вызовов контроллера. Для специальных отчетов мы используем SQL Reporting Services, что опять-таки не идеально, но наши пользователи хотели бы иметь возможность легко переносить данные в 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 из ваших методов действий, вы пропускаете View на стороне сервера, так что это действительно не MVC сервера - это просто MC. Если вы возвращаете PartialViewResult с html вызовам ajax, это сервер MVC. Поэтому, если ваше приложение должно возвращать данные JSON для вызовов ajax, используйте клиентский MVVM, такой как KnockoutJS.
В любом случае, мне не нравится тот JS, который вы опубликовали, потому что он смешивает ваш макет (HTML-теги) с поведением (асинхронная загрузка данных). Выбор сервера MVC с частичными представлениями html или клиента MVVM с чистыми данными модели представления JSON решит эту проблему для вас, но ручное построение DOM / HTML в javascript нарушает разделение проблем.
- Создание файла Javascript. Очевидно, функции минимизации появятся в .NET 4.5 . Если вы идете по ненавязчивому пути, ничто не помешает вам загрузить все ваши файлы JS в 1 скриптовом файле. Я буду осторожен с созданием разных файлов JS для каждого типа сущности, в результате вы получите взрыв файла JS. Помните, что после загрузки файла скрипта браузер должен его кешировать для будущих запросов.
- Сложные запросы, которые я не считаю такими сложными, как разбиение на страницы, сортировка и т. Д. Я предпочитаю обрабатывать это с помощью URL-адреса и серверной логики, чтобы сделать запросы к базе данных настолько ограниченными, насколько это необходимо. Однако мы развернуты в Azure, поэтому для нас важна оптимизация запросов. Например: /widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}
. EF и LINQ to Entities могут обрабатывать разбиение на страницы и сортировку с помощью таких методов, как .Take (), .Skip (), .OrderBy () и .OrderByDescending (), так что вы получите то, что вам нужно во время поездки по БД. Я еще не нашел необходимость в клиентской жизни, поэтому, честно говоря, я мало что о них знаю. Посмотрите другие ответы для получения дополнительных советов по этому вопросу.
- Проект шелка Никогда не слышал об этом, придется проверить это. Я большой поклонник Стива Сандерсона, его книг, его BeginCollectionItem HtmlHelper и его блога. Тем не менее, у меня нет никакого опыта работы с KnockoutJS в производстве . Я проверил его учебники, но я стараюсь не совершать что-либо, пока не выйдет хотя бы версия 2.0. Как я уже говорил, KnockoutJS 2.0 был только что выпущен.
- N-уровень Если под уровнем вы подразумеваете другую физическую машину, то нет, я не думаю, что что-либо выходит за рамки. Обычно 3-х уровневый означает, что у вас есть 3 машины. Таким образом, у вас может быть толстый клиент в качестве уровня представления, который работает на компьютере пользователя. Толстый клиент может получить доступ к уровню обслуживания, который выполняется на сервере приложений и возвращает толстый клиент XML или что-то еще. А сервисный уровень может получать свои данные с SQL-сервера на 3-м компьютере.
MVC - это один слой на один уровень. Все ваши контроллеры, модели и представления являются частью уровня презентаций, который является 1-м уровнем физической архитектуры. MVC реализует шаблон Model-View-Controller, где вы можете увидеть дополнительные слои. Тем не менее, постарайтесь не думать об этих трех аспектах как об уровнях или слоях. Попытайтесь думать обо всех трех из них как о проблемах уровня представления.
Обновление после комментария pres / bus / data
Итак, вы используете слой и слой взаимозаменяемо. Я обычно использую термин «слой» для логических / проектных / сборочных подразделений и уровень для физического разделения сети. Извините за путаницу.
В лагере MVC вы найдете довольно много людей, которые скажут, что вы не должны использовать «Модели» в MVC для вашей модели данных сущности, а также не должны использовать свои Контроллеры для бизнес-логики. В идеале ваши модели должны быть ViewModels для конкретного вида. Используя что-то вроде Automapper, вы берете свои сущности из модели вашего домена и помещаете их в ViewModels, специально разработанные для использования представлением.
Любые бизнес-правила также должны быть частью вашего домена, и вы можете реализовать их, используя доменные службы / шаблон фабрики / все, что подходит на вашем доменном уровне, а не на уровне представления MVC. Контроллеры должны быть глупыми, хотя и не такими глупыми, как модели, и должны нести ответственность перед доменом за все, что требует бизнес-знаний. Контроллеры управляют потоком HTTP-запросов и ответов, но все, что имеет реальную ценность для бизнеса, должно быть выше уровня оплаты контроллера.
Таким образом, вы все еще можете иметь многоуровневую архитектуру с MVC в качестве уровня представления. Это клиент вашего прикладного уровня, сервисного уровня или доменного уровня, в зависимости от того, как вы его разработали. Но в конечном итоге ваша модель сущности должна быть частью домена, а не модели в MVC.