X
August 21, 2020

Writing an Oracle Cloud .NET Application Using VS Code or VS Codespaces

By: Yan Sun

Share

In this blog, I will demonstrate how to use Visual Studio Code and Visual Studio Codespaces with Oracle Cloud software development kit (SDK) for .NET to build an application that can access Oracle Cloud. I will cover steps for onboarding to Oracle Cloud and general set up required to use Oracle Cloud Infrastructure Developer Tools with some more specifics for .NET SDK, and how to run and debug this application in VS Code.

Oracle Cloud software development kit (SDK) for .NET was initially released in July, 2020. It is an addition to the Oracle Cloud developer tools family and enables developers who prefer .NET/C# to interact with Oracle Cloud.

Oracle Cloud Free Tier Offering

Before getting started with the application, let's briefly talk about Oracle Cloud Infrastructure account.

If you are new to Oracle Cloud, the first step required before using it is to create an Oracle Cloud Infrastructure account . Oracle Cloud Free Tier is a great way to to let you have a feeling of Oracle's cloud service offerings.

So if you haven't already signed up, please go ahead and sign up at the official Oracle Cloud Free Tier website.

Configuration for Authentication with Oracle Cloud

With an active Oracle Cloud account, you need to authenticate yourself when interacting with Oracle Cloud through OCI SDK for .NET. This is done through an API key pair and a configuration file that the SDK can read.

  1. API key pair - OCI SDK for .NET, like the other OCI public SDK's, relies on an RSA key pair in PEM format to authenticate a user. Please follow the steps below to generate the key pair and related information:

    - Create a user with appropriate permissions to access Oracle Cloud services: see how to create users, groups, and set permissions. If a user has already been created, you can use that user account and skip this step.

    - Generate key pair if not already available: see how to generate an API signing key. It is recommended that you protect your private key with a passphrase. You will keep the private key safely to yourself and upload the public key in one of the following steps.

    - Get the fingerprint of the public key: see how to get fingerprint. In fact, an easier way to get the fingerprint is from Console. After uploading the public key in the next step, Console will display the fingerprint and you can copy it from there.

    - Upload public key in Console to the user account if it is not already there: see how to upload public key.

  2. The configuration file is a way to supply information to the OCI SDK for .NET. Its default location is in the user's home directory (~/.oci/config), but you can store it in other locations, too. In addition to the key generated above, and the fingerprint, the configuration file also needs to include the tenancy OCID and user OCID. These unique identifiers can be found from Console. See how to find tenancy OCID and user OCID.
    Here is a sample configuration file that you can copy to your environment. The actual values in this file need to be updated to match your user account.
[DEFAULT]
user=ocid1.user.oc1..
fingerprint=f7:12:34:56:78:90:ab:bc:cd:de:ef:98:87:65:43:21
key_file=~/.oci/my_oci_api_key.pem
tenancy=ocid1.tenancy.oc1..
region=us-phoenix-1
pass_phrase=SomePassPhrase

Uploading the OCI Config file in VS Codespaces

If you are using an online IDE like VS Codespaces, you need to upload the config file and the required keys to be able to access OCI resources when you run your application.

There are different ways to upload the files to the remote IDE. In this blog I will highlight creating a config from the terminal.

The first step is to open the terminal in VS Codespace if it is not visible. Open the menu then go to View then select Terminal. Then follow the steps below:

# Create a new config file under ~/.oci
mkdir ~/.oci
cd ~/.oci
vi ~/.oci/config
 
# Copy the content of the config
# Select the correct path for the key file in the config:
key_file=~/.oci/oci_api_key.pem
 
# Copy the content of the local oci_api_key.pem file and paste it in the remote file
vi ~/.oci/oci_api_key.pem
 
# Copy the content of the local oci_api_key_public.pem file and paste it in remote file
vi ~/.oci/oci_api_key_public.pem

Below is the Terminal screen shot from VS Codespaces after creating the config file and the key files.

Working with .NET Core Application

Installing C# Extension

