私の友人、TerryR、あなたと私は飲み物を飲むべきです。同様の問題がいくつかあります。
1.プロジェクトの構造: MVCアプリのフォルダー構造には、何かが残されているというEduardoに同意します。標準のコントローラー、モデル、およびビューのフォルダーがあります。ただし、Viewsフォルダーは、コントローラーごとに異なるフォルダーと、共有フォルダーに分割されます。また、各Views / ControllerNameまたはViews / Sharedは、EditorTemplatesとDisplayTemplatesに分割できます。ただし、Modelsフォルダーの整理方法を決定できます(サブフォルダーと追加の名前空間宣言の有無にかかわらず実行できます)。
各エリアのコントローラー、モデル、ビューのフォルダー構造を複製するエリアを使用することは禁じられています。
/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インテリセンスを取得することでしたが、/ scriptsの配置に関係なくR#からJSインテリセンスを取得するようになったので、それから逸脱してスクリプトをフォルダーごとに分けて整理しやすくしたと思います。ReSharperを使用していますか?純金のIMOです。
リファクタリングに大いに役立つもう1つの小さな金は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サービスを検討しましたか?または、別のデータベースで実行していますか?申し訳ありませんが、正確に何を求めているのか明確ではありません。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" />
今のところ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アクションメソッドがJSONの代わりにHTMLを返すことを気にしない場合は、LIの子を持つ既に構築されたULを返すことができ、data-myapp-response =を使用して要素に追加できます。 "結果"。成功関数は次のようになります。
success: function(html) {
var responseId = $(clicked).data('myapp-response');
if (responseId) {
$('#' + responseId).empty().html(html);
}
}
これに対する私の最良の答えを要約すると、アクションメソッドからJSONを返す必要がある場合、サーバー側のビューをスキップするため、これはサーバーMVCではなく、MCにすぎません。htmlでPartialViewResultをajax呼び出しに返す場合、これはサーバーMVCです。そのため、アプリがajax呼び出しに対してJSONデータを返す必要がある場合は、KnockoutJSなどのクライアントMVVMを使用します。
いずれにせよ、レイアウト(htmlタグ)と動作(非同期データロード)が混在するため、投稿したJSは好きではありません。部分的なHTMLビューを持つサーバーMVCまたは純粋なJSONビューモデルデータを持つクライアントMVVMのいずれかを選択すると、この問題は解決しますが、JavaScriptでDOM / HTMLを手動で構築すると、懸念の分離に違反します。
-Javascriptファイルの作成明らかに、縮小機能は.NET 4.5で導入されています。邪魔にならないルートに行く場合、すべてのJS in 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トリップ中に必要なものを取得できます。私はまだclientlibの必要性を発見していないので、正直なところclientlibについてあまり知りません。その他のアドバイスについては、他の回答をご覧ください。
-プロジェクトシルクこれは聞いたことがないので、チェックアウトする必要があります。私はSteve Sanderson、彼の本、彼のBeginCollectionItem HtmlHelper、そして彼のブログの大ファンです。とはいえ、KnockoutJSを運用環境で使用した経験はありません。私はそのチュートリアルをチェックアウトしましたが、少なくともバージョン2.0になるまで何かにコミットしないようにしています。前述したように、KnockoutJS 2.0はリリースされたばかりです。
-N層層ごとに異なる物理マシンを意味する場合、いいえ、窓からは何も出ないと思います。通常、3層は3台のマシンがあることを意味します。したがって、プレゼンテーション層としてファットクライアントを使用し、ユーザーのマシンで実行することができます。ファットクライアントは、アプリケーションサーバーで実行され、XMLなどをファットクライアントに返すサービス層にアクセスする場合があります。また、サービス層は、3番目のマシンのSQLサーバーからデータを取得する場合があります。
MVCは1層の1層です。コントローラ、モデル、およびビューはすべて、物理アーキテクチャの1層であるプレゼンテーション層の一部です。MVCはModel-View-Controllerパターンを実装します。これは、追加のレイヤーが表示される場所です。ただし、これら3つの側面を階層またはレイヤーと考えないようにしてください。これら3つすべてをプレゼンテーション層の懸念事項と考えてみてください。
pres / bus / dataコメントの後に更新
さて、あなたはティアとレイヤーを交換可能に使用しています。私は通常、論理/プロジェクト/アセンブリ部門に「レイヤー」という用語を使用し、物理ネットワーク分離にティアを使用します。混乱させて申し訳ありません。
MVCキャンプには、エンティティデータモデルにMVCの「モデル」を使用するべきではなく、ビジネスロジックにコントローラーを使用するべきではないと言う人がかなりいます。理想的には、モデルはビュー固有のViewModelである必要があります。Automapperのようなものを使用して、ドメインモデルからエンティティを取得し、ビューで使用するために特別に作成されたViewModelにDTOします。
すべてのビジネスルールもドメインの一部である必要があり、MVCプレゼンテーションレイヤーではなく、ドメインレイヤーで適切なものを使用して、ドメインサービス/ファクトリパターンを使用して実装できます。コントローラーは、モデルほど馬鹿げているわけではありませんが、馬鹿げている必要があり、ビジネス知識を必要とするものについてはドメインに責任を与える必要があります。コントローラはHTTPリクエストとレスポンスのフローを管理しますが、実際のビジネス上の価値があるものはすべてコントローラの給与等級を超えている必要があります。
そのため、プレゼンテーションレイヤーとしてMVCを使用したレイヤーアーキテクチャを引き続き使用できます。設計方法に応じて、アプリケーション層、サービス層、またはドメイン層のクライアントです。ただし、最終的にはエンティティモデルはMVCのモデルではなく、ドメインの一部である必要があります。