最近、ASP.NET MVCに関する記事が頻繁にHabréに掲載され始めました。 ただし、この記事では、上記のフレームワークでのアプリケーションの構築に関するいくつかのメモをしたいと思います。NuGetパッケージの最小セット(それなしでは作業を開始するのは罪です)、ログ、標準メンバーシップ、プロファイルプロバイダーを使用する場合の落とし穴。 そして最後に、MVC 4のWeb APIが私たち全員が待ち望んでいた理由です。
Nugetパッケージ
したがって、ASP.NET MVCでWebアプリケーションの開発を開始できないパッケージがないと判断しましょう。 以下のリストには、ソリューションの作成時にデフォルトでインストールされる[パッケージ]が含まれていますが、それらは引き続き含まれます。
- Entity Framework 4.1(Code Firstとともに)-データアクセス
- jQuery(UI、検証)-[コメントなし]
- Microsoft Webヘルパー
- MvcScaffolding-コード生成
- Ninject(MVC3)-依存性注入
- NLog(構成、拡張、スキーマ)-ロギング
- PagedList(MVC3)-「ページング」のための非常に便利なパッケージ
- Lucene(SimpleLucene)-検索
- JSのリアクティブ拡張-クライアント
Entity Framework 4.1-質問は、なぜですか? さて、例で説明しましょう。 他の同様のORMフレームワークなど、十分な数のORMフレームワークがあります(NHibernateには1つの価値があります)。 ほんの数年前、軽量(比較的、合成テストで判断)LINQ to SQLを使用することから始めることをお勧めします。 しかし! Entity Framework 4.1のリリースとCode Firstの組み合わせは、すべての欠点を上回りました。アプリケーションデータレイヤーのプロトタイピングは喜びでした。 最初にデザイナーで作業する必要がある場合、DBMLファイルを扱う場合、ここではPOCOのみを使用します。 たとえば、ストアのデータモデル:
public class Product { public int ProductId { get; set; } public string Name { get; set; } public int CategoryId { get; set; } public virtual Category Category { get; set; } public int Price { get; set; } public DateTime CreationDate { get; set; } public string Description { get; set; } } public class Category { public int CategoryId { get; set; } public string Name { get; set; } public virtual ICollection<Product> Products { get; set; } } public class ProductsContext : DbContext { public DbSet<Category> Categories { get; set; } }
MvcScaffolding -CRUDパネルをすばやくスケッチする必要がありますか? 既にEFモデル、またはLINQ to SQLをお持ちですか? 次に、NuGetウィンドウに次のコマンドを入力し、コード生成を楽しんでください。
Scaffold Controller [ ] –Repository
–Repositoryフラグを使用すると、データレイヤーを操作するためのリポジトリを作成することもできます。
たとえば、上記のモデルを使用します。
入った後
Scaffold Controller Product –Repository
次のCRUDページと抽象リポジトリが生成されます。
public interface IProductRepository { IQueryable<Product> All { get; } IQueryable<Product> AllIncluding(params Expression<Func<Product, object>>[] includeProperties); Product Find(int id); void InsertOrUpdate(Product product); void Delete(int id); void Save(); }
また、その実装:
public class ProductRepository : IProductRepository { ProductsContext context = new ProductsContext(); public IQueryable<Product> All { get { return context.Products; } } public IQueryable<Product> AllIncluding(params Expression<Func<Product, object>>[] includeProperties) { IQueryable<Product> query = context.Products; foreach (var includeProperty in includeProperties) { query = query.Include(includeProperty); } return query; } public Product Find(int id) { return context.Products.Find(id); } public void InsertOrUpdate(Product product) { if (product.ProductId == default(int)) {
より詳細なレビューについては、クリエイター自身からの
一連の記事を読むことをお勧めします。
Ninject-私は個人的に抽象化なしで働く機会はありません。 ASP.NET MVCには、工場の機能を制御/拡張するための多くの機能があります。 したがって、特定のクラス実装で機能を結び付けるのは悪い形です。 なぜNinjectなのか? 答えは簡単です。軽量で、多くの拡張機能があり、積極的に開発されています。
それをインストールし、
MVC3を追加します。
その後、App_Startフォルダーが表示され、NinjectMVC3.csファイルが配置されます。
DIを実装するには、モジュールを作成します。
class RepoModule : NinjectModule { public override void Load() { Bind<ICategoryRepository>().To<CategoryRepository>(); Bind<IProductRepository>().To<ProductRepository>(); } }
CreateKernelメソッドのNinjectMVC3.csファイルで、次のように記述します。
var modules = new INinjectModule[] { new RepoModule() }; var kernel = new StandardKernel(modules); RegisterServices(kernel); return kernel;
次に、コントローラーを記述します。
public class ProductsController : Controller { private readonly IProductRepository productRepository; public ProductsController(IProductRepository productRepository) { this.productRepository = productRepository; } }
NLog-アプリケーションの動作方法、操作を実行する際の成功/失敗の見つけ方 最も簡単な解決策は、ロギングを使用することです。 バイクを書くのは意味がありません。 とりわけ、NLogとlog4netを区別できると思います。 後者はJava(log4j)を使用した直接ポートです。 しかし、その開発はまったく放棄されていないとしても、あまり活発ではありません。 それどころか、NLogは活発に開発されており、豊富な機能とシンプルなAPIを備えています。
ロガーをすばやく追加する方法:
public class ProductController : Controller { private static Logger log = LogManager.GetCurrentClassLogger(); public ActionResult DoStuff() {
PagedList-ページ
めくりアルゴリズムが必要ですか? はい、自分で座って思いつくことができます。 しかし、なぜですか?
この記事には、それを使った作業の詳細な説明があります。
Lucene.NET-データベース自体の検索を使用してまだ
消去していますか? 忘れて! 数分で、超高速検索ができます。
それとSimpleLuceneアドオンをインストールします。
まず、インデックスの作成作業を自動化します。
public class ProductIndexDefinition : IIndexDefinition<Product> { public Document Convert(Product entity) { var document = new Document(); document.Add(new Field("ProductId", entity.ProductId.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); document.Add(new Field("Name", entity.Name, Field.Store.YES, Field.Index.ANALYZED)); if (!string.IsNullOrEmpty(entity.Description)) { document.Add(new Field("Description", entity.Description, Field.Store.YES, Field.Index.ANALYZED)); } document.Add(new Field("CreationDate", DateTools.DateToString(entity.CreationDate, DateTools.Resolution.DAY), Field.Store.YES, Field.Index.NOT_ANALYZED)); if (entity.Price != null) { var priceField = new NumericField("Price", Field.Store.YES, true); priceField.SetIntValue(entity.Price); document.Add(priceField); } document.Add(new Field("CategoryId", entity.CategoryId.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); return document; } public Term GetIndex(Product entity) { return new Term("ProductId", entity.ProductId.ToString()); } }
Convertメソッドで確認できるように、Lucene DocumentでPOCOをシリアル化します。
コントローラーコード:
public ActionResult Create(Product product) { if (ModelState.IsValid) { product.CreationDate = DateTime.Now; productRepository.InsertOrUpdate(product); productRepository.Save();
結果を表示するには、ResultDefinitionを作成します。
public class ProductResultDefinition : IResultDefinition<Product> { public Product Convert(Document document) { var product = new Product(); product.ProductId = document.GetValue<int>("ProductId"); product.Name = document.GetValue("Name"); product.Price = document.GetValue<int>("Price"); product.CategoryId = document.GetValue<int>("CategoryId"); product.CreationDate = DateTools.StringToDate(document.GetValue("CreationDate")); product.Description = document.GetValue("Description"); return product; } }
これは、POCOが逆シリアル化する場所です。
最後に、リクエストを使用して作業を自動化します。
public class ProductQuery : QueryBase { public ProductQuery(Query query) : base(query) { } public ProductQuery() { } public ProductQuery WithKeywords(string keywords) { if (!string.IsNullOrEmpty(keywords)) { string[] fields = { "Name", "Description" }; var parser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_29, fields, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); Query multiQuery = parser.Parse(keywords); this.AddQuery(multiQuery); } return this; } } }
それでは、コントローラーに移りましょう。
public ActionResult Search(string searchText, bool? orderByDate) { string IndexPath = Server.MapPath("~/Index"); var indexSearcher = new DirectoryIndexSearcher(new DirectoryInfo(IndexPath), true); using (var searchService = new SearchService(indexSearcher)) { var query = new ProductQuery().WithKeywords(searchText); var result = searchService.SearchIndex<Product>(query.Query, new ProductResultDefinition()); if (orderByDate.HasValue) { return View(result.Results.OrderBy(x => x.CreationDate).ToList()) } return View(result.Results.ToList()); } }
JSのリアクティブエクステンション -クライアントの基礎である必要があります。 いいえ、正直に言って、単体テストの可能性を備えた、クライアント上でのアプリケーションフレームワークのよりスムーズな作成を引き続き
求める必要があります。 Rx開発に関する
私の記事を読むことをお勧めします。
認証と承認
すぐに警告します-標準のAspNetMembershipProviderを使用しないでください! 箱から出して彼の巨大なストアドプロシージャを見ると、ただ彼を捨てたいだけです。
C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \フォルダーのInstallMembership.sqlおよびInstallProfile.SQLファイルを開きます。
たとえば、InstallMembership.sqlのFindUsersByNameのSQLコードは次のようになります。
CREATE PROCEDURE dbo.aspnet_Membership_FindUsersByName @ApplicationName nvarchar(256), @UserNameToMatch nvarchar(256), @PageIndex int, @PageSize int AS BEGIN DECLARE @ApplicationId uniqueidentifier SELECT @ApplicationId = NULL SELECT @ApplicationId = ApplicationId FROM dbo.aspnet_Applications WHERE LOWER(@ApplicationName) = LoweredApplicationName IF (@ApplicationId IS NULL) RETURN 0
そして、InstallProfile.SQLのProfile_GetProfilesは次のとおりです。
CREATE PROCEDURE dbo.aspnet_Profile_GetProfiles @ApplicationName nvarchar(256), @ProfileAuthOptions int, @PageIndex int, @PageSize int, @UserNameToMatch nvarchar(256) = NULL, @InactiveSinceDate datetime = NULL AS BEGIN DECLARE @ApplicationId uniqueidentifier SELECT @ApplicationId = NULL SELECT @ApplicationId = ApplicationId FROM aspnet_Applications WHERE LOWER(@ApplicationName) = LoweredApplicationName IF (@ApplicationId IS NULL) RETURN
ご覧のとおり、一時テーブルは常に作成されており、ハードウェアが無効になります。 1秒あたり100のそのような呼び出しがあると想像してください。
したがって、常に独自のプロバイダーを作成してください。
ASP.NET MVC4 Web API
ASP.NET MVCは、RESTfulアプリケーションを作成するための優れたフレームワークです。 APIを提供するには、たとえば、次のコードを記述できます。
public class AjaxProductsController : Controller { private readonly IProductRepository productRepository; public AjaxProductsController(IProductRepository productRepository) { this.productRepository = productRepository; } public ActionResult Details(int id) { return Json(productRepository.Find(id)); } public ActionResult List(int category) { var products = from p in productRepository.All where p.CategoryId == category select p; return Json(products.ToList()); } }
はい、1つの方法は、AJAXリクエストに対応するために別のコントローラーを書くことでした。
もう1つはスパゲッティコードです。
public class ProductsController : Controller { private readonly IProductRepository productRepository; public ProductsController(IProductRepository productRepository) { this.productRepository = productRepository; } public ActionResult List(int category) { var products = from p in productRepository.All where p.CategoryId == category select p; if (Request.IsAjaxRequest()) { return Json(products.ToList()); } return View(products.ToList()); } }
また、CRUD操作を追加する必要がある場合は、次のようにします。
[HttpPost] public ActionResult Create(Product product) { if (ModelState.IsValid) { productRepository.InsertOrUpdate(product); productRepository.Save(); return RedirectToAction("Index"); } return View(); }
属性を見るとわかるように、コード内のAJAX検出は最もクリーンなコードではありません。 APIを書いていますよね?
MVC4のリリースは、Web APIの新しい機能をマークしました。 一見すると、これはMVCコントローラーとWCF Data Servicesの混合物です。
Web APIトピックに関するチュートリアルは提供しませんが、
ASP.NET MVCサイト自体には多数あります。
上記の書き直されたコードの例のみを示します。
最初に、ProductRepositoryからInsertOrUpdateメソッドを変更しましょう。
public Product InsertOrUpdate(Product product) { if (product.ProductId == default(int)) {
そして、コントローラー自体を作成します。
public class ProductsController : ApiController { public IEnumerable<Product> GetAllProducts(int category) { var products = from p in productRepository.All where p.CategoryId == category select p; return products.ToList(); }
それで、いくつかのポイント、何が変更され、どのように機能するか:
- コントローラーはApiControllerを継承するようになりました
- これ以上ActionResultなどはありません -きれいなコードのみ
- これ以上HttpPostなど 属性
- メソッド名は、getリクエストの場合はGet、postリクエストの場合はPOSTで始まる必要があります。
- Web APIのIndexメソッドの類似物はGetAll {0}-コントローラーの名前です
私は、Web APIがMVCとWCF Data Servicesの混合物であることをもう少し指摘しました。 しかし、これはどこで表現されていますか? 簡単です-新しいAPIはODataをサポートします! そして、同様の原理で機能します。
たとえば、並べ替えを示すには、メソッド自体にパラメーターを指定する必要がありました。
public ActionResult List(string sortOrder, int category) { var products = from p in productRepository.All where p.CategoryId == category select p; switch (sortOrder.ToLower()) { case "name": products = products.OrderBy(x => x.Name); break; case "desc": products = products.OrderBy(x => x.Description); break; } return Json(products.ToList()); }
次に、GetAllProductsメソッドを変更するだけです。
public IQueryable<Product> GetAllProducts(int category) { var products = from p in productRepository.All where p.CategoryId == category select p; return products; }
また、たとえばブラウザで次のように入力します:
http://localhost/api/products?category=1&$orderby=Name
したがって、注意散漫を取り除き、API自体の作成に集中できるようになりました。
ご清聴ありがとうございました!