C# support in VS Code is through an optional extension that can be installed from the Marketplace. To install this extension, you can click on the "Extensions" icon on the left side and search for "C#." Install the extension from Microsoft.

C# extension is powered by OmniSharp and provides a lot of convenience to C# development, including:

  • supporting project.json and csproj projects
  • editing support such as IntelliSense, go to definition, syntax highlighting, find references, etc.

Creating New .NET Core Application

One feature that is not available in VS Code is an application creation wizard, which means that you will need to create a new application outside of VS Code.

Assuming .NET Core SDK has been installed (if not, download it here), you can create a new project through .NET Core CLI:

  1. Open a terminal and change directory to the folder where a project will be created. The folder name will be used as the project name in the next step.
  2. Run the following .NET Core CLI command, and a new fully functioning project will be created:
    dotnet new console
  3. Run the following .NET Core CLI command to build and run the new project. It should succeed and print out "Hello World!"
    dotnet run

Note: For VS Codespace, you can create a new project by following the above steps in the terminal.

Loading Existing Project

VS Code

With C# extension installed and a .NET Core application already created, we can open the project in VS Code:

  1. From "File" menu, select "Open..."
  2. Navigate to the folder containing the C# project
  3. Click on "Open"
  4. If VS Code shows a notification that says "Required assets to build and debug are missing. Add them?", select "Yes" and let VS Code load dependencies automatically.

Now we can view the project structure and source code in VS Code. It should contain a .csproj file and a Program.cs file.

VS Codespaces

The project files should be first uploaded to Github. Then you can Create a new Codespace and provide the Github link for the project that you want to import.

Importing OCI SDK for .NET Packages

OCI SDK for .NET are published as NuGet packages. In order to use the SDK to interact with Oracle Cloud, we need to import the packages first. Similar to C# support, in VS Code, you need to install an extension "NuGet Package Manager". Again go to "Extensions" view and search for "nuget package manager." The extension should show up at the top of the results list.

With the extension installed, you can search and install individual NuGet packages:

  1. From "View" menu, select "Command Palette..." (or use keyboard shortcut).
  2. From the pop-up, type "nuget", and search result should show "NuGet Package Manager: Add Package." Select that option.

  3. In the next dialog box, enter the name of the package you want to import and hit "Enter." You can enter a partial name, e.g. "OCI.DotNetSDK." All packages containing that partial name will be returned and displayed below the search box:

  4. Select the package you want to import and all versions found for this package will be displayed. In addition to the package you want to work with, you should also import OCI.DotNetSDK.Common package because it contains all the key functionalities including authenticating and sending HTTP requests.

  5. Pick a version that you want to use, typically the latest one, and the package will be added to the project automatically.

Now if you look at the .csproj file in the project, it should contain a new PackageReference inside ItemGroup section.

In this application, we are going to search and import two NuGet packages from OCI SDK for .NET:

  • OCI.DotNetSDK.Common
  • OCI.DotNetSDK.Identity

And the .csproj file should have an ItemGroup section like this:

<ItemGroup>
  <PackageReference Include="OCI.DotNetSDK.Common" Version="4.1.0"/>
  <PackageReference Include="OCI.DotNetSDK.Identity" Version="4.1.0"/>
</ItemGroup>

Writing Your First OCI .NET Application

