ASP. NET Web API adds, deletes, modifies, and queries based on OData, and processes relations between entities. apiodata

Source: Internet
Author: User
Tags key string

ASP. NET Web API adds, deletes, modifies, and queries based on OData, and processes relations between entities. apiodata

 

This article describes how to add, delete, modify, and query ASP. NET Web APIs Based on OData and process the relationships between entities.

 

The first is a typical one-to-many relationship, Supplier and Product.

 

public class Product{    public int Id { get; set; }    public string Name { get; set; }    public decimal Price { get; set; }    public string Category { get; set; }    [ForeignKey("Supplier")]    public int? SupplierId { get; set; }    public virtual Supplier Supplier { get; set; }}public class Supplier{    public int Id { get; set; }    public string Name { get; set; }    public ICollection<Product> Products { get; set; }}    

 


Product has a foreign key SupplierId for Supplier, which can be null.


The configuration section of Entity Framework is omitted.

 

The configuration of OData in WebApiConfig is as follows:

 

Public static class WebApiConfig {public static void Register (HttpConfiguration config) {// Web API configuration and service // Web API route config. mapHttpAttributeRoutes (); config. routes. mapHttpRoute (name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new {id = RouteParameter. optional}); // For OData // use ODataConventionModelBuilder to create EDM using some conventions // if you want more control over the creation of EDM, use ODataModelBuilder builder = new ODataConventionModelBuilder (); builder. entitySet <Product> ("Products"); // create EntityDataModel (EDM) builder. entitySet <Supplier> ("Suppliers"); config. mapODataServiceRoute (routeName: "ODataRoute", routePrefix: "odata", model: builder. getEdmModel ());}}

 

About ProductsController

 

 

public class ProductsController : ODataController{    ProductsContext db = new ProductsContext();        private bool ProductExists(int key)    {        return db.Products.Any(p => p.Id == key);    }    protected override void Dispose(bool disposing)    {        db.Dispose();        base.Dispose(disposing);    }    ...}

 

 

All OData-related objects must inherit the base class ODataController.

 

● Retrieve all

 

 

[EnableQuery]public IQueryable<Product> Get(){    return db.Products;}

 

After the [EnableQuery] feature is configured for an action, OData query is supported.

 

● Query by Product's primary key

 

[EnableQuery]public SingleResult<Product> Get([FromODataUri] int key){    IQueryable<Product> query = db.Products.Where(p => p.Id == key);    return SingleResult.Create(query);}

 

→ [FromODataUri] The key value in the int key can be obtained from the following uri:

 

GET http: // localhost: 63372/odata/Prodducts (11)

 

The above 11 values are assigned to the key.

 

→ SingleResult can accept 0 or 1 Entity.

 

● Retrieve the navigation attribute Supplier based on the primary key of the Product

 

// GET/Products (1)/Supplier // obtain the Supplier of Poduct navigation attribute Supplier // The Supplier in GetSupplier is the name of the navigation attribute, getSupplier and key write methods comply with the Conventions // [EnableQuery (AllowedQueryOptions = System. web. OData. query. allowedQueryOptions. all)] [EnableQuery] public SingleResult <Supplier> GetSupplier ([FromODataUri] int key) {var result = db. products. where (p => p. id = key ). select (m => m. supplier); return SingleResult. create (result );}

 

As mentioned above, the syntax of GetSupplier complies with the Convention. The navigation attribute names of Supplier and Product are consistent.

 

● Add Product

 

public async Task<IHttpActionResult> Post(Product product){    if(!ModelState.IsValid)    {        return BadRequest(ModelState);    }    db.Products.Add(product);    await db.SaveChangesAsync();    return Created(product);}

 

The above is verification first, then add, and finally put the newly added Product in the Create method and return it to the front-end.


● Partial Product updates

 

public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product){    if(!ModelState.IsValid)    {        return BadRequest(ModelState);    }    var entity = await db.Products.FindAsync(key);    if (entity == null)    {        return NotFound();    }    product.Patch(entity);    try    {        await db.SaveChangesAsync();    }    catch (DbUpdateConcurrencyException)    {        if(!ProductExists(key))        {            return NotFound();        }        else        {            throw;        }    }    return Updated(entity);}

 

Above, the Delta <Product> generic class can track changes in the Product, and finally notify the entity of the changes using its instance method Patch, after the Patch is successful, the Product is put in the Updated method and returned to the front-end.

 

● Update Product

 

public async Task<IHttpActionResult> Put([FromODataUri] int key, Product product){    if(!ModelState.IsValid)    {        return BadRequest(ModelState);    }    if(key != product.Id)    {        return BadRequest();    }    db.Entry(product).State = System.Data.Entity.EntityState.Modified;    try    {        await db.SaveChangesAsync();    }    catch (DbUpdateConcurrencyException)    {        if (!ProductExists(key))        {            return NotFound();        }        else        {            throw;        }    }    return Updated(product);}

 

Here, we first determine the ModelState of the object, and then determine whether the primary key of the Product from the front end is equal to the primary key of the Product from the front end, catch a DbUpdateConcurrencyException when processing the changes submitted by the Entity Framwork unit to prevent the Product from being deleted during update. Finally, the Product is put in the Updated method and returned to the front-end.

 

● Delete Product

 

public async Task<IHttpActionResult> Delete([FromODataUri] int key){    var product = await db.Products.FindAsync(key);    if(product==null)    {        return NotFound();    }    db.Products.Remove(product);    await db.SaveChangesAsync();    return StatusCode(HttpStatusCode.NoContent);}

 

● Create the entity relationship between Product and Supplier

 

/// <Summary> /// create the relationship between the Product and Supplier /// if it is a Product. supplier creates a link and uses PUT request // if it is Supplier. products create relationship, use POST request /// </summary> /// <param name = "key"> Primary key of Product </param> /// <param name = "navigationProperty"> Product </param> /// <param name = "link"> </param> /// <returns> </returns> [AcceptVerbs ("POST ", "PUT")] public async Task <IHttpActionResult> CreateRef ([FromODataUri] int key, string navigationProperty, [FromBody] Uri link) {// now ensure that the Product is an existing var product = db. products. singleOrDefault (p => p. id = key); if (product = null) return NotFound (); switch (navigationProperty) {case "Supplier": // obtain the Supplier's primary key var supplierId = Helpers. getKeyFromUri <int> (Request, link); var supplier = db. suppliers. singleOrDefault (s => s. id = supplierId); if (supplier = null) return NotFound (); product. supplier = supplier; break; default: return StatusCode (HttpStatusCode. notImplemented);} await db. saveChangesAsync (); return StatusCode (HttpStatusCode. noContent );}

 

If the Supplier relationship of the Product is created, the PUT request is used. If the Products relationship of the Supplier is created, the POST request is used.

 

The frontend sends a PUT request with the uri: http: // localhost: 54714/odata/Products (1)/Supplier/$ ref

 

This means you need to create a Supplier for the Product numbered 1.

 

Where does the Supplier need to be created from? It must be passed in from the front body in the following format:

 

{"@ Odata. id": "http: // localhost: 54714/odata/Suppliers (2 )"}

 

In the CreateRef method, the parameter key is used to receive the Product primary key 1, the parameter navigationProperty is used to receive the Supplier, and the parameter link is used to receive the complete uri of a specific Supplier from the body, that is, http: // localhost: 54714/odata/Suppliers (2 ).

 

$ Ref is placed after Products (1)/Supplier/, indicating that the relationship between the Product numbered 1 and a Supplier is processed.

 

The Helpers. GetKeyFromUri <int> method is used to retrieve the primary key 2 of a Supplier in http: // localhost: 54714/odata/Suppliers (2.


The Helpers. GetKeyFromUri <T> method is as follows:

 

// Split the uri into a segment, find the key value of the key, and convert it to a suitable type public static class Helpers {public static TKey GetKeyFromUri <TKey> (HttpRequestMessage request, Uri uri) {if (uri = null) {throw new ArgumentNullException ("uri");} var urlHelper = request. getUrlHelper ()?? New UrlHelper (request); string serviceRoot = urlHelper. createODataLink (request. ODataProperties (). routeName, request. ODataProperties (). pathHandler, new List <ODataPathSegment> (); var odataPath = request. ODataProperties (). pathHandler. parse (request. ODataProperties (). model, serviceRoot, uri. localPath); var keySegment = odataPath. segments. ofType <KeyValuePathSegment> (). firstOrDefault (); if (keySegment = null) {throw new InvalidOperationException ("The link does not contain a key. ");} var value = ODataUriUtils. convertFromUriLiteral (keySegment. value, Microsoft. OData. core. ODataVersion. v4); return (TKey) value ;}}

 

● Delete the entity relationship between Product and Supplier

 

/// <Summary> /// Delete the relationship between Product and Supplier /// </summary> /// <param name = "key"> Product primary key </param>/ // <param name = "navigationProperty"> navigation property of Product </param> // <param name = "link"> Suppliers (1) </param> /// <returns> </returns> [HttpDelete] public async Task <IHttpActionResult> DeleteRef ([FromODataUri] int key, string navigationProperty, [FromBody] Uri link) {var product = db. products. singleOrDefault (p => p. id = key); if (product = null) return NotFound (); switch (navigationProperty) {case "Supplier": product. supplier = null; break; default: return StatusCode (HttpStatusCode. notImplemented);} await db. saveChangesAsync (); return StatusCode (HttpStatusCode. noContent );}

 

The front end sends a DELETE request: http: // localhost: 54714/odata/Products (1)/Supplier/$ ref

 

In the DeleteRef method, the parameter key is used to receive the primary key 1 of the Product, and the parameter navigationProperty is used to receive the Supplier.

 

SuppliersController, similar to Product

 

Public class SuppliersController: ODataController {ProductsContext db = new ProductsContext (); [EnableQuery] public IQueryable <Product> GetProducts ([FromODataUri] int key) {return db. suppliers. where (m => m. id. equals (key )). selectiterator (m => m. products);} [EnableQuery] public IQueryable <Supplier> Get () {return db. suppliers;} [EnableQuery] public SingleResult <Supplier> Get ([FromODataUri] int key) {IQueryable <Supplier> result = db. suppliers. where (s => s. id = key); return SingleResult. create (result) ;}/// <summary> /// DELETE the relationship between a Supplier and a Product. // DELETE http://host/Suppliers (1)/Products/$ ref? $ Id = http://host/Products (1) /// </summary> /// <param name = "key"> Primary key of the Supplier </param> /// <param name = "relatedKey"> primary key string of the Product </param> /// <param name = "navigationProperty"> navigation attribute of Supplier </param> /// <returns> </returns> [HttpDelete] public async Task <IHttpActionResult> DeleteRef ([FromODataUri] int key, [FromODataUri] string relatedKey, string navigationProperty) {var supplier = db. suppliers. singleOrDefault (p => p. id = key); if (supplier = null) return NotFound (); switch (navigationProperty) {case "Products": var productId = Convert. toInt32 (relatedKey); var product = db. products. singleOrDefault (p => p. id = productId); if (product = null) return NotFound (); product. supplier = null; break; default: return StatusCode (HttpStatusCode. notImplemented);} await db. saveChangesAsync (); return StatusCode (HttpStatusCode. noContent);} protected override void Dispose (bool disposing) {db. dispose (); base. dispose (disposing );}}

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.