zoukankan      html  css  js  c++  java
  • [转]Web API OData V4 Keys, Composite Keys and Functions Part 11

    本文转自:https://damienbod.com/2014/09/12/web-api-odata-v4-keys-composite-keys-and-functions-part-11/

    Web API OData V4 Keys, Composite Keys and Functions Part 11

    This article demonstrates how OData Functions can be used together with entities which have simple keys, composite keys, entity collections or used as plain simple global functions.

    Part 1 Getting started with Web API and OData V4 Part 1.
    Part 2 Web API and OData V4 Queries, Functions and Attribute Routing Part 2
    Part 3 Web API and OData V4 CRUD and Actions Part 3
    Part 4 Web API OData V4 Using enum with Functions and Entities Part 4
    Part 5 Web API OData V4 Using Unity IoC, SQLite with EF6 and OData Model Aliasing Part 5
    Part 6 Web API OData V4 Using Contained Models Part 6
    Part 7 Web API OData V4 Using a Singleton Part 7
    Part 8 Web API OData V4 Using an OData T4 generated client Part 8
    Part 9 Web API OData V4 Caching Part 9
    Part 10 Web API OData V4 Batching Part 10
    Part 11 Web API OData V4 Keys, Composite Keys and Functions Part 11

    Code: https://github.com/damienbod/WebApiODataFunctionsAndKeys

    Basic Model and Entities

    To demonstrate the different functions, with the different model types, a simple OData model is used with 2 Entities, a Stadium entity and a City entity. A namespace ‘D’ is defined because functions require a namespace. If not defined, the ‘Default’ namespace is used. I would prefer to use no namespace in the URL but this is not possible.

    public static IEdmModel GetModel()
    {
     ODataModelBuilder builder = new ODataConventionModelBuilder();
     builder.Namespace = "D";
     builder.ContainerName = "Default";
     EntitySetConfiguration<City> cities = builder.EntitySet<City>("City");
     EntitySetConfiguration<Stadium> stadiums = builder.EntitySet<Stadium>("Stadium");
    

    The City entity has just a simple key property. This is the normal form.

    [DataContract(Name = "City")]
    public class City
    {
     [DataMember]
     [Key]
     public int Id { get; set; }
    
     [DataMember]
     public long Population { get; set; }
    
     [DataMember]
     public string Country { get; set; }
    }
    

    The Stadium entity has a composite key made up of 2 key properties.

    [DataContract(Name = "Stadium")]
    public class Stadium
    {
     [DataMember]
     [Key]
     public string Name { get; set; }
    
     [DataMember]
     [Key]
     public string Country { get; set; }
    
     [DataMember]
     public int Capacity { get; set; }
    
     [DataMember]
     public string Owner { get; set; }
    }
    

    Web Configuration for IIS

    The web.config needs some special configuration, if you want that the function URLs are reachable in the IIS. If not configured, you receive 404 for your URLs because the ‘.’ is not evaluated. The runAllManagedModulesForAllRequests is set to true.

    <system.webServer>
      <modules runAllManagedModulesForAllRequests="true"></modules>
        <handlers>
          <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
          <remove name="OPTIONSVerbHandler" />
          <remove name="TRACEVerbHandler" />
          <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
        </handlers>
    </system.webServer>
    

    OData Global Functions

    These functions are the most simple of all the OData functions to setup. The functions are global and not connected to any entity or collection of entities. The global functions can be added directly to the model builder.

    builder.Function("GlobalFunction")
       .ReturnsCollectionFromEntitySet<Stadium>("Stadium");
    

    The global functions can then be made public inside any ODataController, the route must match the OData routes. This can be checked using the ~/odata/$metadata URL.

    using System.Collections.Generic;
    using System.Web.Http;
    using System.Web.OData;
    using System.Web.OData.Query;
    using System.Web.OData.Routing;
    using WebAPIODataKeys.Models;
    
    namespace WebApiODataKeys.Controllers
    {
        public class GlobalOdataFunctionsActionsController : ODataController
        {
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[ODataRoute("GlobalFunction()")]
    		[HttpGet]
    		public IHttpActionResult GlobalFunction()
    		{
    			return Ok(new List<Stadium>{ new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "Global Owner" } });
    		}
        }
    }
    
    

    ~/odata/GlobalFunction()

    webApifunctionsKeys_01

    Collection Function which returns a Collection

    OData functions can also be added to collections of entities. The following model configuration adds a GetStadiumsWithFunction function to the Stadium entity collection and returns a collection of stadium entities.

    FunctionConfiguration getStadiumsWithFunction = stadiums.EntityType.Collection.Function("GetStadiumsWithFunction");
    getStadiumsWithFunction.ReturnsCollectionFromEntitySet<Stadium>("Stadium");
    

    The function is then made public in the Stadium ODataController. The URL contains the function namespace.

    [EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    [ODataRoute("D.GetStadiumsWithFunction()")]
    [HttpGet]
    public IHttpActionResult GetStadiumsWithFunction()
    {
       return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "FC Zug" } });
    }
    

    The function can then be used as follows:
    ~/odata/Stadium/D.GetStadiumsWithFunction()

    webApifunctionsKeys_02

    Collection Function which returns an Entity

    This is very similar to the previous functions except this function has 2 input parameters and returns a single Stadium entity.

    Function definition in the model:

    // http://localhost:60096/odata/Stadium/D.GetStadiumTest(test='ddd', land='ssss')
    FunctionConfiguration getStadiumsTest = stadiums.EntityType.Collection.Function("GetStadiumTest");
    getStadiumsTest.Parameter<string>("test");
    getStadiumsTest.Parameter<string>("name");
    getStadiumsTest.ReturnsFromEntitySet<Stadium>("Stadium");
    

    Function definition in the controller:

    // http://localhost:60096/odata/Stadium/D.GetStadiumTest(test='ddd', land='ssss')
    [ODataRoute("D.GetStadiumTest(test={test}, name={name})")]
    [EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    [HttpGet]
    public IHttpActionResult GetStadiumTest([FromODataUri] string test, [FromODataUri] string name)
    {
      return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = name, Name = test, Owner = "FC Zug" } });
    }
    

    ~/odata/Stadium/D.GetStadiumTest(test=’ddd’, land=’ssss’)
    webApifunctionsKeys_03

    Entity Get with single key Entity

    Before a function can be returned for a single entity, we need to know how to return the single entity in the controller. The key word can be used to match the single key property.

    public IHttpActionResult Get(int key)
    {
     return Ok(new City { Population = 9000000, Country = "Berlin", Id = key  });
    }
    

    This can then be used as follows:
    ~/odata/City(8)

    Entity Function (Single key property)

    The following example shows how to map a function to a single entity from type City. The function returns a Stadium entity.

    FunctionConfiguration getStadiumFromCityWithFunction = cities.EntityType.Function("GetStadiumsFromCityWithFunction");
    getStadiumFromCityWithFunction.ReturnsCollectionFromEntitySet<Stadium>("Stadium");
    

    The function is added to the city controller, namespace included.

    [EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    [ODataRoute("City({key})/D.GetStadiumsFromCityWithFunction()")]
    [HttpGet]
    public IHttpActionResult GetStadiumsFromCityWithFunction(int key)
    {
      return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "FC Zug" } });
    }
    

    The function can be called as follows:
    ~/odata/City(8)/D.GetStadiumsFromCityWithFunction()

    Entity Get with Composite keys Entity

    The following controller method shows how to GET the composite Key entity. The key word cannot be used here because it has 2 separate key properties. The key properties have to be defined in the URL.

    // http://localhost:60096/odata/Stadium(Name='Baz', Country='Germany')
    [ODataRoute("(Name={name}, Country={country})")]
    [EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    [HttpGet]
    public IHttpActionResult Get([FromODataUri] string name, [FromODataUri] string country)
    {
      return Ok(new Stadium { Capacity = 2300, Country = country, Name = name, Owner = "FC Zug" });
    }
    

    This can be called as follows:

    ~/odata/Stadium(Name=’Baz’, Country=’Germany’)

    Entity Function with Composite keys Entity

    Now that we know how to return a single entity for a composite entity, we can add a function to this and use it. The following method adds a function to the composite key entity Stadium.

    FunctionConfiguration getCityFromStadiumWithFunction = stadiums.EntityType.Function("GetCityFromStadiumWithFunction");
    getCityFromStadiumWithFunction.ReturnsFromEntitySet<City>("City");
    

    This is then used in the Stadium ODataController.

    // http://localhost:60096/odata/Stadium(Name='Baz', Country='Germany')/D.GetCityFromStadiumWithFunction()
    [EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    [ODataRoute("(Name={name},Country={country})/D.GetCityFromStadiumWithFunction()")]
    [HttpGet]
    public IHttpActionResult GetCityFromStadiumWithFunction([FromODataUri] string name, [FromODataUri] string country)
    {
     return Ok(new City { Population = 9000000, Country = "Berlin", Id = 8 });
    }
    

    This can then be called:
    ~/odata/Stadium(Name=’Baz’, Country=’Germany’)/D.GetCityFromStadiumWithFunction()

    The full code example can be download with this link.

    As you can see, OData Functions are very easy to use once you understand the design constraints and how the routing works.

    Complete OData Model

    public static IEdmModel GetModel()
    {
    	ODataModelBuilder builder = new ODataConventionModelBuilder();
    	builder.Namespace = "D";
    	builder.ContainerName = "Default";
    
    	EntitySetConfiguration<City> cities = builder.EntitySet<City>("City");
    	EntitySetConfiguration<Stadium> stadiums = builder.EntitySet<Stadium>("Stadium");
    
    	// Per Collection Stadium
    	FunctionConfiguration getStadiumsWithFunction = stadiums.EntityType.Collection.Function("GetStadiumsWithFunction");
    	getStadiumsWithFunction.ReturnsCollectionFromEntitySet<Stadium>("Stadium");
    
    	//  Per Collection Stadium, returns single entity
    	FunctionConfiguration getStadiumsTest = stadiums.EntityType.Collection.Function("GetStadiumTest");
    	getStadiumsTest.Parameter<string>("test");
    	getStadiumsTest.Parameter<string>("name");
    	getStadiumsTest.ReturnsFromEntitySet<Stadium>("Stadium");
    
    	// Per Entity (Single key property) City
    	FunctionConfiguration getStadiumFromCityWithFunction = cities.EntityType.Function("GetStadiumsFromCityWithFunction");
    	getStadiumFromCityWithFunction.ReturnsCollectionFromEntitySet<Stadium>("Stadium");
    
    	// Per Entity composite key Stadium
    	FunctionConfiguration getCityFromStadiumWithFunction = stadiums.EntityType.Function("GetCityFromStadiumWithFunction");
    	getCityFromStadiumWithFunction.ReturnsFromEntitySet<City>("City");
    
    	// Global Function
    	builder.Function("GlobalFunction").ReturnsCollectionFromEntitySet<Stadium>("Stadium");
    
    	return builder.GetEdmModel();
    }
    

    Complete OData StadiumController

    using System.Collections.Generic;
    using System.Web.Http;
    using System.Web.OData;
    using System.Web.OData.Query;
    using System.Web.OData.Routing;
    using WebAPIODataKeys.Models;
    
    namespace WebApiODataKeys.Controllers
    {
    	[ODataRoutePrefix("Stadium")]
    	public class StadiumController : ODataController
    	{
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[HttpGet]
    		[ODataRoute]
    		public IHttpActionResult Get()
    		{
    			return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "FC Zug" } });
    		}
    
    		// GET odata/stadium?name=fds&country=Switzerland
    		//[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		//[HttpGet]
    		//public IHttpActionResult Get(string name, string country)
    		//{
    		//	return Ok(new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "FC Zug" });
    		//}
    
    		// http://localhost:60096/odata/Stadium(Name='Baz', Country='Germany')
    		[ODataRoute("(Name={name}, Country={country})")]
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[HttpGet]
    		public IHttpActionResult Get([FromODataUri] string name, [FromODataUri] string country)
    		{
    			return Ok(new Stadium { Capacity = 2300, Country = country, Name = name, Owner = "FC Zug" });
    		}
    
    		// http://localhost:60096/odata/Stadium/D.GetStadiumTest(test='ddd', land='ssss')
    		[ODataRoute("D.GetStadiumTest(test={test}, name={name})")]
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[HttpGet]
    		public IHttpActionResult GetStadiumTest([FromODataUri] string test, [FromODataUri] string name)
    		{
    			return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = name, Name = test, Owner = "FC Zug" } });
    		}
    
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[ODataRoute("D.GetStadiumsWithFunction()")]
    		[HttpGet]
    		public IHttpActionResult GetStadiumsWithFunction()
    		{
    			return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "FC Zug" } });
    		}
    
    		// http://localhost:60096/odata/Stadium(Name='Baz', Country='Germany')/D.GetCityFromStadiumWithFunction()
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[ODataRoute("(Name={name},Country={country})/D.GetCityFromStadiumWithFunction()")]
    		[HttpGet]
    		public IHttpActionResult GetCityFromStadiumWithFunction([FromODataUri] string name, [FromODataUri] string country)
    		{
    			return Ok(new City { Population = 9000000, Country = "Berlin", Id = 8 });
    		}
    	}
    }
    
    

    Complete OData CityController

    using System.Collections.Generic;
    using System.Web.Http;
    using System.Web.OData;
    using System.Web.OData.Query;
    using System.Web.OData.Routing;
    using WebAPIODataKeys.Models;
    
    namespace WebApiODataKeys.Controllers
    {
    	public class CityController : ODataController
        {
    		public IHttpActionResult Get()
    		{
    			return Ok(new List<City> { new City { Population = 2300000, Country = "Switzerland", Id=1} });
    		}
    
    		public IHttpActionResult Get(int key)
    		{
    			return Ok(new City { Population = 9000000, Country = "Berlin", Id = key  });
    		}
    
    		[EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    		[ODataRoute("City({key})/D.GetStadiumsFromCityWithFunction()")]
    		[HttpGet]
    		public IHttpActionResult GetStadiumsFromCityWithFunction(int key)
    		{
    			return Ok(new List<Stadium> { new Stadium { Capacity = 2300, Country = "Switzerland", Name = "Times Stadium", Owner = "FC Zug" } });
    		}
        }
    }
    

    Links:

    http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-routing-conventions

    http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/

    http://blogs.msdn.com/b/odatateam/

    Advertisements
  • 相关阅读:
    将Java应用部署到SAP云平台neo环境的两种方式
    Java实现 LeetCode 524 通过删除字母匹配到字典里最长单词(又是一道语文题)
    Java实现 LeetCode 524 通过删除字母匹配到字典里最长单词(又是一道语文题)
    Java实现 LeetCode 523 连续的子数组和(ง •_•)ง
    Java实现 LeetCode 523 连续的子数组和(ง •_•)ง
    Java实现 LeetCode 523 连续的子数组和(ง •_•)ง
    Java实现 LeetCode 522 最长特殊序列 II(查找最长的非子序列的长度)
    Java实现 LeetCode 522 最长特殊序列 II(查找最长的非子序列的长度)
    Java实现 LeetCode 522 最长特殊序列 II(查找最长的非子序列的长度)
    Java实现 LeetCode 521 最长特殊序列 Ⅰ(出题人:“就是喜欢看你们不敢相信那么简单,又不敢提交的样子。”)...
  • 原文地址:https://www.cnblogs.com/freeliver54/p/6934448.html
Copyright © 2011-2022 走看看