Now we are ready to work on the source code and interact with Oracle Cloud in our application. Since all the information needed to authenticate the user with Oracle Cloud is already included in the configuration file, we only need to configure the service client to use the configuration file, and call service API's using the service client. We will update the auto-generated Program.cs with changes below:

  1. Make sure all the correct using statements are included. Some important ones are:

    - System.Threading.Tasks: Used for asynchronous methods
    - Oci.Common.Auth: Used to build authentication details provider
    - Oci.<Service>: Used to create service client
    - Oci.<Service>.Requests and Oci.<Service>.Responses: Used for API request/response objects

    Here is an example that uses IdentityService:

    using System.Threading.Tasks;
    using Oci.Common.Auth;
    using Oci.IdentityService;
    using Oci.IdentityService.Requests;
    using Oci.IdentityService.Responses;
    
  2. In the Main function, remove the original statement that prints out "Hello World!" and create an authentication details provider that uses values from the configuration file. There are multiple variations of authentication details provider initialization using the configuration file.

    If the configuration file is stored in the default location (~/.oci/config), we only need to provide the profile name to create an authentication details provider:

    var config = new ConfigFileAuthenticationDetailsProvider("DEFAULT");
            

    If the configuration file is stored in a custom location, we also need to supply the file path:

    var config = new ConfigFileAuthenticationDetailsProvider("/path/to/config/file", "DEFAULT");
    
  3. We use the authentication details provider to build a service client:

    using (var client = new IdentityClient(config))
    {
    }
    
  4. Create a request object, send it out using the service client, and receive the response returned by the service:

    var listCompartmentsRequest = new ListCompartmentsRequest
    {
        CompartmentId = compartmentId
    };
     
    using (var client = new IdentityClient(config))
    {
        ListCompartmentsResponse response = await client.ListCompartments(listCompartmentsRequest);
    }
    
  5. Because we are calling asynchronous function above, we also needs to update the Main function into an asynchronous function:

    static async Task Main(string[] args)
    

The final code looks like this:

using System;
using System.Threading.Tasks;
using Oci.Common.Auth;
using Oci.IdentityService;
using Oci.IdentityService.Requests;
using Oci.IdentityService.Responses;
 
