ASP.NET Web API 2.2를 사용 하는 OData v4의 동작 및 함수Actions and Functions in OData v4 Using ASP.NET Web API 2.2

Mike Wassonby Mike Wasson

OData에서 작업 및 함수는 엔터티에 대 한 CRUD 작업으로 쉽게 정의 되지 않는 서버 쪽 동작을 추가 하는 방법입니다.In OData, actions and functions are a way to add server-side behaviors that are not easily defined as CRUD operations on entities. 이 자습서에서는 Web API 2.2를 사용 하 여 OData v4 끝점에 작업 및 함수를 추가 하는 방법을 보여 줍니다.This tutorial shows how to add actions and functions to an OData v4 endpoint, using Web API 2.2. 자습서는 ASP.NET Web API 2를 사용 하 여 OData V4 엔드포인트 만들기 자습서를 기반으로 합니다.The tutorial builds on the tutorial Create an OData v4 Endpoint Using ASP.NET Web API 2

자습서에서 사용 되는 소프트웨어 버전Software versions used in the tutorial

  • 웹 API 2.2Web API 2.2
  • OData v4OData v4
  • Visual Studio 2013 (Visual Studio 2017 다운로드)Visual Studio 2013 (download Visual Studio 2017 here)
  • .NET 4.5.NET 4.5

자습서 버전Tutorial versions

OData 버전 3의 경우 ASP.NET Web API 2의 Odata 작업을 참조 하세요.For OData Version 3, see OData Actions in ASP.NET Web API 2.

작업과 함수의 차이점 은 작업에 부작용이 있을 수 있다는 것 이며 함수는 그렇지 않습니다.The difference between actions and functions is that actions can have side effects, and functions do not. 작업 및 함수는 모두 데이터를 반환할 수 있습니다.Both actions and functions can return data. 작업에는 다음이 포함 됩니다.Some uses for actions include:

  • 복잡 한 트랜잭션.Complex transactions.
  • 여러 엔터티를 한 번에 조작 합니다.Manipulating several entities at once.
  • 엔터티의 특정 속성만 업데이트할 수 있습니다.Allowing updates only to certain properties of an entity.
  • 엔터티가 아닌 데이터를 보내는 중입니다.Sending data that is not an entity.

함수는 엔터티 또는 컬렉션과 직접 일치 하지 않는 정보를 반환 하는 데 유용 합니다.Functions are useful for returning information that does not correspond directly to an entity or collection.

작업 (또는 함수)은 단일 엔터티 또는 컬렉션을 대상으로 할 수 있습니다.An action (or function) can target a single entity or a collection. OData 용어에서이는 바인딩입니다.In OData terminology, this is the binding. 서비스에서 정적 작업으로 호출 되는 "바인딩되지 않은" 작업/함수를 사용할 수도 있습니다.You can also have "unbound" actions/functions, which are called as static operations on the service.

예: 작업 추가Example: Adding an Action

제품을 평가 하는 작업을 정의 해 보겠습니다.Let's define an action to rate a product.

Note

이 자습서에서는 ASP.NET Web API 2를 사용 하 여 OData V4 끝점 만들기 자습서를 기반으로 합니다.This tutorial builds on the tutorial Create an OData v4 Endpoint Using ASP.NET Web API 2

먼저 등급을 나타내는 ProductRating 모델을 추가 합니다.First, add a ProductRating model to represent the ratings.

namespace ProductService.Models
{
    public class ProductRating
    {
        public int ID { get; set; }
        public int Rating { get; set; }
        public int ProductID { get; set; }
        public virtual Product Product { get; set; }  
    }
}

또한 EF가 데이터베이스에 등급 테이블을 만들 수 있도록 DbsetProductsContext 클래스에 추가 합니다.Also add a DbSet to the ProductsContext class, so that EF will create a Ratings table in the database.

public class ProductsContext : DbContext
{
    public ProductsContext() 
            : base("name=ProductsContext")
    {
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    // New code:
    public DbSet<ProductRating> Ratings { get; set; }
}

EDM에 작업 추가Add the Action to the EDM

WebApiConfig.cs에서 다음 코드를 추가 합니다.In WebApiConfig.cs, add the following code:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>()
    .Action("Rate")
    .Parameter<int>("Rating");

EntityTypeConfiguration 메서드는 EDM (엔터티 데이터 모델)에 작업을 추가 합니다.The EntityTypeConfiguration.Action method adds an action to the entity data model (EDM). Parameter 메서드는 동작에 대해 형식화 된 매개 변수를 지정 합니다.The Parameter method specifies a typed parameter for the action.

또한이 코드는 EDM에 대 한 네임 스페이스를 설정 합니다.This code also sets the namespace for the EDM. 네임 스페이스는 동작에 대 한 URI에 정규화 된 동작 이름이 포함 되기 때문에 중요 합니다.The namespace matters because the URI for the action includes the fully-qualified action name:

http://localhost/Products(1)/ProductService.Rate

Note

일반적인 IIS 구성에서이 URL의 점은 IIS에서 404 오류를 반환 하는 원인이 됩니다.In a typical IIS configuration, the dot in this URL will cause IIS to return error 404. Web.config 파일에 다음 섹션을 추가 하 여이 문제를 해결할 수 있습니다.You can resolve this by adding the following section to your Web.Config file:

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

작업에 대 한 컨트롤러 메서드 추가Add a Controller Method for the Action

"Rate" 작업을 사용 하도록 설정 하려면 다음 메서드를 ProductsController에 추가 합니다.To enable the "Rate" action, add the following method to ProductsController:

[HttpPost]
public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    int rating = (int)parameters["Rating"];
    db.Ratings.Add(new ProductRating
    {
        ProductID = key,
        Rating = rating
    });

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

    return StatusCode(HttpStatusCode.NoContent);
}

메서드 이름이 작업 이름과 일치 하는지 확인 합니다.Notice that the method name matches the action name. [HttpPost] 특성은 메서드가 HTTP POST 메서드를 지정 합니다.The [HttpPost] attribute specifies the method is an HTTP POST method.

동작을 호출 하기 위해 클라이언트는 다음과 같은 HTTP POST 요청을 보냅니다.To invoke the action, the client sends an HTTP POST request like the following:

POST http://localhost/Products(1)/ProductService.Rate HTTP/1.1
Content-Type: application/json
Content-Length: 12

{"Rating":5}

작업에 대 한 URI는 엔터티 URI에 추가 된 정규화 된 작업 이름 이므로 작업에 대 한 URI가 제품 인스턴스에 바인딩되어" "합니다.The "Rate" action is bound to Product instances, so the URI for the action is the fully-qualified action name appended to the entity URI. EDM 네임 스페이스를 "제품 서비스"로 설정 했으므로 정규화 된 작업 이름은 제품 서비스 "입니다. Rate".(Recall that we set the EDM namespace to "ProductService", so the fully-qualified action name is "ProductService.Rate".)

요청의 본문은 작업 매개 변수를 JSON 페이로드로 포함 합니다.The body of the request contains the action parameters as a JSON payload. Web API는 JSON 페이로드를 매개 변수 값의 사전 인 ODataActionParameters 개체로 자동으로 변환 합니다.Web API automatically converts the JSON payload to an ODataActionParameters object, which is just a dictionary of parameter values. 이 사전을 사용 하 여 컨트롤러 메서드의 매개 변수에 액세스 합니다.Use this dictionary to access the parameters in your controller method.

클라이언트에서 동작 매개 변수를 잘못 된 형식으로 보내는 경우 Modelstate. IsValid 값은 false입니다.If the client sends the action parameters in the wrong format, the value of ModelState.IsValid is false. 컨트롤러 메서드에서이 플래그를 확인 하 고 IsValid 가 false 인 경우 오류를 반환 합니다.Check this flag in your controller method and return an error if IsValid is false.

