앱 서비스 만들기 및 사용하기
Important
이 항목의 코드는 전부 C#으로 작성되었습니다. C++/WinRT 및 C#으로 작성된 앱 서비스 샘플 앱은 앱 서비스 샘플 앱을 참조하세요.
앱 서비스는 다른 UWP 앱에 서비스를 제공하는 UWP 앱입니다. 디바이스에서 앱 서비스는 웹 서비스와 비슷합니다. 앱 서비스는 호스트 앱에서 배경 작업으로 실행되며 다른 앱에 서비스를 제공할 수 있습니다. 예를 들어, 앱 서비스는 다른 앱에서 사용할 수 있는 바코드 스캐너 서비스를 제공할 수 있습니다. 또는 앱의 Enterprise Suite에 해당 제품군의 다른 앱에서 사용할 수 있는 일반 맞춤법 검사 앱 서비스가 있습니다. Windows 10, 버전 1607부터 앱 서비스를 통해 앱이 동일한 디바이스에서 호출할 수 있는 UI 없는 서비스를 원격 디바이스에 만들 수 있습니다.
Windows 10 버전 1607부터는 호스트 앱과 동일한 프로세스로 실행되는 앱 서비스를 개발할 수 있습니다. 이 문서는 별도 백그라운드 프로세스에서 실행되는 앱 서비스 만들기 및 사용에 중점을 둡니다. 공급자와 같은 프로세스에서 앱 서비스를 실행하는 방법에 대한 자세한 내용은 앱 서비스가 호스트 앱과 동일한 프로세스에서 실행되도록 변환을 참조하세요.
새 앱 서비스 공급자 프로젝트 생성하기
이 방법 섹션에서는 편의상 모든 기능을 하나의 솔루션으로 만들어 보겠습니다.
Visual Studio 2015 이상에서 새 UWP 앱 프로젝트를 만들고 이름을 AppServiceProvider로 지정합니다.
- 파일 > 새로 만들기 > 프로젝트...를 선택합니다.
- 새 프로젝트 만들기 대화 상자에서 비어 있는 앱(유니버설 Windows) C#을 선택합니다. 이렇게 하면 다른 UWP 앱에서 앱 서비스 사용할 수 있게 됩니다.
- 다음을 클릭하고 프로젝트 이름을 AppServiceProvider로 지정하고 위치를 선택한 다음, 만들기를 클릭합니다.
프로젝트의 대상 버전 및 최소 버전을 선택하라는 메시지가 나타나면 10.0.14393 이상을 선택합니다. 새 SupportsMultipleInstances 특성을 사용하려면 Visual Studio 2017 또는 Visual Studio 2019를 사용 중이면서 10.0.15063(Windows 10 크리에이터스 업데이트) 이상을 대상으로 해야 합니다.
Package.appxmanifest에 앱 서비스 확장 추가
AppServiceProvider 프로젝트의 Package.appxmanifest 파일을 텍스트 편집기에서 엽니다.
- 솔루션 탐색기에서 이 파일을 마우스 오른쪽 단추로 클릭합니다.
- 다른 프로그램으로 열기를 선택합니다.
- XML(텍스트) 편집기를 선택합니다.
다음 AppService
확장을 <Application>
요소 안에 추가합니다. 이 예제에서는 com.microsoft.inventory
서비스를 광고하고 이 앱을 앱 서비스 공급자로 식별합니다. 실제 서비스는 백그라운드 작업으로 구현됩니다. 앱 서비스 프로젝트에서 다른 앱에 서비스를 공개합니다. 서비스 이름에는 역방향 도메인 이름 스타일을 사용하는 것이 좋습니다.
xmlns:uap4
네임스페이스 접두사 및 uap4:SupportsMultipleInstances
특성은 Windows SDK 버전 10.0.15063 이상을 대상으로 하는 경우에만 유효하다는 점에 유의하세요. 이전 SDK 버전을 대상으로 하는 경우 안전하게 제거할 수 있습니다.
참고 항목
C++/WinRT 및 C#으로 작성된 앱 서비스 샘플 앱은 앱 서비스 샘플 앱을 참조하세요.
<Package
...
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
...
<Applications>
<Application Id="AppServiceProvider.App"
Executable="$targetnametoken$.exe"
EntryPoint="AppServiceProvider.App">
...
<Extensions>
<uap:Extension Category="windows.appService" EntryPoint="MyAppService.Inventory">
<uap3:AppService Name="com.microsoft.inventory" uap4:SupportsMultipleInstances="true"/>
</uap:Extension>
</Extensions>
...
</Application>
</Applications>
Category
특성은 이 애플리케이션을 앱 서비스 공급자로 식별합니다.
EntryPoint
특성은 서비스를 구현하는 정규화된 네임스페이스 클래스를 식별하며, 이 예에서 다음으로 이 클래스를 구현할 것입니다.
SupportsMultipleInstances
특성은 앱 서비스가 호출될 때마다 새 프로세스에서 실행되어야 함을 나타냅니다. 이는 필수 조건은 아니지만, 해당 기능이 필요하고 10.0.15063 SDK(Windows 10 크리에이터스 업데이트) 이상을 대상으로 하는 경우에 사용자에게 제공됩니다. 마찬가지로 앞에 uap4
네임스페이스가 붙습니다.
앱 서비스 개발하기
앱 서비스를 백그라운드 작업으로 구현할 수 있습니다. 이렇게 하면 포그라운드 애플리케이션이 다른 애플리케이션에서 앱 서비스를 호출할 수 있습니다. 앱 서비스를 백그라운드 작업으로 만들려면 MyAppService라는 솔루션에 새 Windows 런타임 구성 요소 프로젝트를 추가합니다(파일 > 추가 > 새 프로젝트). 새 프로젝트 추가 대화 상자에서 설치됨 > Visual C# > Windows 런타임 구성 요소(유니버설 Windows)를 선택합니다.
AppServiceProvider 프로젝트에서 프로젝트 간 참조를 새 MyAppService 프로젝트에 추가합니다(솔루션 탐색기에서 AppServiceProvider 프로젝트 >추가>참조>프로젝트>솔루션을 마우스 오른쪽 단추로 클릭하고 MyAppService>확인 선택). 참조를 추가하지 않으면 앱 서비스가 런타임 시 연결되지 않으므로 이 단계가 매우 중요합니다.
MyAppService 프로젝트에서 다음 using 문을 Class1.cs의 맨 위에 추가합니다.
using Windows.ApplicationModel.AppService; using Windows.ApplicationModel.Background; using Windows.Foundation.Collections;
Class1.cs의 이름을 Inventory.cs로 바꾸고, Class1의 스텁 코드를 Inventory라는 새 백그라운드 작업 클래스로 바꿉니다.
public sealed class Inventory : IBackgroundTask { private BackgroundTaskDeferral backgroundTaskDeferral; private AppServiceConnection appServiceconnection; private String[] inventoryItems = new string[] { "Robot vacuum", "Chair" }; private double[] inventoryPrices = new double[] { 129.99, 88.99 }; public void Run(IBackgroundTaskInstance taskInstance) { // Get a deferral so that the service isn't terminated. this.backgroundTaskDeferral = taskInstance.GetDeferral(); // Associate a cancellation handler with the background task. taskInstance.Canceled += OnTaskCanceled; // Retrieve the app service connection and set up a listener for incoming app service requests. var details = taskInstance.TriggerDetails as AppServiceTriggerDetails; appServiceconnection = details.AppServiceConnection; appServiceconnection.RequestReceived += OnRequestReceived; } private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { // This function is called when the app service receives a request. } private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { if (this.backgroundTaskDeferral != null) { // Complete the service deferral. this.backgroundTaskDeferral.Complete(); } } }
이 클래스는 앱 서비스가 그 작업을 수행하는 위치입니다.
백그라운드 작업을 만들면 Run이 호출됩니다. 백그라운드 작업은 Run이 완료되고 나면 종료되므로 백그라운드 작업이 요청을 계속 처리하도록 코드가 지연됩니다. 백그라운드 작업으로 구현된 앱 서비스는 호출을 받은 후 약 30초 동안 다시 호출되거나 지연을 사용하지 않는 한 활성 상태를 유지합니다. 앱 서비스가 호출자와 동일한 프로세스에 구현되면 앱 서비스의 수명은 호출자의 수명과 연결됩니다.
앱 서비스의 수명은 호출자에 따라 달라집니다.
- 호출자가 포그라운드이면 앱 서비스 수명은 호출자와 동일합니다.
- 호출자가 백그라운드이면 앱 서비스는 30초간 실행됩니다. 지연을 사용하면 일회에 한해 5초가 추가로 제공됩니다.
작업이 취소되면 OnTaskCanceled가 호출됩니다. 클라이언트 앱에서 AppServiceConnection을 삭제하거나 클라이언트 앱이 일시 중단되거나 OS가 종료 또는 절전 상태이거나 OS에 작업을 실행할 리소스가 없는 경우 이 작업이 취소됩니다.
앱 서비스용 코드 작성하기
OnRequestReceived로 앱 서비스의 코드가 이동됩니다. MyAppService의 Inventory.cs에 있는 스텁 OnRequestReceived를 이 예의 코드로 바꿉니다. 이 코드는 재고 품목에 대한 색인을 가져온 다음, 지정된 재고 품목의 이름과 가격을 검색하기 위해 명령 문자열과 함께 해당 색인을 서비스에 전달합니다. 여러분의 프로젝트에 대해 오류 처리 코드를 추가합니다.
private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
// Get a deferral because we use an awaitable API below to respond to the message
// and we don't want this call to get canceled while we are waiting.
var messageDeferral = args.GetDeferral();
ValueSet message = args.Request.Message;
ValueSet returnData = new ValueSet();
string command = message["Command"] as string;
int? inventoryIndex = message["ID"] as int?;
if (inventoryIndex.HasValue &&
inventoryIndex.Value >= 0 &&
inventoryIndex.Value < inventoryItems.GetLength(0))
{
switch (command)
{
case "Price":
{
returnData.Add("Result", inventoryPrices[inventoryIndex.Value]);
returnData.Add("Status", "OK");
break;
}
case "Item":
{
returnData.Add("Result", inventoryItems[inventoryIndex.Value]);
returnData.Add("Status", "OK");
break;
}
default:
{
returnData.Add("Status", "Fail: unknown command");
break;
}
}
}
else
{
returnData.Add("Status", "Fail: Index out of range");
}
try
{
// Return the data to the caller.
await args.Request.SendResponseAsync(returnData);
}
catch (Exception e)
{
// Your exception handling code here.
}
finally
{
// Complete the deferral so that the platform knows that we're done responding to the app service call.
// Note for error handling: this must be called even if SendResponseAsync() throws an exception.
messageDeferral.Complete();
}
}
이 예에서는 SendResponseAsync에 대해 awaitable 메서드를 호출하므로 OnRequestReceived가 async입니다.
서비스가 OnRequestReceived 처리기에서 async 메서드를 사용할 수 있도록 지연됩니다. 메시지 처리를 완료할 때까지 OnRequestReceived에 대한 호출이 완료되지 않게 합니다. SendResponseAsync는 결과를 호출자에게 보냅니다. SendResponseAsync는 호출 완료의 신호를 보내지 않습니다. 지연이 완료되어야 SendMessageAsync에 OnRequestReceived가 완료되었다는 신호를 보냅니다. SendResponseAsync에서 예외가 발생된 경우에도 지연을 완료해야 하므로 SendResponseAsync에 대한 호출이 try/finally 블록에서 래핑됩니다.
앱 서비스에서는 ValueSet 개체를 사용하여 정보를 교환합니다. 전달 가능한 데이터의 크기는 시스템 리소스에 의해서만 제한됩니다. ValueSet에 사용할 수 있는 사전 정의 키가 없습니다. 사용자는 앱 서비스의 프로토콜을 정의하는 데 사용할 키 값을 결정해야 합니다. 해당 프로토콜을 염두에 두면서 호출자를 기록해야 합니다. 이 예제에서는 Command
라는 키를 선택했습니다. 이 키의 값을 통해 앱 서비스에서 인벤토리 항목의 이름을 제공할지 아니면 값을 제공할지를 나타냅니다. 인벤토리 이름의 색인은 ID
키에 저장됩니다. 반환 값은 Result
키에 저장됩니다.
AppServiceClosedStatus 열거형은 앱 서비스 호출의 성공 또는 실패 여부를 나타내도록 호출자에게 반환됩니다. 앱 서비스 호출이 실패할 수 있는 예로는 리소스가 초과되어 OS에서 서비스 엔드포인트를 중단하는 경우를 들 수 있습니다. 사용자는 ValueSet를 통해 추가 오류 정보를 반환할 수 있습니다. 이 예제에서는 Status
라는 키를 사용하여 호출자에게 자세한 오류 정보를 반환합니다.
SendResponseAsync를 호출하면 ValueSet가 호출자에게 반환됩니다.
서비스 앱 배포하기/패키지 제품군 이름 가져오기
앱 서비스 공급자 앱을 배포해야 클라이언트에서 호출할 수 있습니다. Visual Studio에서 빌드 > 솔루션 배포를 선택하여 배포할 수 있습니다.
또한 앱 서비스 공급자를 호출하려면 해당 패키지 패밀리 이름이 필요합니다. 디자이너 뷰에서 AppServiceProvider 프로젝트의 Package.appxmanifest 파일을 열어 얻을 수 있습니다(솔루션 탐색기에서 파일을 두 번 클릭). 패키징 탭을 선택하고, 패키지 패밀리 이름 옆에 있는 값을 복사하여 메모장 같은 곳에 붙여넣습니다.
앱 서비스를 호출할 클라이언트 작성하기
파일 > 추가 > 새 프로젝트를 선택하여 솔루션에 Windows 유니버설 앱 프로젝트를 추가합니다. 새 프로젝트 추가 대화 상자에서 설치됨 > Visual C# > 비어 있는 앱(유니버설 Windows)을 선택하고 이름을 ClientApp으로 지정합니다.
ClientApp 프로젝트에서 다음 using 문을 MainPage.xaml.cs의 맨 위에 추가합니다.
using Windows.ApplicationModel.AppService;
MainPage.xaml에 textBox라는 텍스트 상자와 단추를 추가합니다.
button_Click이라는 단추 클릭 처리기를 추가하고, 단추 처리기의 서명에 async 키워드를 추가합니다.
버튼 클릭 처리기의 스텁을 다음 코드로 바꾸세요.
inventoryService
필드 선언을 반드시 포함시켜야 합니다.private AppServiceConnection inventoryService; private async void button_Click(object sender, RoutedEventArgs e) { // Add the connection. if (this.inventoryService == null) { this.inventoryService = new AppServiceConnection(); // Here, we use the app service name defined in the app service // provider's Package.appxmanifest file in the <Extension> section. this.inventoryService.AppServiceName = "com.microsoft.inventory"; // Use Windows.ApplicationModel.Package.Current.Id.FamilyName // within the app service provider to get this value. this.inventoryService.PackageFamilyName = "Replace with the package family name"; var status = await this.inventoryService.OpenAsync(); if (status != AppServiceConnectionStatus.Success) { textBox.Text= "Failed to connect"; this.inventoryService = null; return; } } // Call the service. int idx = int.Parse(textBox.Text); var message = new ValueSet(); message.Add("Command", "Item"); message.Add("ID", idx); AppServiceResponse response = await this.inventoryService.SendMessageAsync(message); string result = ""; if (response.Status == AppServiceResponseStatus.Success) { // Get the data that the service sent to us. if (response.Message["Status"] as string == "OK") { result = response.Message["Result"] as string; } } message.Clear(); message.Add("Command", "Price"); message.Add("ID", idx); response = await this.inventoryService.SendMessageAsync(message); if (response.Status == AppServiceResponseStatus.Success) { // Get the data that the service sent to us. if (response.Message["Status"] as string == "OK") { result += " : Price = " + response.Message["Result"] as string; } } textBox.Text = result; }
this.inventoryService.PackageFamilyName = "Replace with the package family name";
줄의 패키지 패밀리 이름을 위의 서비스 앱 배포 및 패키지 패밀리 이름 가져오기에서 얻은 AppServiceProvider 프로젝트의 패키지 패밀리 이름으로 바꿉니다.참고 항목
문자열 리터럴을 변수에 넣지 말고 붙여넣어야 합니다. 변수를 사용하면 작동하지 않습니다.
이 코드는 먼저 앱 서비스와의 연결을 설정합니다. 연결은
this.inventoryService
를 삭제할 때까지 열린 상태로 유지됩니다. 앱 서비스 이름은 AppServiceProvider 프로젝트의 Package.appxmanifest 파일에 추가한AppService
요소의Name
특성과 일치해야 합니다. 이 예제에서는<uap3:AppService Name="com.microsoft.inventory"/>
입니다.앱 서비스에 보낼 명령을 지정할 수 있도록
message
라는 ValueSet가 만들어집니다. 이 예제 앱 서비스는 두 작업 중 어떤 작업을 수행할지 나타내는 명령을 실행해야 합니다. 클라이언트 앱의 텍스트 상자에서 인덱스를 가져온 다음, 항목 설명을 가져오도록Item
명령을 사용하여 서비스를 호출합니다. 그런 다음, 항목 가격을 가져오도록Price
명령을 사용하여 호출합니다. 버튼 텍스트가 결과로 설정됩니다.AppServiceResponseStatus는 운영 체제에서 앱 서비스에 호출을 연결할 수 있었는지 여부만 나타내므로 앱 서비스에서 수신한 ValueSet의
Status
키를 확인하여 요청을 수행할 수 있었는지 확인합니다.ClientApp 프로젝트를 시작 프로젝트로 설정하고(솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭>시작 프로젝트로 설정) 솔루션을 실행합니다. 텍스트 상자에 숫자 1을 입력하고 버튼을 클릭하세요. 해당 서비스로부터 "Chair : Price = 88.99"를 다시 가져와야 합니다.
앱 서비스 호출에 실패하면 ClientApp 프로젝트에서 다음 사항을 확인합니다.
- 인벤토리 서비스 연결에 할당된 패키지 제품군 이름이 AppServiceProvider 앱의 패키지 제품군 이름과 일치하는지 확인합니다. button_Click에서
this.inventoryService.PackageFamilyName = "...";
인 줄을 확인합니다. - button_Click에서 인벤토리 서비스 연결에 할당된 앱 서비스 이름이 AppServiceProvider의 Package.appxmanifest 파일에 있는 앱 서비스 이름과 일치하는지 확인합니다.
this.inventoryService.AppServiceName = "com.microsoft.inventory";
을(를) 참조하세요. - AppServiceProvider 앱이 배포되었는지 확인합니다. (솔루션 탐색기에서 솔루션을 마우스 오른쪽 단추로 클릭하고 솔루션 배포를 선택합니다.)
앱 서비스 디버깅하기
- 앱 서비스 공급자 앱을 배포해야 서비스를 호출할 수 있으므로 디버깅 전에 솔루션이 배포되도록 합니다. (Visual Studio에서 >솔루션 배포를 빌드하세요.)
- 솔루션 탐색기에서 AppServiceProvider 프로젝트를 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 디버그 탭에서 작업 시작을 열지는 말되, 코드가 시작되면 디버깅으로 변경하세요. (C++를 사용하여 앱 서비스 공급자를 구현하는 경우 디버깅 탭에서 애플리케이션 시작을 아니요로 변경합니다).
- MyAppService 프로젝트의 Inventory.cs 파일에서 OnRequestReceived에 중단점을 설정합니다.
- AppServiceProvider 프로젝트를 시작 프로젝트로 설정하고 F5 키를 누릅니다.
- [시작] 메뉴에서(Visual Studio 아님) ClientApp을 시작합니다.
- 텍스트 상자에 숫자 1을 입력하고 버튼을 누르세요. 디버거가 앱 서비스의 중단점에서 앱 서비스 호출을 중지합니다.
클라이언트 디버깅하기
- 이전 단계의 지침에 따라 앱 서비스를 호출하는 클라이언트를 디버그합니다.
- [시작] 메뉴에서 ClientApp을 실행합니다.
- ClientApp.exe 프로세스(ApplicationFrameHost.exe 프로세스 아님)에 디버거를 연결합니다. (Visual Studio에서 >프로세스에 연결 디버깅을 선택하세요.)
- ClientApp 프로젝트에서 button_Click에 중단점을 설정합니다.
- ClientApp의 텍스트 상자에 숫자 1을 입력하고 단추를 클릭하면 클라이언트와 앱 서비스 둘 다의 중단점이 적중됩니다.
일반적인 앱 서비스 문제 해결
앱 서비스에 연결하려고 하면 AppUnavailable 상태가 되는 경우 다음 사항을 확인합니다.
- 앱 서비스 공급자 프로젝트 및 앱 서비스 프로젝트가 배포되었는지 확인합니다. 클라이언트를 실행하기 전에 둘 다 배포되어 있어야 하며, 그렇지 않으면 클라이언트가 연결할 대상이 없습니다. Visual Studio에서 빌드>솔루션 배포를 사용하여 배포할 수 있습니다.
- 솔루션 탐색기에서 앱 서비스 공급자 프로젝트에 앱 서비스를 구현하는 프로젝트에 대한 프로젝트-프로젝트 참조가 있는지 확인합니다.
<Extensions>
항목과 그 자녀 요소가 위의 Package.appxmanifest에 앱 서비스 확장 추가에서 지정된 앱 서비스 공급자 프로젝트에 속한 Package.appxmanifest 파일에 추가되었는지 확인합니다.- 클라이언트에서 앱 서비스 공급자를 호출하는 AppServiceConnection.AppServiceName 문자열이 앱 서비스 공급자 프로젝트의 Package.appxmanifest 파일에 지정된
<uap3:AppService Name="..." />
와 일치하는지 확인합니다. - AppServiceConnection.PackageFamilyName이 위의 Package.appxmanifest에 앱 서비스 확장 추가에서 지정한 앱 서비스 공급자 구성 요소의 패키지 패밀리 이름과 일치하는지 확인합니다.
- 이 예제의 서비스와 같은 out-of-proc 앱 서비스의 경우 앱 서비스 공급자 프로젝트에 있는 Package.appxmanifest 파일의
<uap:Extension ...>
요소에 지정된EntryPoint
가 앱 서비스 프로젝트의 IBackgroundTask를 구현하는 공용 클래스의 네임스페이스 및 클래스 이름과 일치하는지 확인합니다.
디버깅 문제 해결
디버거가 앱 서비스 공급자 앱 또는 서비스 프로젝트의 중단점에서 중지하지 않으면 다음 사항을 확인합니다.
- 앱 서비스 공급자 프로젝트 및 앱 서비스 프로젝트가 배포되었는지 확인합니다. 클라이언트를 실행하기 전에 둘 다 배포되어 있어야 합니다. Visual Studio에서 빌드>솔루션 배포를 사용하여 배포할 수 있습니다.
- 디버깅할 프로젝트가 시작 프로젝트로 설정되어야 하며 해당 프로젝트의 디버깅 속성이 F5 키를 눌러도 프로젝트를 실행하지 않도록 설정되어야 합니다. 프로젝트를 마우스 오른쪽 단추로 클릭하고 속성, 디버그(또는 C++에서는 디버깅)를 차례로 클릭합니다. C#에서 시작 작업을 실행하지 않지만 시작되면 내 코드 디버그로 변경합니다. C++에서 애플리케이션 시작을 아니요로 설정합니다.
설명
이 예제에서는 백그라운드 작업으로 실행되는 앱 서비스를 만들고 다른 앱에서 이 앱을 호출하는 방법을 간략하게 소개합니다. 유의해야 할 핵심은 다음과 같습니다.
- 앱 서비스를 호스트하는 백그라운드 작업을 만듭니다.
- 앱 서비스 공급자의 Package.appxmanifest 파일에
windows.appService
확장을 추가합니다. - 클라이언트 앱에서 연결할 수 있도록 앱 서비스 공급자의 패키지 패밀리 이름을 얻습니다.
- 앱 서비스 공급자 프로젝트에서 앱 서비스 프로젝트로의 프로젝트 간 참조를 추가합니다.
- Windows.ApplicationModel.AppService.AppServiceConnection을 사용하여 서비스를 호출합니다.
MyAppService의 전체 코드
using System;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Foundation.Collections;
namespace MyAppService
{
public sealed class Inventory : IBackgroundTask
{
private BackgroundTaskDeferral backgroundTaskDeferral;
private AppServiceConnection appServiceconnection;
private String[] inventoryItems = new string[] { "Robot vacuum", "Chair" };
private double[] inventoryPrices = new double[] { 129.99, 88.99 };
public void Run(IBackgroundTaskInstance taskInstance)
{
// Get a deferral so that the service isn't terminated.
this.backgroundTaskDeferral = taskInstance.GetDeferral();
// Associate a cancellation handler with the background task.
taskInstance.Canceled += OnTaskCanceled;
// Retrieve the app service connection and set up a listener for incoming app service requests.
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceconnection = details.AppServiceConnection;
appServiceconnection.RequestReceived += OnRequestReceived;
}
private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
// Get a deferral because we use an awaitable API below to respond to the message
// and we don't want this call to get canceled while we are waiting.
var messageDeferral = args.GetDeferral();
ValueSet message = args.Request.Message;
ValueSet returnData = new ValueSet();
string command = message["Command"] as string;
int? inventoryIndex = message["ID"] as int?;
if (inventoryIndex.HasValue &&
inventoryIndex.Value >= 0 &&
inventoryIndex.Value < inventoryItems.GetLength(0))
{
switch (command)
{
case "Price":
{
returnData.Add("Result", inventoryPrices[inventoryIndex.Value]);
returnData.Add("Status", "OK");
break;
}
case "Item":
{
returnData.Add("Result", inventoryItems[inventoryIndex.Value]);
returnData.Add("Status", "OK");
break;
}
default:
{
returnData.Add("Status", "Fail: unknown command");
break;
}
}
}
else
{
returnData.Add("Status", "Fail: Index out of range");
}
// Return the data to the caller.
await args.Request.SendResponseAsync(returnData);
// Complete the deferral so that the platform knows that we're done responding to the app service call.
// Note for error handling: this must be called even if SendResponseAsync() throws an exception.
messageDeferral.Complete();
}
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
if (this.backgroundTaskDeferral != null)
{
// Complete the service deferral.
this.backgroundTaskDeferral.Complete();
}
}
}
}