.NET MVCのODataコントローラー

最近リリースされたASP.NETおよびWeb Tools 2012.2 Updateは、ASP.NET Web APIのoDataプロトコルの部分的なサポートを主張しています。 私は自分で試して、同僚と共有することにしました。 この記事では、oDataプロトコルといくつかの関連データモデルオブジェクトを使用して、クエリとCRUD操作を使用する方法を説明します。 フロントエンドクライアントとして、Kendo UIフレームワークが使用されます。



まえがき


この記事は、新技術の研究の対象となる資料を統合するために書かれました。 .NET MVCプラットフォームでアプリケーションを作成した実際の経験がまったくないため、欠陥の可能性について事前に謝罪します。 だから...



初期インストール。


ASP.NETおよびWeb Tools 2012.2 Updateをダウンロードしてインストールします (必要ない場合は、作成したプロジェクトにMicrosoft ASP.NET Web API ODataパッケージをインストールするだけです)。 新しいASP.NET MVC 4 Webアプリケーションプロジェクトを作成し、 「ODataSample」と呼びます。 [ 新しいASP.NET MVC 4プロジェクト ]ダイアログWeb APIテンプレートを選択します

すぐにKendoUIWebパッケージをインストールし 、必要な設定を行います。_Layout.cshtmlファイルに、kendo.common.min.css、kendo.default.min.css、kendo.web.min.jsへのリンクを含める必要があります



モデル。


規則に従って、 ModelsフォルダーにCategoryクラスを作成し、フィールドを追加します。

public class Category { public int ID { get; set; } [Required] [StringLength(50)] public string Name { get; set; } }
      
      





同じフォルダーで、 ODataSampleContextコンテキストクラスファイルを作成します。

  public class ODataSampleContext : DbContext { public ODataSampleContext() : base("name=ODataSampleContext") { } public DbSet<Category> Categories { get; set; } }
      
      





そして、Entity Framework Migrationの非常に便利な機能を間違いなく使用します。パッケージマネージャーコンソールで実行します。



その結果、必要なフィールドを持つデータベースのテーブルを取得します。 もちろん、移行を使用しないこともできましたが、非常に便利です。 どうすれば違う仕事ができるか想像もできません。 しかし、私は何かに気を取られています。 次に、最も興味深いものに移ります。



ODataコントローラー。


ODataコントローラーのテンプレートはまだ作成していないため、 「Empty API Controller」テンプレートを選択し、コードを次のように変更します。

 public class CategoryController : EntitySetController<Category, int> { private ODataSampleContext db = new ODataSampleContext(); public override IQueryable<Category> Get() { return db.Categories; ; } protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } }
      
      





一般に、 EntitySetControllerは ODataControllerから継承され、 ODataControllerはApiControllerから継承されます。 これらはすべてWebApiのアドオンであることがわかりました。 したがって、一般的には、特にASP.NET CodePlexプロジェクトのすべてのソースコードがあるため、自分でプロトコルを実装できます。 EntitySetControllerは、エンティティタイプとエンティティキータイプの2つの主なタイプを受け入れます。

