本文转自:https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/creating-an-odata-endpoint

+

+

+

+

Software versions used in the tutorial

+

+

+

+

+

Create the Visual Studio Project

+

+

+

+

+

+

+

Add an Entity Model

+

+

+

+

+

Note

+

+

C#
public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

+

+

+

Add an OData Controller

+

+

+

+

+

+

+

+

+

+

+

+

+

+

  • Products.cs defines the Web API controller that implements the OData endpoint.
  • ProductServiceContext.cs provides methods to query the underlying database, using Entity Framework.

+

+

+

Add the EDM and Route

+

C#
using ProductService.Models;
using System.Web.Http;
using System.Web.Http.OData.Builder;

namespace ProductService
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Product>("Products");
            config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
        }
    }
}

+

  • Creates an Entity Data Model (EDM) for the OData endpoint.
  • Adds a route for the endpoint.

+

+

+

C#
modelBuilder.EntitySet<Product>("Products");

+

+

C#
config.Routes.MapODataRoute("ODataRoute", "odata", model);

+

+

Seed the Database (Optional)

+

+

console
Enable-Migrations

+

+

+

C#
protected override void Seed(ProductService.Models.ProductServiceContext context)
{
    // New code 
    context.Products.AddOrUpdate(new Product[] {
        new Product() { ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
        new Product() { ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
        new Product() { ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
        new Product() { ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
        new Product() { ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
    });
}

+

console
Add-Migration Initial
Update-Database

+

+

Exploring the OData Endpoint

+

+

+

Service Document

+

+

+

+

+

+

+

+

console
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/atomsvc+xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 17:51:01 GMT
Content-Length: 364

<?xml version="1.0" encoding="utf-8"?>
<service xml:base="http://localhost:60868/odata" 
    xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
  <workspace>
    <atom:title type="text">Default</atom:title>
    <collection href="Products">
        <atom:title type="text">Products</atom:title>
    </collection>
    </workspace>
</service></pre>

+

+

+

+

console
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 22:59:28 GMT
Content-Length: 136

{
  "odata.metadata":"http://localhost:60868/odata/$metadata","value":[
    {
      "name":"Products","url":"Products"
    }
  ]
}

Service Metadata Document

+

+

console
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:05:52 GMT
Content-Length: 1086

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
  <edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0" 
    xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <Schema Namespace="ProductService.Models" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
      <EntityType Name="Product">
        <Key>
          <PropertyRef Name="ID" />
        </Key>
        <Property Name="ID" Type="Edm.Int32" Nullable="false" />
        <Property Name="Name" Type="Edm.String" />
        <Property Name="Price" Type="Edm.Decimal" Nullable="false" />
        <Property Name="Category" Type="Edm.String" />
      </EntityType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
      <EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
        <EntitySet Name="Products" EntityType="ProductService.Models.Product" />
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

Entity Set

+

console
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:01:31 GMT
Content-Length: 459

{
  "odata.metadata":"http://localhost:60868/odata/$metadata#Products","value":[
    {
      "ID":1,"Name":"Hat","Price":"15.00","Category":"Apparel"
    },{
      "ID":2,"Name":"Socks","Price":"5.00","Category":"Apparel"
    },{
      "ID":3,"Name":"Scarf","Price":"12.00","Category":"Apparel"
    },{
      "ID":4,"Name":"Yo-yo","Price":"4.95","Category":"Toys"
    },{
      "ID":5,"Name":"Puzzle","Price":"8.00","Category":"Toys"
    }
  ]
}

Entity

+

console
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:04:29 GMT
Content-Length: 140

{
  "odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element","ID":1,
      "Name":"Hat","Price":"15.00","Category":"Apparel"
}

+

OData Serialization Formats

+

  • Atom Pub (XML)
  • JSON "light" (introduced in OData v3)
  • JSON "verbose" (OData v2)

+

+

+

console
<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://localhost:60868/odata" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
  <id>http://localhost:60868/odata/Products(1)</id>
  <category term="ProductService.Models.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
  <link rel="edit" href="http://localhost:60868/odata/Products(1)" />
  <link rel="self" href="http://localhost:60868/odata/Products(1)" />
  <title />
  <updated>2013-09-23T23:42:11Z</updated>
  <author>
    <name />
  </author>
  <content type="application/xml">
    <m:properties>
      <d:ID m:type="Edm.Int32">1</d:ID>
      <d:Name>Hat</d:Name>
      <d:Price m:type="Edm.Decimal">15.00</d:Price>
      <d:Category>Apparel</d:Category>
    </m:properties>
  </content>
</entry>

+

+

console
{
  "odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element",
  "ID":1,
  "Name":"Hat",
  "Price":"15.00",
  "Category":"Apparel"
}

+

console
{
  "d":{
    "__metadata":{
      "id":"http://localhost:18285/odata/Products(1)",
      "uri":"http://localhost:18285/odata/Products(1)",
      "type":"ProductService.Models.Product"
    },"ID":1,"Name":"Hat","Price":"15.00","Category":"Apparel"
  }
}

+

Next Steps

 

 

 

相应源码:ProductsController.cs

https://code.msdn.microsoft.com/ASPNET-Web-API-OData-cecdb524/sourcecode?fileId=109458&pathId=1986186869

using Microsoft.Data.OData;
using Microsoft.Data.OData.Query;
using ProductService.Models;
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.OData;
using System.Web.Http.OData.Routing;
using System.Web.Http.Routing;

namespace ProductService.Controllers
{
    public class ProductsController : ODataController
    {
        private ProductServiceContext db = new ProductServiceContext();

        // GET odata/Products
        [Queryable]
        public IQueryable<Product> GetProducts()
        {
            return db.Products;
        }

        // GET odata/Products(5)
        [Queryable]
        public SingleResult<Product> GetProduct([FromODataUri] int key)
        {
            return SingleResult.Create(db.Products.Where(product => product.ID == key));
        }

        // PUT odata/Products(5)
        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 = EntityState.Modified;

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(product);
        }

        // POST odata/Products
        public async Task<IHttpActionResult> Post(Product product)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Products.Add(product);
            await db.SaveChangesAsync();

            return Created(product);
        }

        // PATCH odata/Products(5)
        [AcceptVerbs("PATCH", "MERGE")]
        public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> patch)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            patch.Patch(product);

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(product);
        }

        // DELETE odata/Products(5)
        public async Task<IHttpActionResult> Delete([FromODataUri] int key)
        {
            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            db.Products.Remove(product);
            await db.SaveChangesAsync();

            return StatusCode(HttpStatusCode.NoContent);
        }


        // GET /Products(1)/Supplier
        public Supplier GetSupplier([FromODataUri] int key)
        {
            Product product = db.Products.FirstOrDefault(p => p.ID == key);
            if (product == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return product.Supplier;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool ProductExists(int key)
        {
            return db.Products.Count(e => e.ID == key) > 0;
        }

        // The next methods support entity relations.
        // For more information, see http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations

        public async Task<IHttpActionResult> CreateLink([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            switch (navigationProperty)
            {
                case "Supplier":
                    string supplierKey = GetKeyFromLinkUri<string>(link);
                    Supplier supplier = await db.Suppliers.FindAsync(supplierKey);
                    if (supplier == null)
                    {
                        return NotFound();
                    }
                    product.Supplier = supplier;
                    await db.SaveChangesAsync();
                    return StatusCode(HttpStatusCode.NoContent);

                default:
                    return NotFound();
            }
        }

        public async Task<IHttpActionResult> DeleteLink([FromODataUri] int key, string navigationProperty)
        {
            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            switch (navigationProperty)
            {
                case "Supplier":
                    product.Supplier = null;
                    await db.SaveChangesAsync();
                    return StatusCode(HttpStatusCode.NoContent);

                default:
                    return NotFound();

            }
        }

        // Helper method to extract the key from an OData link URI.
        private TKey GetKeyFromLinkUri<TKey>(Uri link)
        {
            TKey key = default(TKey);

            // Get the route that was used for this request.
            IHttpRoute route = Request.GetRouteData().Route;

            // Create an equivalent self-hosted route. 
            IHttpRoute newRoute = new HttpRoute(route.RouteTemplate,
                new HttpRouteValueDictionary(route.Defaults),
                new HttpRouteValueDictionary(route.Constraints),
                new HttpRouteValueDictionary(route.DataTokens), route.Handler);

            // Create a fake GET request for the link URI.
            var tmpRequest = new HttpRequestMessage(HttpMethod.Get, link);

            // Send this request through the routing process.
            var routeData = newRoute.GetRouteData(
                Request.GetConfiguration().VirtualPathRoot, tmpRequest);

            // If the GET request matches the route, use the path segments to find the key.
            if (routeData != null)
            {
                ODataPath path = tmpRequest.GetODataPath();
                var segment = path.Segments.OfType<KeyValuePathSegment>().FirstOrDefault();
                if (segment != null)
                {
                    // Convert the segment into the key type.
                    key = (TKey)ODataUriUtils.ConvertFromUriLiteral(
                        segment.Value, ODataVersion.V3);
                }
            }
            return key;
        }

        // The next method implements an OData action.
        // For more information, see http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-actions

        // POST /odata/Products(5)/RateProduct
        [HttpPost]
        public async Task<IHttpActionResult> RateProduct([FromODataUri] int key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            int rating = (int)parameters["Rating"];

            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            product.Ratings.Add(new ProductRating() { Rating = rating });
            db.SaveChanges();

            double average = product.Ratings.Average(x => x.Rating);

            return Ok(average);
        }


    }
}

 

相关文章: