Getting Started w/ .NET on GCP

The “Missing Tutorials” series

Daz Wilkin
8 min readAug 31, 2017

Writing a short series of ‘getting started’ posts for those of you, like me, who may get to the point of wanting to write code against a Google service, having a language chosen but then, having not written code for a week or two, I’m stalled by “How exactly do I get started?”

C#|.NET

Several years ago, I spent many, contented days writing C#. Google provides excellent support for .NET developers and now that the platform is open source, you’re not limited to running .NET applications on Windows but can run them on Linux (both are supported on Compute Engine), or you can package them in containers and run these under App Engine flex or Container Engine.

Setup

To avoid supporting the myriad ways you will arrive at this point, I’m just going to tell you what I’m running (Linux), and .NET Core (2.0.0).

PROJECT_ID=[[YOUR-PROJECT-ID]]
LANG=dotnet
mkdir -p ${HOME}/${PROJECT_ID}/${LANG}
cd ${HOME}/${PROJECT_ID}/${LANG}

Then:

dotnet new console \
--output cloud-storage
The template "Console Application" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on cloud-storage/cloud-storage.csproj...
Restoring packages for .../cloud-storage/cloud-storage.csproj...
Generating MSBuild file .../cloud-storage/obj/cloud-storage.csproj.nuget.g.props.
Generating MSBuild file .../cloud-storage/obj/cloud-storage.csproj.nuget.g.targets.
Restore completed in 185.64 ms for .../cloud-storage/cloud-storage.csproj.
Restore succeeded.

Google’s .NET Libraries

API Client Libraries

Google provides machine-generated Libraries for all its published services for .NET

https://developers.google.com/api-client-library/dotnet/
https://developers.google.com/api-client-library/dotnet/apis/
https://developers.google.com/resources/api-libraries/documentation/storage/v1/csharp/latest/

nuget:

https://www.nuget.org/packages/Google.Apis/
https://www.nuget.org/packages/Google.Apis.Auth/
https://www.nuget.org/packages/Google.Apis.Storage.v1/
NuGET “Google.Apis” (1.29.0)

The nuget page provides the .NET CLI command to use:

dotnet add package Google.Apis --version 1.29.0  Writing /tmp/tmpM6kupa.tmp
info : Adding PackageReference for package 'Google.Apis' into project '.../cloud-storage/cloud-storage.csproj'.
log : Restoring packages for .../cloud-storage/cloud-storage.csproj...
...
log : Installing Google.Apis.Core 1.29.0.
log : Installing Google.Apis 1.29.0.
...
info : Package 'Google.Apis' is compatible with all the specified frameworks in project '.../cloud-storage/cloud-storage.csproj'.
info : PackageReference for package 'Google.Apis' version '1.29.0' added to file '.../cloud-storage/cloud-storage.csproj'.

We’ll also use Application Default Credentials and these require:

https://www.nuget.org/packages/Google.Apis.Auth/
NuGet “Google.Apis.Auth” (1.29.0)

The nuget page provides the .NET CLI command:

dotnet add package Google.Apis.Auth --version 1.29.0

and, lastly, we’ll use Google Cloud Storage [GCS] (though you can use any of Google’s services as they’re all supported by the API Client Libraries) and for GCS this specific nuget package:

dotnet add package Google.Apis.Storage.v1 --version 1.29.0.958

Alternatively, you can manually edit the csproj file:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Apis" Version="1.29.0" />
<PackageReference Include="Google.Apis.Auth" Version="1.29.0" />
<PackageReference Include="Google.Apis.Storage.v1" Version="1.29.0.958" />
</ItemGroup>
</Project>

Cloud Client Libraries

Google

http://googlecloudplatform.github.io/google-cloud-dotnet/
https://googlecloudplatform.github.io/google-cloud-dotnet/docs/
https://github.com/GoogleCloudPlatform/google-cloud-dotnet

nuget

Google Cloud Storage

We’ll use Google Cloud Storage for this example. You may use any of Google’s services with the API Client Libraries because all of them are supported by the Libraries. Only a subset of the services is supported for Cloud Client Libraries and these are released on a different cadence to the underlying services. So, for example BigQuery is a GA service with a Beta Cloud Client Library for .NET.