最後の仕上げWebApiConfigファイルにいくつかの変更を加える必要があります。

 public static class WebApiConfig { public static void Register(HttpConfiguration config) { // ... config.EnableQuerySupport(); // ... ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); modelBuilder.EntitySet<Category>("Category"); Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel(); config.Routes.MapODataRoute("ODataRoute", "odata", model); } }
      
      





EnableQuerySupportメソッドには、 IQueryable型を返すメソッドのODataクエリオプションが含まれています。 これが不要な場合は、必要なメソッドを[Queryable]属性で簡単にマークできます 。 ところで、現在次のパラメーターが実装されています: $ filter、$ inlinecount、$ orderby、$ skip、$ top ; 十分ではありませんが、サーバー側のページネーション(サーバー側のページング)などの基本的な目的には十分です。 残りのコードは、Entity Data Model(EDM)を作成します。 EntitySetメソッドは、エンティティのセットをEDMに追加します。 MapODataRouteメソッドはURIを設定し、エンドポイントを構成します。 これにより、 http:// localhost:52864 / odata / Categoryというリンクを使用できます。 JSON結果:

 { "odata.metadata":"http://localhost:52864/odata/$metadata#Category","value":[ { "ID":1,"Name":"Categoty1" },{ "ID":2,"Name":"Category2" } ] }
      
      





リンクhttp:// localhost:52864 / odata / Category(1)で特定のエントリを取得するには、パラメーターがキーであるため、 GetEntityByKeyメソッドをオーバーライドする必要があります。

  //... protected override Category GetEntityByKey(int key) { return db.Categories.FirstOrDefault(c => c.ID == key); } //...
      
      







タスクが複雑になります。


次に、 Categoryに関連付けられたProductモデルを作成します。

 public class Product { public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } [Required] public int CategoryID { get; set; } public virtual Category Category { get; set; } }
      
      





コンテキストに行を追加することを忘れないでください
  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




  public DbSet Products { get; set; }    :  Add-Migration AddProduct, Update-Database .      . ,         Seed    Migrations/Configuration.cs   :  context.Products.AddOrUpdate(…) . 
      

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .




public DbSet Products { get; set; } : Add-Migration AddProduct, Update-Database . . , Seed Migrations/Configuration.cs : context.Products.AddOrUpdate(…) .

OData. , . OData . OData $expand , ; Data Tranfer Object (DTO):

public class ProductDTO { public ProductDTO() { } public ProductDTO(Product product) { ProductID = product.ProductID; Name = product.Name; Price = product.Price; CategoryID = product.CategoryID; CategoryName = product.Category.Name; } [Key] public int ProductID { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } public int CategoryID { get; set; } public string CategoryName { get; set; } public Product ToEntity() { return new Product { ProductID = ProductID, Name = Name, Price = Price, CategoryID = CategoryID }; } }








CRUD

:

public class ProductController : EntitySetController<ProductDTO, int> { ODataSampleContext _context = new ODataSampleContext(); public override IQueryable<ProductDTO> Get() { return _context.Products.Include(p => p.Category) .Select(product => new ProductDTO { ProductID = product.ProductID, Name = product.Name, Price = product.Price, CategoryID = product.CategoryID, CategoryName = product.Category.Name, }); } protected override ProductDTO GetEntityByKey(int key) { return new ProductDTO(_context.Products.FirstOrDefault(p => p.ProductID == key)); } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } }





– DTO. , "".

CRUD . :

protected override ProductDTO CreateEntity(ProductDTO entityDto) { if (ModelState.IsValid) { var entity = entityDto.ToEntity(); _context.Products.Add(entity); _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == entity.ProductID)); } else { HttpResponseMessage response = null; response = Request.CreateResponse(HttpStatusCode.BadRequest, new ODataError { ErrorCode = "ValidationError", Message = String.Join(";", ModelState.Values.First().Errors.Select(e => e.ErrorMessage).ToArray()) }); throw new HttpResponseException(response); } }





. HttpStatusCode.BadRequest ODataError .

, JSON. . HttpResponseException .

:

protected override ProductDTO UpdateEntity(int key, ProductDTO updateDto) { if (!_context.Products.Any(p => p.ProductID == key)) { throw new HttpResponseException(HttpStatusCode.NotFound); } var update = updateDto.ToEntity(); _context.Products.Attach(update); _context.Entry(update).State = System.Data.EntityState.Modified; _context.SaveChanges(); return new ProductDTO(_context.Products.Include(p => p.Category).FirstOrDefault(p => p.ProductID == key)); } protected override ProductDTO PatchEntity(int key, Delta<ProductDTO> patch) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ProductDTO ProductDTO = new ProductDTO(product); patch.Patch(ProductDTO); _context.Products.Attach(ProductDTO.ToEntity()); _context.SaveChanges(); return new ProductDTO(product); } public override void Delete([FromODataUri] int key) { Product product = _context.Products.FirstOrDefault(p => p.ProductID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } _context.Products.Remove(product); _context.SaveChanges(); } protected override int GetKey(ProductDTO entity) { return entity.ProductID; }