namespace oci_sdk_dotnet_blog
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // Needs a valid compartment ocid from your tenancy.
            var compartmentId = "ocid1.tenancy.oc1..";
            try
            {
                var config = new ConfigFileAuthenticationDetailsProvider("DEFAULT");
 
                var listCompartmentsRequest = new ListCompartmentsRequest
                {
                    CompartmentId = compartmentId
                };
 
                using (var client = new IdentityClient(config))
                {
                    ListCompartmentsResponse response = await client.ListCompartments(listCompartmentsRequest);
                    Console.WriteLine($"Found {response.Items.Count} compartments.");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
}

Running/Debugging Application in VS Code

Now we are ready to run or debug our application, and this can be done right inside VS Code.

Running Application

In order to run the application without debugging, go to "Run" menu and select "Run Without Debugging."

If this is the first time you run this application, VS Code may not have been configured properly for it. It may ask for the environment to run in if it cannot make a decision automatically. If prompted for environment during this step, you can select ".NET Core" and VS Code will automatically create two files in a folder called ".vscode" under the project directory . These two files, tasks.json and launch.json, tell VS Code how to build and run the application.

After tasks.json and launch.json files have been created, run the project again and you should be able to see logs in debug console at the bottom of VS Code window. If everything goes well, the project should pass the build and produce outputs similar to this:

-------------------------------------------------------------------
You may only use the Microsoft .NET Core Debugger (vsdbg) with
Visual Studio Code, Visual Studio or Visual Studio for Mac software
to help you develop and test your applications.
-------------------------------------------------------------------
 
Found 40 compartments.

Debugging Application

Just like how we use other IDE's to debug our code, we can set breakpoints in our code by clicking to the left of the line numbers.

To start debugging, go to "Run" menu and select "Start Debugging." Again, if VS Code hasn't been configured to run or debug your program, follow the same steps as mentioned above on running application to create tasks.json and launch.json files. Once the debugging session starts, you can monitor the progress and see it stopping at the breakpoint you set. From the left side of VS Code window, click on the "Run" icon to go to the run/debug view, where you can find call stacks, variables, etc. that are common among all IDE's.

A floating control shows up at the top right corner of VS Code window. This control allows you to perform normal debugging operations, e.g. step over, step into, step out, continue, stop, etc.

Logging

Logging is an important way to debug your program, and OCI SDK for .NET uses NLog for logging. Any application using this SDK can capture the logs from the SDK, but you must include your own NLog configuration file to the project. This configuration file defines the logging targets and desired logging levels. Below is a sample config file that tells NLog to log to both console and file. For console output, NLog will print messages from Info logging level and above. For the log file, Nlog will include everything from Debug logging level and above. You can find more details about logging levels from  NLog official documentation.

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <targets>
        <target name="console" xsi:type="ColoredConsole" />
        <target name="file" xsi:type="File" fileName="${CurrentDir}/logs/${date:format=yyyy-MM-dd}.log" />
    </targets>
    <rules> 
        <logger name="*" minlevel="Info" writeTo="console" /> 
        <logger name="*" minlevel="Debug" writeTo="file" /> 
    </rules> 
</nlog>

So in order to see logging from the SDK, follow the steps below:

  1. create and add an NLog.config file to the root of your project.
  2. update .csproj file by adding the following section:
<ItemGroup>
  <Content Include="NLog.config">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

Now run or debug the program, you will see that in addition to the normal console logging, a folder called "logs" (as defined in NLog.config) was added to your project. It contains a log file with timestamp as its name (again defined in NLog.config). Both console output and the log file contain some information that did not show before. Those logging lines came from the OCI SDK for .NET.

Note that by adding NLog.config to the project, you will only see logging from OCI SDK for .NET. You may choose to use NLog to log information from your application, too. The only change you need to make is to add the following logger to your class.

private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

Then you can replace all statements that was writing to console with logger methods similar to this:

logger.Info($"Found {response.Items.Count} compartments.");

Providing User Values

In the example above, we are explicitly setting the value of "CompartmentId" that is required by the ListCompartmentRequest object. If you do not want to expose the value from the source code, or you would like to allow other people to customize the values, you can consider using one of the following approaches to supply the information to your application.

Reading from Environment Variables

VS Code is able to access environment variables that are available in all terminal sessions (e.g. the environment variables set through .bash_profile in MacOS or Linux). Optionally, you can define environment variables only for your application in VS Code by adding an "env" section in launch.json file (please see "Running/Debugging Application in VS Code" section above on how to generate the launch.json file). For example, we are adding a new environment variable "VSCODE_COMPARTMENT_ID" that will be used when running or debugging this application.


Then instead of hardcoding the value to compartmentId variable, we can retrieve it from this environment variable:

var compartmentId = Environment.GetEnvironmentVariable("VSCODE_COMPARTMENT_ID");

When you set environment variable in launch.json file, you will not be able to access it when you run your application from terminal, including the terminal in VS Code. Your application can read this value as environment variable only when it is launched by VS Code.

Reading from Configuration File

A third way to supply values to our application, in addition to hard-coding in source code and reading from environment variables, is through the configuration file. So far you have included user authentication information in that file. You can also supply other data as key value pairs. OCI SDK for .NET provides functions to read values from the configuration. Let's add an extra key value pair at the end of the DEFAULT profile in the existing configuration file:

[DEFAULT]
user=ocid1.user.oc1..
fingerprint=f7:12:34:56:78:90:ab:bc:cd:de:ef:98:87:65:43:21
key_file=~/.oci/my_oci_api_key.pem
tenancy=ocid1.tenancy.oc1..
region=us-phoenix-1
pass_phrase=SomePassPhrase
config_compartment_id=ocid1.tenancy.oc1..

Now you can update the code to read the value from the configuration file using its key "config_compartment_id". Remember to add "using Oci.Common;" in order to access ConfigFileReader.

var config = ConfigFileReader.Parse(ConfigFileReader.DEFAULT_FILE_PATH);
var compartmentId = config.GetValue("config_compartment_id");

Summary

In this blog, we walked through an end-to-end example of creating, configuring, and running/debugging a .NET Core application that uses OCI SDK for .NET in VS Code. As one of many IDE choices, VS Code obviously has its own advantages and limitations. But once configured for C# development, VS Code can be used to develop .NET Core applications with ease by providing features to browse, search, and import OCI SDK for .NET NuGet packages, edit source code, and run/debug the application.

If you are a .NET developer and have interests in Oracle Cloud, I would strongly recommend you experience it through our SDK and take advantage of the great offering from Oracle through the Free Tier program. I hope this blog can help you get jump-started with your own application interacting with Oracle Cloud. Please leave comments if you have any questions or feedback.

Here are some of the sources that we are referencing in this blog:

Share