BUCKET=$(whoami)-$(date +%y%m%d%H%M)
FILE=[[/Path/To/Your/File]]
gsutil mb -p ${PROJECT_ID} gs://${BUCKET}
Creating gs://${BUCKET}/...
for i in $(seq -f "%02g" 1 10)
do
gsutil cp $FILE gs://${BUCKET}/${i}
done
gsutil ls gs://${BUCKET}
gs://${BUCKET}/01
gs://${BUCKET}/02
gs://${BUCKET}/03
gs://${BUCKET}/04
gs://${BUCKET}/05
gs://${BUCKET}/06
gs://${BUCKET}/07
gs://${BUCKET}/08
gs://${BUCKET}/09
gs://${BUCKET}/10

Solution #1: Using API Client Libraries

When we ran ‘dotnet new’, we created a boilerplate Program.cs that we will now extend.

Program.cs:

using System;namespace cloud_storage
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Henry!");
}
}
}

For starters which should work with:

dotnet runHello Henry!

We have the relevant packages but we need to reference these from our code. Your IDE may facilitate specifying these. Unfortunately I couldn’t find Google documentation that helps with this. Because we’ll be using async calls, you’ll need:

using System.Threading.Tasks;

For Google auth:

using Google.Apis.Auth.OAuth2;

All the service-specific APIs depend upon:

using Google.Apis.Services;

And, for GCS:

using Google.Apis.Storage.v1;
using Google.Apis.Storage.v1.Data;

NB many (all?) of the APIs comprise two namespaces of the form:

using Google.[service].[version];
using Google.[service].[version].Data;

Define constants for the project ID and the bucket name and use the environment variables you created previously as values:

namespace cloud_storage
{
class Program
{
const string PROJECT_ID = [[PROJECT_ID]];
const string BUCKETNAME = [[BUCKET]];

static void Main(string[] args)
{
}
}
}

Everything hereafter will be within the Main method. I’m jamming everything in Main for ease. Please use your customary development best practices.

Using Main changes the async calls slightly, I’ve tweaked Google’s recommended approach to getting Application Default Credentials to make the call synchronous. This code is boilerplate to get us a proxy to a service:

GoogleCredential credential = Task.Run(
() => GoogleCredential.GetApplicationDefaultAsync()
).Result;
if (credential.IsCreateScopedRequired)
{
credential = credential.CreateScoped(new[]{
"https://www.googleapis.com/auth/devstorage.full_control"
});
}

StorageService service = new StorageService(
new BaseClientService.Initializer(){
HttpClientInitializer = credential
}
);

OK. We will need to do some work to determine how to call the relevant GCS methods. The .NET API is documented completely here:

https://developers.google.com/resources/api-libraries/documentation/storage/v1/csharp/latest/

And, it’s a good practice to use Google API Explorer as way to navigate service methods and determine properties. Using API Explorer, it’s possible to replicate the intended language call and then work backwards. So, starting with buckets list.

API Explorer “cloud storage”

We’re looking for “Cloud Storage JSON API” (v1):

https://developers.google.com/apis-explorer/#search/cloud%20storage/storage/v1/

And you may be able to see “storage.buckets.list” (and I assure you there’s another for storage.objects.list):

API Explorer

And we can see that the only required property required for the call is “project” (which corresponds to the $PROJECT_ID):

API Explorer “storage.buckets.list”

We already have a proxy to the service, and so this is easily translated into a method call:

var request = service.Buckets.List(PROJECT_ID);
var buckets = request.Execute();
if (buckets != null)
{
if (buckets.Items != null)
{
foreach (var b in buckets.Items)
{
Console.WriteLine(b.Name);
}
}
}

I think it takes a little work, but knowing that all the GCP Resources are called [Name]Resource, we can find the relevant class and methods here:

https://developers.google.com/resources/api-libraries/documentation/storage/v1/csharp/latest/classGoogle_1_1Apis_1_1Storage_1_1v1_1_1BucketsResource.html

And:

Google.Apis.Storage.v1.BucketsResource.List method

And, when we Execute() the ListRequest, we’re returned v1.Data.Buckets:

https://developers.google.com/resources/api-libraries/documentation/storage/v1/csharp/latest/classGoogle_1_1Apis_1_1Storage_1_1v1_1_1Data_1_1Buckets.html
Google.Apis.Storage.v1.Data.Buckets

We can apply the Items’ getter and enumerate the returned list of Bucket.

The procedure is very similar (which is good) for the Objects (within a Bucket). Here’s the code:

var request = service.Objects.List(BUCKETNAME);
var objects = request.Execute();
if (objects != null)
{
if (objects.Items != null)
{
foreach (var o in objects.Items)
{
Console.WriteLine(o.Name);
}
}
}

And here are the docs:

https://developers.google.com/resources/api-libraries/documentation/storage/v1/csharp/latest/classGoogle_1_1Apis_1_1Storage_1_1v1_1_1ObjectsResource.html

Jump to “Testing” to run the code.

Solution #2: Using Cloud Client Libraries

Unlike the machine-generated API Client Libraries, the Cloud Client Libraries include hand-crafted code. For this reason, Cloud Client Libraries aren’t always available for every Google service and, if a Library is available it may not be GA.

The Cloud Client Library main page leads us to the Cloud Client Libraries .NET documentation from where we can select Cloud Storage. This page includes a reference to the nuget package for library which is convenient:

Google.Cloud.Storage.V1

So, let’s generate boilterplate and add the package:

dotnet new console --output cloud-storage
The template "Console Application" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on cloud-storage/cloud-storage.csproj...
Restoring packages for .../cloud-storage/cloud-storage.csproj...
Generating MSBuild file .../cloud-storage/obj/cloud-storage.csproj.nuget.g.props.
Generating MSBuild file .../cloud-storage/obj/cloud-storage.csproj.nuget.g.targets.
Restore completed in 174 ms for .../cloud-storage/cloud-storage.csproj.

and:

dotnet add package "Google.Cloud.Storage.V1"
Writing /tmp/tmp4T8eCT.tmp
info : Adding PackageReference for package 'Google.Cloud.Storage.V1' ...
log : Installing Google.Apis.Core 1.27.1.
log : Installing Google.Apis 1.27.1.
log : Installing Google.Apis.Auth 1.27.1.
log : Installing Google.Api.Gax 2.0.0.
log : Installing Google.Apis.Storage.v1 1.27.1.881.
log : Installing Google.Api.Gax.Rest 2.0.0.
log : Installing Google.Cloud.Storage.V1 2.0.0.
info : Package 'Google.Cloud.Storage.V1' is compatible with all the specified frameworks in project '.../cloud-storage/cloud-storage.csproj'.
info : PackageReference for package 'Google.Cloud.Storage.V1' version '2.0.0' added to file '.../cloud-storage/cloud-storage.csproj'.

For reference:

https://www.nuget.org/packages/Google.Cloud.Storage.V1/
Google.Cloud.Storage.V1

You’ll notice that there are newer versions available 2.1.0-alpha.

Google’s documentation now uniquely uses Cloud Client Libraries samples and the Cloud Storage documentation includes a sample for .NET so I won’t reproduce everything here, suffice to write:

https://cloud.google.com/storage/docs/listing-buckets

Program.cs:

using Google.Apis.Storage.v1;
using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;namespace cloud_storage
{
class Program
{
const string PROJECT_ID = "dazwilkin-170828-medium";
const string BUCKETNAME = "dazwilkin-1708301815";
static void Main(string[] args)
{
var storage = StorageClient.Create();
var buckets = storage.ListBuckets(PROJECT_ID);
if (buckets != null)
{
foreach (var b in buckets)
{
Console.WriteLine(b.Name);
}
}
var objects = storage.ListObjects(BUCKETNAME);
if (objects != null)
{
foreach (var o in objects)
{
Console.WriteLine(o.Name);
}
}
}
}
}

This code is slightly simpler than the API Client Libraries code.

When you’re ready to test it, please proceed to “Testing”.

Testing

I recommend using Application Default Credentials to test your code. This enables config changes only for running the code as an authorized user locally, on App Engine, Compute Engine, and Container Engine. You must:

gcloud auth application-default login

You may also|additionally, authenticate with a service account. To do so, you must create a service account with sufficient permissions (the role ‘roles/storage.admin’ will be sufficient to make the buckets.list and objects.list calls. You must then also set the environment variable:

GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/key.json

Then:

dotnet run
${BUCKET}
01
02
03
04
05
06
07
08
09
10

You may delete the bucket (and its objects) when you’re done. Be *very* careful that you specify the correct bucket when you perform this delete. It deletes all the objects and then the bucket:

gsutil rm -r gs://${BUCKET}

Done.

--

--