if (!ModelState.IsValid)
{
    return BadRequest();
}

예: 함수 추가Example: Adding a Function

이제 가장 비용이 많이 드는 제품을 반환 하는 OData 함수를 추가 해 보겠습니다.Now let's add an OData function that returns the most expensive product. 이전 처럼 첫 번째 단계는 함수를 EDM에 추가 하는 것입니다.As before, the first step is adding the function to the EDM. WebApiConfig.cs에서 다음 코드를 추가 합니다.In WebApiConfig.cs, add the following code.

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>().Collection
    .Function("MostExpensive")
    .Returns<double>();

이 경우 함수는 개별 제품 인스턴스가 아닌 Products 컬렉션에 바인딩됩니다.In this case, the function is bound to the Products collection, rather than individual Product instances. 클라이언트는 GET 요청을 보내 함수를 호출 합니다.Clients invoke the function by sending a GET request:

GET http://localhost:38479/Products/ProductService.MostExpensive

이 함수에 대 한 컨트롤러 메서드는 다음과 같습니다.Here is the controller method for this function:

public class ProductsController : ODataController
{
    [HttpGet]
    public IHttpActionResult MostExpensive()
    {
        var product = db.Products.Max(x => x.Price);
        return Ok(product);
    }

    // Other controller methods not shown.
}

메서드 이름이 함수 이름과 일치 하는지 확인 합니다.Notice that the method name matches the function name. [HttpGet] 특성은 메서드가 HTTP GET 메서드를 지정 합니다.The [HttpGet] attribute specifies the method is an HTTP GET method.

HTTP 응답은 다음과 같습니다.Here is the HTTP response:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 00:44:07 GMT
Content-Length: 85

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Decimal","value":50.00
}

예제: 바인딩되지 않은 함수 추가Example: Adding an Unbound Function

이전 예제는 컬렉션에 바인딩된 함수 였습니다.The previous example was a function bound to a collection. 다음 예제에서는 바인딩되지 않은 함수를 만듭니다.In this next example, we'll create an unbound function. 바인딩되지 않은 함수는 서비스에서 정적 작업으로 호출 됩니다.Unbound functions are called as static operations on the service. 이 예제의 함수는 지정 된 우편 번호에 대 한 판매 세율을 반환 합니다.The function in this example will return the sales tax for a given postal code.

WebApiConfig 파일에서 함수를 EDM에 추가 합니다.In the WebApiConfig file, add the function to the EDM:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Function("GetSalesTaxRate")
    .Returns<double>()
    .Parameter<int>("PostalCode");

엔터티 형식 또는 컬렉션이 아닌 만드는에서 직접 함수 를 호출 하 고 있습니다.Notice that we are calling Function directly on the ODataModelBuilder, instead of the entity type or collection. 이렇게 하면 함수가 바인딩되지 않음을 모델 작성기에 알려 줍니다.This tells the model builder that the function is unbound.

함수를 구현 하는 컨트롤러 메서드는 다음과 같습니다.Here is the controller method that implements the function:

[HttpGet]
[ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
{
    double rate = 5.6;  // Use a fake number for the sample.
    return Ok(rate);
}

이 메서드를 배치한 웹 API 컨트롤러에는 중요 하지 않습니다.It does not matter which Web API controller you place this method in. ProductsController에 추가 하거나 별도의 컨트롤러를 정의할 수 있습니다.You could put it in ProductsController, or define a separate controller. [ODataRoute] 특성은 함수에 대 한 URI 템플릿을 정의 합니다.The [ODataRoute] attribute defines the URI template for the function.

클라이언트 요청 예제는 다음과 같습니다.Here is an example client request:

GET http://localhost:38479/GetSalesTaxRate(PostalCode=10) HTTP/1.1

HTTP 응답:The HTTP response:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 01:05:32 GMT
Content-Length: 82

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Double","value":5.6
}