Getting Started w/ .NET on GCP
The “Missing Tutorials” series
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/
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/
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
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}
donegsutil 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.
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):
And we can see that the only required property required for the call is “project” (which corresponds to the $PROJECT_ID):
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:
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
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/
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.