ASP.NET Core Mobile Service with Visual Studio for Mac

With Visual Studio for Mac, a lot of new project templates are now available to macOS developers. For example, you can now create an ASP.NET Core or a .NET Core console project directly on your Mac. Wow! But, in my opinion, one of the most interesting project template is the Connected App. With this template you’ll be able to create a complete solution with a Mobile app (iOS, Android and Windows or Xamarin.Forms) and a Backend app, an ASP.NET Core mobile project that will be hosted in an Azure Mobile Services app.

In this post we’ll see how we can create a Connected App and publish our backend to Azure by using GitHub as source control host. Yes, it is a Continuos Delivery system.

Step 1 | Create a GitHub Repository

First of all, you need to create a new GitHub repository. If you already have a GitHub repository, please go ahead. So, navigate to your GitHub page and on Repository tab click on New. After that, create a README.md file to initialize your repository and, on desktop side, clone the repository by using your git client (like GitHub Desktop).

For a full guide, follow these steps: https://help.github.com/articles/creating-a-new-repository/

Step 2 | Create a Connected App solution

After you have created and cloned the repository, open Visual Studio for Mac and create a new project in the local repository folder by using the Connected App template:

Now we have a complete solution, from frontend, the mobile apps, to backend, the web api app:

By using your git client, commit and push the project on GitHub.

Step 3 | Publish to Azure via GitHub

Finally we are now ready to publish the service to Azure. So, go to the Microsoft Azure portal, log-in into your account and create a new Mobile App.

Set up the Mobile Service with your preferred settings:

To connect our Mobile Service to GitHub, go to Deployment Options and choose GitHub as source.

Then you’ll be requested to authorize access to the GitHub account.

Finally, select the repository and the branch you’d like to use.

And you have done! Now Azure automatically start the deployment process and … BOOM!

Yes! You got the following error:

Command: "D:\home\site\deployments\tools\deploy.cmd"
Handling ASP.NET Core Web Application deployment.
D:\home\site\repository\XConn\Droid\XConn.Droid.csproj(137,3): error MSB4019: The imported project "D:\Program Files (x86)\dotnet\sdk\1.0.0-preview3-004056\Xamarin\Android\Xamarin.Android.CSharp.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
D:\home\site\repository\XConn\iOS\XConn.iOS.csproj(163,3): error MSB4019: The imported project "D:\Program Files (x86)\dotnet\sdk\1.0.0-preview3-004056\Xamarin\iOS\Xamarin.iOS.CSharp.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
Failed exitCode=1, command=dotnet restore "XConn\XConn.sln"
An error has occurred during web site deployment.
\r\nD:\Program Files (x86)\SiteExtensions\Kudu\59.51212.2600\bin\Scripts\starter.cmd "D:\home\site\deployments\tools\deploy.cmd"

Step 4 | Publish with a Custom Deployment script

What happen? Just some background info. The deployment process is governed by Kudu, “the engine behind git/hg deployments, WebJobs, and various other features in Azure Web Sites”. In small words, Kudu scan the repository to find a project to publish. When done, it will generate a deployment script which first step is restoring NuGet packages from the solution.

Ah, the solution! The Connected App solution also include the Xamarin.iOS and Xamarin.Android project. Bingo! The deployment process is trying to solve and build the Xamarin packages on Azure Mobile Service, which is not allowed!

So, what we’ll do is to create a custom deployment script to restore, build and publish only the Mobile Service project. To do that we’ll use a .deployment file (check instruction here):

[config]
command = deploy.cmd

And this is the custom deployment script that you can customize by only replacing the project name:

@if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off

:: ----------------------
:: KUDU Deployment Script
:: Version: 1.0.10
:: ----------------------

:: Prerequisites
:: -------------

:: Verify node.js installed
where node 2>nul >nul
IF %ERRORLEVEL% NEQ 0 (
  echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.
  goto error
)

:: Setup
:: -----

setlocal enabledelayedexpansion

SET ARTIFACTS=%~dp0%..\artifacts

IF NOT DEFINED DEPLOYMENT_SOURCE (
  SET DEPLOYMENT_SOURCE=%~dp0%.
)

IF NOT DEFINED DEPLOYMENT_TARGET (
  SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot
)

IF NOT DEFINED NEXT_MANIFEST_PATH (
  SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest

  IF NOT DEFINED PREVIOUS_MANIFEST_PATH (
    SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest
  )
)

IF NOT DEFINED KUDU_SYNC_CMD (
  :: Install kudu sync
  echo Installing Kudu Sync
  call npm install kudusync -g --silent
  IF !ERRORLEVEL! NEQ 0 goto error

  :: Locally just running "kuduSync" would also work
  SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
)
IF NOT DEFINED DEPLOYMENT_TEMP (
  SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random%
  SET CLEAN_LOCAL_DEPLOYMENT_TEMP=true
)

IF DEFINED CLEAN_LOCAL_DEPLOYMENT_TEMP (
  IF EXIST "%DEPLOYMENT_TEMP%" rd /s /q "%DEPLOYMENT_TEMP%"
  mkdir "%DEPLOYMENT_TEMP%"
)

IF DEFINED MSBUILD_PATH goto MsbuildPathDefined
SET MSBUILD_PATH=%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe
:MsbuildPathDefined
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Deployment
:: ----------

echo Handling ASP.NET Core Web Application deployment.

:: 1. Restore nuget packages
call :ExecuteCmd dotnet restore "XConn\MobileAppService\XConn.MobileAppService.csproj"
IF !ERRORLEVEL! NEQ 0 goto error

:: 2. Build and publish
call :ExecuteCmd dotnet publish "XConn\MobileAppService\XConn.MobileAppService.csproj" --framework netcoreapp1.0 --configuration Release --output "%DEPLOYMENT_TEMP%"
IF !ERRORLEVEL! NEQ 0 goto error

:: 3. KuduSync
call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
IF !ERRORLEVEL! NEQ 0 goto error

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto end

:: Execute command routine that will echo out when error
:ExecuteCmd
setlocal
set _CMD_=%*
call %_CMD_%
if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%
exit /b %ERRORLEVEL%

:error
endlocal
echo An error has occurred during web site deployment.
call :exitSetErrorLevel
call :exitFromFunction 2>nul

:exitSetErrorLevel
exit /b 1

:exitFromFunction
()

:end
endlocal
echo Finished successfully.

Finally, you can publish the .deployment and deploy.cmd files to the root of the repository. Once done, the deployment process restart and now … the Mobile Service is up and running!

Happy coding!

Found a problem? Edit this post