TerryR 내 친구, 너와 나는 술을 마셔야한다. 비슷한 문제가 있습니다.
1. 프로젝트 구조 : MVC 앱의 폴더 구조가 원하는 것으로 남겨둔다는 Eduardo의 의견에 동의합니다. 표준 컨트롤러, 모델 및보기 폴더가 있습니다. 그러나 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는 즉시 컨텐츠 및 스크립트를 제공합니다. 우리는 모든 정적 / 컴파일되지 않은 파일을 이러한 장소에 다시 유지하여 규칙을 따릅니다. 테마 (이미지 및 CSS)를 사용하는 js 라이브러리를 통합 할 때마다 테마 파일은 모두 / content 아래에 있습니다. 스크립트의 경우 모든 스크립트를 / scripts에 직접 넣습니다. 원래 이것은 VS에서 JS intellisense를 가져 오는 것이었지만 이제는 / scripts의 배치에 관계없이 R #에서 JS intellisense를 얻었으므로 그로부터 벗어날 수 있고 스크립트를 폴더별로 나누어 더 잘 정리 할 수 있다고 가정합니다. 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 Reporting Services를 고려 했습니까? 아니면 다른 DB에서 실행 중입니까? 정확히 당신이 요구하는 것이 확실하지 않습니다. EF + LINQ를 사용하지만 도메인 클래스에서 보고서를 생성하는 방법에 대한 특정 지식도 제공합니다. 따라서 컨트롤러 호출 저장소를 직접 사용하는 대신 컨트롤러 호출 도메인 서비스 호출 저장소가 있습니다. 임시 보고서의 경우 SQL Reporting Services를 사용하지만 완벽하지는 않지만 사용자는 데이터를 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" />
지금은 아약스 성공 함수를 무시하십시오 (나중에 자세히 설명). 일부 작업에 대해 단일 스크립트를 사용하여 벗어날 수 있습니다.
$('.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();
}
}
}
});
}
});
위의 코드는 확인을 처리하고 스피너를 표시하고 대기 메시지를 표시하며 아약스 호출이 완료된 후 스피너 / 대기 메시지를 숨 깁니다. 눈에 거슬리지 않는 라이브러리와 같은 data- * 속성을 사용하여 동작을 구성합니다.
일반적인 질문들
-클라이언트 MVC 대 서버 MVC? 컨트롤러가 JSON을 반환하는 것처럼 보이기 때문에 성공 함수에서 수행 한 작업을 유지하려고하지 않았습니다. 컨트롤러가 JSON을 반환하는 경우 KnockoutJS를 살펴볼 수 있습니다. 녹아웃 JS 버전 2.0이 오늘 출시되었습니다 . JSON에 바로 연결할 수 있으므로 관찰 가능한 클릭이 자동으로 데이터를 자바 스크립트 템플릿에 바인딩 할 수 있습니다. 반면에 아약스 액션 메서드가 JSON 대신 HTML을 반환하도록 신경 쓰지 않는다면 이미 구성된 UL을 LI 자식과 함께 반환 할 수 있으며 data-myapp-response =를 사용하여 요소에 추가 할 수 있습니다 "결과". 성공 함수는 다음과 같습니다.
success: function(html) {
var responseId = $(clicked).data('myapp-response');
if (responseId) {
$('#' + responseId).empty().html(html);
}
}
이에 대한 최선의 대답을 요약하면 액션 메서드에서 JSON을 반환 해야하는 경우 서버 측보기를 건너 뛰므로 실제로 서버 MVC가 아니며 MC 일뿐입니다. html과 함께 PartialViewResult를 아약스 호출에 반환하면 이것이 서버 MVC입니다. 따라서 앱이 ajax 호출에 JSON 데이터를 반환해야하는 경우 KnockoutJS와 같은 클라이언트 MVVM을 사용하십시오.
어느 쪽이든, 레이아웃 (html 태그)과 동작 (비동기 데이터로드)을 혼합하기 때문에 게시 한 JS가 마음에 들지 않습니다. 부분 html 뷰가있는 서버 MVC 또는 순수한 JSON 뷰 모델 데이터가있는 클라이언트 MVVM을 선택하면이 문제가 해결되지만 Javascript에서 DOM / HTML을 수동으로 구성하면 문제가 분리됩니다.
-Javascript 파일 작성 분명히 축소 기능이 .NET 4.5에 제공 될 것입니다 . 눈에 거슬리지 않는 경로로 가면 모든 JS를 1 스크립트 파일로로드하지 못하게하는 것이 없어야합니다. 각 엔티티 유형마다 다른 JS 파일을 작성하는 데주의를 기울이면 JS 파일 폭발이 발생합니다. 스크립트 파일이로드되면 브라우저는 향후 요청을 위해이를 캐시해야합니다.
-복잡한 쿼리 페이지 매김, 정렬 등과 같은 기능을 복잡한 것으로 생각하지 않습니다. DB 쿼리를 필요에 따라 제한적으로 만들기 위해 URL 및 서버 측 논리로 처리하는 것이 좋습니다. 그러나 Azure에 배포되었으므로 쿼리 최적화가 중요합니다. 예를 들면 다음과 같습니다 /widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}
.. EF 및 LINQ to Entities는 .Take (), .Skip (), .OrderBy () 및 .OrderByDescending ()과 같은 메소드를 사용하여 페이지 매김 및 정렬을 처리 할 수 있으므로 db 트립 중에 필요한 것을 얻을 수 있습니다. 아직 clientlib의 필요성을 찾지 못했기 때문에 솔직히 그들에 대해 많이 알지 못합니다. 그것에 대한 더 많은 조언을 얻으려면 다른 답변을 찾으십시오.
-프로젝트 비단 이것에 대해 들어 본 적이 없는지 확인해야합니다. 나는 Steve Sanderson, 그의 서적, BeginCollectionItem HtmlHelper 및 그의 블로그를 좋아합니다. 즉, 프로덕션에서 KnockoutJS 에 대한 경험이 없습니다 . 튜토리얼을 확인했지만 버전 2.0 이상이 될 때까지 무언가를 저 지르려고하지 않습니다. 앞서 언급했듯이 KnockoutJS 2.0이 출시되었습니다.
-N-tier 만약 당신이 다른 물리적 기계를 의미한다면, 아니, 나는 창문 밖으로 나가는 것이 없다고 생각합니다. 일반적으로 3 계층은 3 대의 컴퓨터가 있음을 의미합니다. 따라서 사용자의 컴퓨터에서 실행되는 프레젠테이션 계층으로 팻 클라이언트가있을 수 있습니다. 팻 클라이언트는 응용 프로그램 서버에서 실행되고 XML 또는 팻 클라이언트에 무엇이든 반환하는 서비스 계층에 액세스 할 수 있습니다. 그리고 서비스 계층은 세 번째 시스템의 SQL Server에서 데이터를 가져올 수 있습니다.
MVC는 1 계층의 한 계층입니다. 컨트롤러, 모델 및보기는 모두 프리젠 테이션 레이어의 일부이며 물리적 아키텍처에서 1 계층입니다. MVC는 Model-View-Controller 패턴을 구현하여 추가 레이어를 볼 수 있습니다. 그러나이 세 가지 측면을 계층 또는 계층으로 생각하지 마십시오. 이 3 가지를 모두 프레젠테이션 레이어 문제로 생각하십시오.
사전 / 버스 / 데이터 주석 후 업데이트
좋아, 그래서 당신은 계층과 계층을 상호 교환하여 사용하고 있습니다. 나는 보통 논리적 / 프로젝트 / 조립 부문에 "계층"이라는 용어를 사용하고 물리적 네트워크 분리에 계층을 사용합니다. 혼란을 드려 죄송합니다.
MVC 캠프에서 엔터티 데이터 모델에 MVC의 "모델"을 사용하지 말고 비즈니스 로직에 컨트롤러를 사용해서는 안된다고 말하는 사람들이 꽤 있습니다. 이상적으로 모델은 뷰 특정 ViewModel이어야합니다. Automapper와 같은 것을 사용하면 도메인 모델에서 엔티티를 DTO로 가져와 뷰에서 사용하도록 특별히 조각 된 ViewModels로 DTO합니다.
모든 비즈니스 규칙은 도메인의 일부 여야하며 도메인 서비스 / 팩토리 패턴 / MVC 프레젠테이션 계층이 아닌 도메인 계층에 적합한 모든 것을 사용하여 규칙을 구현할 수 있습니다. 컨트롤러는 모델만큼 멍청하지는 않지만 멍청해야하며 비즈니스 지식이 필요한 모든 것에 대해서는 도메인에 책임을 져야합니다. 컨트롤러는 HTTP 요청 및 응답의 흐름을 관리하지만 실제 비즈니스 가치가있는 것은 컨트롤러의 급여 등급보다 높아야합니다.
따라서 프리젠 테이션 레이어로 MVC를 사용하여 레이어 아키텍처를 계속 사용할 수 있습니다. 설계 방식에 따라 응용 프로그램 계층, 서비스 계층 또는 도메인 계층의 클라이언트입니다. 그러나 궁극적으로 엔티티 모델은 MVC의 모델이 아닌 도메인의 일부 여야합니다.