, . PatchEntity, Delta, , .



– KendoUI Grid

HomeController :

public ActionResult Products() { return View(); }





View Products.cshtml View/Home , div Grid :

@{ ViewBag.Title = "Product"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="body"> <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Product</h1> </hgroup> <div id="Grid" style="height: 380px"></div> </div> </section> </div>





JavaScript:

Script <script> $(document).ready(function () { $("#Grid").kendoGrid({ columns: [ { field: "Name", title: "Product Name", width: "100px" }, { field: "CategoryName", title: "Category", width: "150px", editor: categoryDropDownEditor, template: "#=CategoryName#" }, { field: "Price", title: "Price", width: "100px" }, { command: "edit", title: "Edit", width: "110px" }, { command: "destroy", title: "Delete", width: "110px" }, ], pageable: true, pageSize: 5, sortable: true, filterable: true, editable: "popup", toolbar: ["create"], dataSource: { serverPaging: true, serverFiltering: true, serverSorting: true, pageSize: 5, type: "odata", schema: { data: function (response) { if (response.value !== undefined) return response.value; else{ delete response["odata.metadata"]; return response; } }, total: function (response) { return response['odata.count']; }, model: { id: "ProductID", fields: { ProductID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, Price: { nullable: false, type: "number" }, CategoryID: { type: "number",validation: { required: true }, editable: true}, CategoryName: { validation: { required: true }, editable: true }, } }, }, batch: false, error: error, transport: { create: { url: "/odata/Products", contentType: "application/json", type: "POST", }, read: { url: "/odata/Products", dataType: "json", contentType: "application/json", }, update: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, dataType: "json", contentType: "application/json", type: "PUT", headers: { Prefer: "return-content" } }, destroy: { url: function (record) { return "/odata/Products" + "(" + record.ProductID + ")"; }, contentType: "application/json", type: "DELETE" }, parametermap: function (data, operation) { console.log(data); if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } } } }); }); function categoryDropDownEditor(container, options) { $('<input data-bind="value:CategoryID"/>') .appendTo(container) .kendoDropDownList({ dataTextField: "Name", dataValueField: "ID", optionLabel: "--Select Value--", dataSource: { schema: { data: "value", total: function (response) { return response['odata.count']; }, model: { id: "ID", fields: { ID: { editable: false, type: "number" }, Name: { type: "string", nullable: false }, } }, }, type: "odata", serverFiltering: true, serverPaging: true, pageSize: 20, transport: { read: { url: "/odata/Categories", dataType: "json", contentType: "application/json" } }, parametermap: function (data, operation) { if (operation === "read") { var parammap = kendo.data.transports.odata.parametermap(data); return parammap; } return json.stringify(data); } }, }); } </script>







Kendo , . dataSource . schema ( data ), ( total ), . « total » , GET /odata/Products?%24inlinecount=allpages&%24top=5 5 . transport CRUD . headers: { Prefer: "return-content" }



update , , . , . " CategoryName " editor: categoryDropDownEditor



, .

:

script <script> function error(e) { if (e.errorThrown === "Bad Request") { var response = JSON.parse(e.xhr.responseText); console.log(response); if (response['odata.error'] != undefined) { alert(response['odata.error'].message.value) } } else { alert(e.status + ": " + e.errorThrown) } }; </script>







, ODataError , :

{ "odata.error":{ "code":"ValidationError","message":{ "lang":"en-US","value":" Name." } } }





.





画像

REST CRUD , , , , , , . .net MVC, .

画像

, :









PS . SPA (Single page application) . – . , , .







All Articles