рд╣рд╛рд▓ рд╣реА рдореЗрдВ рдЬрд╛рд░реА ASP.NET рдФрд░
Web Tools 2012.2 рдЕрджреНрдпрддрди рдХрд╛ рджрд╛рд╡рд╛ рд╣реИ рдХрд┐ ASP.NET рд╡реЗрдм API рдореЗрдВ oData рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рд▓рд┐рдП рдЖрдВрд╢рд┐рдХ рд╕рдорд░реНрдерди рд╣реИред рдореИрдВрдиреЗ рдЗрд╕реЗ рд╕реНрд╡рдпрдВ рдЖрдЬрдорд╛рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рдФрд░ рдЗрд╕реЗ рдЕрдкрдиреЗ рд╕рд╣рдпреЛрдЧрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рд╕рд╛рдЭрд╛ рдХрд┐рдпрд╛ред рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдХрдИ рд╕рдВрдмрдВрдзрд┐рдд рдбреЗрдЯрд╛ рдореЙрдбрд▓ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХреЗ рд╕рд╛рде рдУрдбреЗрдЯрд╛ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреНрд░рд╢реНрдиреЛрдВ рдФрд░ рд╕реАрдЖрд░рдпреВрдбреА рд╕рдВрдЪрд╛рд▓рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рддрд░реАрдХрд╛ рдмрддрд╛рдКрдВрдЧрд╛ред рдлреНрд░рдВрдЯ-рдПрдВрдб рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рд░реВрдк рдореЗрдВ, рдХреЗрдВрдбреЛ рдпреВрдЖрдИ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдкреНрд░рд╕реНрддрд╛рд╡рдирд╛ред
рдирдИ рддрдХрдиреАрдХ рдХреЗ рдЕрдзреНрдпрдпрди рдХреЗ рд▓рд┐рдП рдХрд╡рд░ рд╕рд╛рдордЧреНрд░реА рдХреЛ рдордЬрдмреВрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд▓реЗрдЦ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рдерд╛ред .NET MVC рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рдкрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдореЗрдВ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЕрдиреБрднрд╡ рдХреА рдкреВрд░реА рдХрдореА рдХреЗ рдХрд╛рд░рдг, рдореИрдВ рд╕рдВрднрд╛рд╡рд┐рдд рдЦрд╛рдорд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдкрд╣рд▓реЗ рд╕реЗ рдорд╛рдлреА рдорд╛рдВрдЧрддрд╛ рд╣реВрдВред рдЗрд╕рд▓рд┐рдП ...
рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реНрдерд╛рдкрдирд╛ред
ASP.NET рдФрд░ рд╡реЗрдм рдЯреВрд▓ рдХреЛ рдбрд╛рдЙрдирд▓реЛрдб рдФрд░ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░реЗрдВред
реирежрезреи рдЕрджреНрдпрддрди (рдпрджрд┐ рдЖрдк рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдмрд╕ рдмрдирд╛рдИ рдЧрдИ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ
Microsoft ASP.NET рд╡реЗрдм рдПрдкреАрдЖрдИ OData рдкреИрдХреЗрдЬ рд╕реНрдерд╛рдкрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ)ред рдПрдХ рдирдпрд╛
ASP.NET MVC 4 рд╡реЗрдм рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдПрдВ рдФрд░ рдЗрд╕реЗ
"ODataSample" рдирд╛рдо рджреЗрдВред
рдирдП ASP.NET MVC 4 рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕рдВрд╡рд╛рдж рдореЗрдВ
, рд╡реЗрдм рдПрдкреАрдЖрдИ рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрд╛ рдЪрдпрди рдХрд░реЗрдВред
KendoUIWeb рдкреИрдХреЗрдЬ
рдХреЛ рддреБрд░рдВрдд
рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ рдФрд░ рдЖрд╡рд╢реНрдпрдХ рд╕реЗрдЯрд┐рдВрдЧ рдХрд░реЗрдВ: рдЖрдкрдХреЛ
_Layout.cshtml рдлрд╝рд╛рдЗрд▓ рдореЗрдВ kendo.common.min.css, kendo.default.min.css, kendo.web.min.js рд▓рд┐рдВрдХ рд╢рд╛рдорд┐рд▓ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ
редрдореЙрдбрд▓ред
рд╕рдореНрдореЗрд▓рдиреЛрдВ рдХреЗ рдЕрдиреБрд╕рд╛рд░,
рдореЙрдбрд▓ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ
рд╢реНрд░реЗрдгреА рд╡рд░реНрдЧ рдмрдирд╛рдПрдВ рдФрд░ рдлрд╝реАрд▓реНрдб рдЬреЛрдбрд╝реЗрдВ:
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; } }
рдФрд░ рд╣рдо рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдПрдВрдЯрд┐рдЯреА рдлреНрд░реЗрдорд╡рд░реНрдХ рдорд╛рдЗрдЧреНрд░реЗрд╢рди рдХреА рдмрд╣реБрдд рдЙрдкрдпреЛрдЧреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ - рд╣рдо рдЗрд╕реЗ рдкреИрдХреЗрдЬ рдореИрдиреЗрдЬрд░ рдХрдВрд╕реЛрд▓ рдореЗрдВ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддреЗ рд╣реИрдВ:
- рдкрд▓рд╛рдпрди рд╕рдХреНрд╖рдо рдХрд░реЗрдВ
- рдРрдб-рдорд╛рдЗрдЧреНрд░реЗрд╢рди рдЗрдирд┐рдЯ
- рдбреЗрдЯрд╛рдмреЗрд╕ рдЕрджреНрдпрддрди рдХрд░реЗрдВ
рдирддреАрдЬрддрди, рд╣рдореЗрдВ рдЖрд╡рд╢реНрдпрдХ рдлрд╝реАрд▓реНрдб рдХреЗ рд╕рд╛рде рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдПрдХ рддрд╛рд▓рд┐рдХрд╛ рдорд┐рд▓рддреА рд╣реИред рдмреЗрд╢рдХ, рдкреНрд░рд╡рд╛рд╕рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдирд╛ рд╕рдВрднрд╡ рдерд╛, рд▓реЗрдХрд┐рди рдпрд╣ рдмрд╣реБрдд рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИред рдореИрдВ рдпрд╣ рднреА рдирд╣реАрдВ рд╕реЛрдЪ рд╕рдХрддрд╛ рдХрд┐ рдЖрдк рдХреИрд╕реЗ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдореИрдВ рдХрд┐рд╕реА рдЪреАрдЬ рд╕реЗ рд╡рд┐рдЪрд▓рд┐рдд рд╣реВрдВред рдЕрдм рд╣рдо рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╕реЗ рдЧреБрдЬрд░рддреЗ рд╣реИрдВред
рдУрдбрддрд╛ рдирд┐рдпрдВрддреНрд░рдХред
рд╣рдордиреЗ рдЕрднреА рддрдХ OData рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдЦрд╛рдХрд╛ рдирд╣реАрдВ рдмрдирд╛рдпрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП
"рдЦрд╛рд▓реА рдПрдкреАрдЖрдИ рдирд┐рдпрдВрддреНрд░рдХ" рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрд╛ рдЪрдпрди рдХрд░реЗрдВ рдФрд░ рдХреЛрдб рдХреЛ рдирд┐рдореНрди рдореЗрдВ рдмрджрд▓реЗрдВ:
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 рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд
рдореЗрдВ рдорд┐рд▓рд╛ рд╣реИ, рдЬреЛ рдмрджрд▓реЗ рдореЗрдВ
ApiController рд╕реЗ рд╣реИ ред рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╕рдм WebApi рдкрд░ рдПрдХ рдРрдб-рдСрди рд╣реИред рддреЛ рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдЖрдк рд╕реНрд╡рдпрдВ рдХрд┐рд╕реА рднреА рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЛ рд▓рд╛рдЧреВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЦрд╛рд╕рдХрд░ рдХреНрдпреЛрдВрдХрд┐ ASP.NET рдХреЛрдбрдкреНрд▓реЗрдХреНрд╕ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЗ рд▓рд┐рдП рд╕рднреА рд╕реНрд░реЛрдд рдХреЛрдб рд╣реИрдВред
EntitySetController рджреЛ рдореБрдЦреНрдп рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ: рдЗрдХрд╛рдИ рдкреНрд░рдХрд╛рд░ рдФрд░ рдЗрдХрд╛рдИ рдХреБрдВрдЬреА рдкреНрд░рдХрд╛рд░ред
рдФрд░ рдЕрдВрддрд┐рдо рд╕реНрдкрд░реНрд╢: рдЖрдкрдХреЛ
WebApiConfig рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдХреБрдЫ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:
public static class WebApiConfig { public static void Register(HttpConfiguration config) {
EnableQuerySupport рдкрджреНрдзрддрд┐ рдореЗрдВ
IQueryable рдкреНрд░рдХрд╛рд░ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рддрд░реАрдХреЛрдВ рдХреЗ рд▓рд┐рдП OData рдХреНрд╡реЗрд░реА рд╡рд┐рдХрд▓реНрдк рд╢рд╛рдорд┐рд▓ рд╣реИрдВред рдпрджрд┐ рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИ, рддреЛ рдЖрдк рдмрд╕
[рдХреНрд╡реЗрд░реА рдХрд░рдиреЗ рдпреЛрдЧреНрдп] рдЙрди рддрд░реАрдХреЛрдВ рдХреЛ рдЪрд┐рд╣реНрдирд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рдирдХреА рдЖрдкрдХреЛ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рд╡реИрд╕реЗ, рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреИрд░рд╛рдореАрдЯрд░ рд▓рд╛рдЧреВ рдХрд┐рдП рдЧрдП рд╣реИрдВ:
$ рдлрд╝рд┐рд▓реНрдЯрд░, $ рдЗрдирд▓рд╛рдЗрдирдХрд╛рдЙрдВрдЯ, $ рдСрд░реНрдбрд░рдмреА, $ рд╕реНрдХрд┐рдк, $ рдЯреЙрдк ; рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдмреБрдирд┐рдпрд╛рджреА рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб (рд╕рд░реНрд╡рд░-рд╕рд╛рдЗрдб рдкреЗрдЬрд┐рдВрдЧ) рдкрд░ рдкреГрд╖реНрдард╛рдВрдХрдиред рдмрд╛рдХреА рдХреЛрдб рдПрдХ Entity Data Model (EDM) рдмрдирд╛рддрд╛ рд╣реИред
EntitySet рдкрджреНрдзрддрд┐ рдИрдбреАрдПрдо рдореЗрдВ рд╕рдВрд╕реНрдерд╛рдУрдВ рдХрд╛ рдПрдХ рд╕реЗрдЯ рдЬреЛрдбрд╝рддреА рд╣реИред
MapODataRoute рд╡рд┐рдзрд┐ URI рд╕реЗрдЯ рдХрд░рддреА рд╣реИ рдФрд░ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреА рд╣реИред рдпрд╣ рд╕рдм рдЗрддрдирд╛ рд╣реИ рдХрд┐ рд╣рдо рд▓рд┐рдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
http: // localhost: 52864 / odata / рд╢реНрд░реЗрдгреА ред JSON рдкрд░рд┐рдгрд╛рдо:
{ "odata.metadata":"http://localhost:52864/odata/$metadata#Category","value":[ { "ID":1,"Name":"Categoty1" },{ "ID":2,"Name":"Category2" } ] }
рд▓рд┐рдВрдХ рдкрд░ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП
http: // localhost: 52864 / odata / рд╢реНрд░реЗрдгреА (1) , рдЬрд╣рд╛рдВ рдкреИрд░рд╛рдореАрдЯрд░ рдХреБрдВрдЬреА рд╣реИ, рдЖрдкрдХреЛ
GetEntityByKey рд╡рд┐рдзрд┐ рдХреЛ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП:
рд╣рдо рдХрд╛рд░реНрдп рдХреЛ рдЬрдЯрд┐рд▓ рдХрд░рддреЗ рд╣реИрдВред
рдЕрдм
рд╢реНрд░реЗрдгреА рд╕реЗ рдЬреБрдбрд╝реЗ
рдЙрддреНрдкрд╛рдж рдореЙрдбрд▓ рдмрдирд╛рдПрдВред
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) . тАУ . , , .