서비스 작업 및 동작 호출(WCF Data Services)
Open Data Protocol(OData)은 데이터 서비스에 대한 서비스 작업과 서비스 동작을 모두 정의합니다. 다른 데이터 서비스 리소스와 마찬가지로, 서비스 작업 및 서비스 동작은 URI을 사용하여 지정합니다. 서비스 작업과 동작은 모두 엔터티 형식의 컬렉션, 단일 엔터티 형식 인스턴스, 기본 형식(예: 정수 및 문자열), null(Visual Basic의 경우 Nothing)을 반환할 수 있습니다. 서비스 작업과 달리 서비스 동작은 데이터 모델 리소스에 바인딩할 수 있으며 시스템에 의도하지 않은 결과를 가져올 수 있으므로 HTTP POST 요청으로 호출해야 합니다. 자세한 내용은 서비스 작업(WCF Data Services) 및 OData 동작을 사용하여 서버 쪽 동작 구현을 참조하십시오.
서비스 작업과 서비스 동작은 모두 OData를 구현하는 데이터 서비스에서 반환하는 메타데이터에 노출됩니다. 메타데이터에서 두 항목은 모두 FunctionImport 요소로 표현됩니다. 강력한 형식의 DataServiceContext를 생성할 경우 서비스 참조 추가 및 DataSvcUtil.exe 도구는 이 요소를 무시합니다. 이 때문에 서비스 작업을 직접 호출하는 데 사용할 수 있는 컨텍스트에서 메서드를 찾을 수 없습니다. 그러나, 다음 두 가지 중 한 가지 방법으로 WCF Data Services 클라이언트를 사용하여 서비스 작업을 호출할 수 있습니다.
DataServiceContext에서 Execute<TElement>(Uri) 메서드를 호출하여 서비스 작업의 URI를 제공합니다. 이 방법은 모든 서비스 작업과 서비스 동작을 호출할 때 권장되는 방법입니다. HTTP POST 요청을 사용하여 호출되는 서비스 동작 또는 서비스 작업의 경우 httpMethod를 사용하는 Execute<TElement>(Uri, String, Boolean, array<OperationParameter[]) 메서드 오버로드를 호출하고 POST의 값을 제공합니다. 또한 이 메서드를 호출할 때는 매개 변수 값의 OperationParameter 컬렉션을 operationParameters에 전달하여 실행 중에 하나 이상의 매개 변수를 제공할 수도 있습니다. 서비스 동작을 호출할 때는 바인딩이 아닌 매개 변수만 이 방식으로 제공합니다.
DataServiceContext에서 CreateQuery<T>(String) 메서드를 사용하여 DataServiceQuery<TElement> 개체를 만듭니다. CreateQuery<T>(String)를 호출하면 서비스 작업의 이름이 entitySetName 매개 변수에 제공됩니다. 이 메서드는 열거될 때 또는 Execute() 메서드가 호출될 때 서비스 작업을 호출하는 DataServiceQuery<TElement> 개체를 반환합니다. 이 메서드는 컬렉션을 반환하는 GET 서비스 작업을 호출하는 데 사용합니다. AddQueryOption(String, Object) 메서드를 사용하면 단일 매개 변수를 제공할 수 있습니다. 이 메서드에서 반환한 DataServiceQuery<TElement> 개체는 모든 쿼리 개체와 같이 추가 구성할 수 있습니다. 자세한 내용은 데이터 서비스 쿼리(WCF Data Services)를 참조하십시오. 이 메서드는 서비스 동작을 호출하는 데 사용할 수 없습니다.
서비스 작업 및 서비스 동작 호출에 대한 고려 사항
WCF Data Services 클라이언트를 사용하여 서비스 작업을 호출하는 경우 다음과 같은 사항을 고려해야 합니다.
데이터 서비스를 비동기적으로 액세스하는 경우 DataServiceContext에 동일한 비동기 BeginExecute<TElement>(Uri, AsyncCallback, Object)/EndExecute<TElement>(IAsyncResult) 메서드를 사용하거나 DataServiceQuery<TElement>에 동일한 비동기 BeginExecute(AsyncCallback, Object)/EndExecute(IAsyncResult) 메서드를 사용해야 합니다.
도구에 의해 생성되고 CreateQuery<T>(String) 또는 Execute<TElement>(Uri) 메서드를 사용하여 서비스 작업을 호출하는 강력한 형식의 DataServiceContext 부분 클래스에 확장 메서드를 만드는 것을 고려해 보십시오. 이렇게 하면 컨텍스트에서 직접 서비스 작업을 호출할 수 있습니다. 자세한 내용은 서비스 작업 및 WCF Data Services 클라이언트 블로그 게시물을 참조하십시오.
CreateQuery<T>(String)를 사용하여 서비스 작업을 호출하는 경우 클라이언트 라이브러리는 앰퍼샌드(&)와 같은 예약 문자의 백분율 인코딩 및 문자열의 작은 따옴표 이스케이프를 수행하여 AddQueryOption(String, Object)에 제공된 문자를 자동으로 이스케이프합니다. 그러나, Execute 메서드 중 하나를 호출하여 서비스 작업을 호출할 경우 이와 같이 모든 사용자 제공 문자열 값을 반드시 이스케이프해야 합니다. URI의 작은 따옴표는 작은 따옴표의 쌍으로 이스케이프됩니다.
서비스 작업과 달리 서비스 동작은 추가로 구성할 수 없습니다. 즉, 서비스 동작을 호출한 후 추가 서비스 쪽 쿼리 작업을 수행할 수 없습니다. 자세한 내용은 OData 동작을 사용하여 서버 쪽 동작 구현을 참조하십시오.
서비스 작업 호출 예제
이 단원에는 WCF Data Services 클라이언트 라이브러리를 사용하여 서비스 작업을 호출하는 방법에 대한 다음 예제가 나와 있습니다.
Execute<T>를 호출하여 엔터티 컬렉션 반환
다음 예제는 city 문자열 매개 변수를 사용하고 IQueryable<T>을 반환하는 GetOrdersByCity라는 서비스 작업을 호출합니다.
' Define the service operation query parameter.
Dim city As String = "London"
' Define the query URI to access the service operation with specific
' query options relative to the service URI.
Dim queryString As String = String.Format("GetOrdersByCity?city='{0}'", city) _
& "&$orderby=ShippedDate desc" _
& "&$expand=Order_Details"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute the service operation that returns all orders for the specified city.
Dim results = context.Execute(Of Order)(New Uri(queryString, UriKind.Relative))
' Write out order information.
For Each o As Order In results
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the service operation query parameter.
string city = "London";
// Define the query URI to access the service operation with specific
// query options relative to the service URI.
string queryString = string.Format("GetOrdersByCity?city='{0}'", city)
+ "&$orderby=ShippedDate desc"
+ "&$expand=Order_Details";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute the service operation that returns all orders for the specified city.
var results = context.Execute<Order>(new Uri(queryString, UriKind.Relative));
// Write out order information.
foreach (Order o in results)
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
이 예제에서는 서비스 작업이 관련 Order_Detail 개체를 사용하여 Order 개체의 컬렉션을 반환합니다.
CreateQuery<T>를 사용하여 엔터티 컬렉션 반환
다음 예제에서는 CreateQuery<T>(String)를 사용하여 동일한 GetOrdersByCity 서비스 작업을 호출하는 데 사용되는 DataServiceQuery<TElement>를 반환합니다.
' Define the service operation query parameter.
Dim city As String = "London"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
' Use the CreateQuery method to create a query that accessess
' the service operation passing a single parameter.
Dim query = context.CreateQuery(Of Order)("GetOrdersByCity") _
.AddQueryOption("city", String.Format("'{0}'", city)).Expand("Order_Details")
Try
' The query is executed during enumeration.
For Each o As Order In query
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the service operation query parameter.
string city = "London";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
// Use the CreateQuery method to create a query that accessess
// the service operation passing a single parameter.
var query = context.CreateQuery<Order>("GetOrdersByCity")
.AddQueryOption("city", string.Format("'{0}'", city))
.Expand("Order_Details");
try
{
// The query is executed during enumeration.
foreach (Order o in query)
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
이 예제에서 AddQueryOption(String, Object) 메서드는 쿼리에 매개 변수를 추가하는 데 사용되고 Expand(String) 메서드는 관련 Order_Details 개체를 결과에 포함하는 데 사용됩니다.
Execute<T>를 호출하여 단일 엔터티 반환
다음 예제에서는 단일 Order 엔터티만 반환하는 GetNewestOrder라는 서비스 작업을 호출합니다.
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "GetNewestOrder"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns only the newest single order.
Dim o As Order = _
context.Execute(Of Order)( _
New Uri(queryString, UriKind.Relative)).FirstOrDefault()
' Write out order information.
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
Console.WriteLine(String.Format("Order date: {0}", o.OrderDate))
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "GetNewestOrder";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns only the newest single order.
Order order
= (context.Execute<Order>(new Uri(queryString, UriKind.Relative)))
.FirstOrDefault();
// Write out order information.
Console.WriteLine(string.Format("Order ID: {0}", order.OrderID));
Console.WriteLine(string.Format("Order date: {0}", order.OrderDate));
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
이 예제에서 FirstOrDefault<TSource>(IEnumerable<TSource>) 메서드는 실행 시 단일 Order 엔터티만 요청하는 데 사용됩니다.
Execute<T>를 호출하여 기본 값 컬렉션 반환
다음 예제에서는 문자열 값의 컬렉션을 반환하는 서비스 작업을 호출합니다.
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "GetCustomerNames"
'Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns a collection of customer names.
Dim customerNames As IEnumerable(Of String) _
= context.Execute(Of String)(New Uri(queryString, UriKind.Relative))
For Each name As String In customerNames
' Write out customer information.
Console.WriteLine(name)
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "GetCustomerNames";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns a collection of customer names
IEnumerable<string> customerNames
= context.Execute<string>(new Uri(queryString, UriKind.Relative));
foreach (string name in customerNames)
{
// Write out customer information.
Console.WriteLine(name);
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
Execute<T>를 호출하여 단일 기본 값 반환
다음 예제에서는 단일 문자열 값을 반환하는 서비스 작업을 호출합니다.
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "CountOpenOrders"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns the integer
' count of open orders.
Dim numOrders As Integer = context.Execute(Of Integer)( _
New Uri(queryString, UriKind.Relative)).FirstOrDefault()
' Write out the number of open orders.
Console.WriteLine(String.Format("Open orders as of {0}: {1}",
DateTime.Today.Date, numOrders))
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "CountOpenOrders";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns the integer
// count of open orders.
int numOrders
= (context.Execute<int>(new Uri(queryString, UriKind.Relative)))
.FirstOrDefault();
// Write out the number of open orders.
Console.WriteLine(string.Format("Open orders as of {0}: {1}",
DateTime.Today.Date, numOrders));
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
이 예제에서는 다시 FirstOrDefault<TSource>(IEnumerable<TSource>) 메서드를 사용하여 실행 시 단일 정수 값만 요청합니다.
데이터를 반환하지 않는 서비스 작업 호출
다음 예제에서는 데이터를 반환하지 않는 서비스 작업을 호출합니다.
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "ReturnsNoData"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns void.
context.Execute(Of String)( _
New Uri(queryString, UriKind.Relative))
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "ReturnsNoData";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns void.
context.Execute<string>(new Uri(queryString, UriKind.Relative));
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
데이터가 반환되지 않기 때문에 실행 값이 할당되지 않습니다. 요청이 성공했다는 유일한 표시는 DataServiceQueryException이 발생하지 않는다는 것입니다.
서비스 작업을 비동기적으로 호출
다음 예제에서는 BeginExecute<TElement>(Uri, AsyncCallback, Object) 및 EndExecute<TElement>(IAsyncResult)를 호출하여 서비스 작업을 비동기적으로 호출합니다.
' Define the service operation query parameter.
Dim city As String = "London"
' Define the query URI to access the service operation with specific
' query options relative to the service URI.
Dim queryString As String = String.Format("GetOrdersByCity?city='{0}'", city) _
& "&$orderby=ShippedDate desc" _
& "&$expand=Order_Details"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
' Define the delegate to callback into the process
Dim callback As AsyncCallback = AddressOf OnAsyncExecutionComplete
' Execute the service operation that returns
' all orders for the specified city.
Dim results = context.BeginExecute(Of Order)( _
New Uri(queryString, UriKind.Relative), _
callback, context)
// Define the service operation query parameter.
string city = "London";
// Define the query URI to access the service operation with specific
// query options relative to the service URI.
string queryString = string.Format("GetOrdersByCity?city='{0}'", city)
+ "&$orderby=ShippedDate desc"
+ "&$expand=Order_Details";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
// Execute the service operation that returns
// all orders for the specified city.
var results = context.BeginExecute<Order>(
new Uri(queryString, UriKind.Relative),
OnAsyncExecutionComplete, context);
Private Shared Sub OnAsyncExecutionComplete(ByVal result As IAsyncResult)
' Get the context back from the stored state.
Dim context = TryCast(result.AsyncState, NorthwindEntities)
Try
' Complete the exection and write out the results.
For Each o As Order In context.EndExecute(Of Order)(result)
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
End Sub
private static void OnAsyncExecutionComplete(IAsyncResult result)
{
// Get the context back from the stored state.
var context = result.AsyncState as NorthwindEntities;
try
{
// Complete the exection and write out the results.
foreach (Order o in context.EndExecute<Order>(result))
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
}
데이터가 반환되지 않기 때문에 실행으로 반환된 값이 할당되지 않습니다. 요청이 성공했다는 유일한 표시는 DataServiceQueryException이 발생하지 않는다는 것입니다.
다음 예제에서는 CreateQuery<T>(String)를 사용하여 동일한 서비스 작업을 비동기적으로 호출합니다.
' Define the service operation query parameter.
Dim city As String = "London"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
' Use the CreateQuery method to create a query that accessess
' the service operation passing a single parameter.
Dim query = context.CreateQuery(Of Order)("GetOrdersByCity") _
.AddQueryOption("city", String.Format("'{0}'", city)) _
.Expand("Order_Details")
' Define the delegate to callback into the process
Dim callback As AsyncCallback = AddressOf OnAsyncQueryExecutionComplete
' Execute the service operation that returns
' all orders for the specified city.
Dim results = _
query.BeginExecute(callback, query)
// Define the service operation query parameter.
string city = "London";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
// Use the CreateQuery method to create a query that accessess
// the service operation passing a single parameter.
var query = context.CreateQuery<Order>("GetOrdersByCity")
.AddQueryOption("city", string.Format("'{0}'", city))
.Expand("Order_Details");
// Execute the service operation that returns
// all orders for the specified city.
var results =
query.BeginExecute(OnAsyncQueryExecutionComplete, query);
Private Shared Sub OnAsyncQueryExecutionComplete(ByVal result As IAsyncResult)
' Get the query back from the stored state.
Dim query = TryCast(result.AsyncState, DataServiceQuery(Of Order))
Try
' Complete the exection and write out the results.
For Each o As Order In query.EndExecute(result)
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
End Sub
private static void OnAsyncQueryExecutionComplete(IAsyncResult result)
{
// Get the query back from the stored state.
var query = result.AsyncState as DataServiceQuery<Order>;
try
{
// Complete the exection and write out the results.
foreach (Order o in query.EndExecute(result))
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
}