/ DOTNET, HOWTO

How-To write a C# SDK in .NET Core

For interacting with OpenAI through C#, I decided to write one myself. But how do we get started writing a SDK?

Getting started writing our SDK

Deciding on the structure of your SDK

First of all we need to decide on how we want to use it. This is a crucial step, since it will notate how easy it is to use, and influence the adoption rate in the long run.

In this case, I decided that the easiest way to start is by being able to create an instance of a class that allows me to set properties such as the host and others (maybe even an access key in the future for online APIs). For the Project structure I decided on creating a solution with a unique namespace that can be re-used and imported by my other solutions.

Configuring the Project structure with dotnet core

Start by creating the solution and sub projects:

# Create 2 Projects, one for the SDK and one for the Consumer
dotnet new classlib --name <sdk>
dotnet new console --name <sdk-consumer>

# Create a solution and add the 2 projects
dotnet new sln --name <name>
dotnet sln <name> add <sdk>
dotnet sln <name> add <sdk-consumer>

# Add a reference to Newonsoft.Json for JSON parsing
dotnet add <sdk> package NewtonSoft.Json

Afterwards we add a reference in our <sdk-consumer> to our ./<sdk-consumer>/<sdk-consumer>.csproj file so that it looks like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\<sdk>\sdk.csproj" />
  </ItemGroup>

</Project>

Writing our entry class in the SDK

To be able to access our SDK, we can now create a class in the <sdk> project:

using System;

namespace OpenAI.SDK
{
    public class ApiService
    {
        private static readonly HttpClient client = new HttpClient();

        private readonly string host;

        public ApiService(string host = "http://127.0.0.1:5000")
        {
            this.host = host;
        }
    }
}

That we are then able to use in other class like this:

// Imports 
using OpenAI.SDK; // or your own namespace

// The code in your method or constructor:
var sdk = new ApiService("http://127.0.0.1:1337");
sdk.DoCall();

Allowing me to inject it further on and still have the same properties wherever I use it.

Creating API calls from the SDK

Since I will be interacting with a JSON API, there are 2 calls that are mainly used: GET and POST. To be able to perform this call, we create a HttpClient and set it in our constructor like this:

namespace OpenAI.SDK
{
    public class ApiService
    {
        private static readonly HttpClient client = new HttpClient();

        private readonly string host;

        public ApiService(string host = "http://127.0.0.1:5000")
        {
            this.host = host;
        }
    }
}

Performing a GET call

For our GET call we do a simple GetStringAsync call through our HttpClient which we will then decode by using the NewtonSoft.JSON’s excellent DeserializeObject method that uses a POCO class, resulting in:

public async Task<EnvGetAllResponse> EnvListAll()
{
    var json = await client.GetStringAsync($"{this.host}/v1/envs/");
    var resParsed = JsonConvert.DeserializeObject<EnvGetAllResponse>(json);
    return resParsed;
}

POCO Class:

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace OpenAI.SDK.Models.Response
{
    public class EnvGetAllResponse
    {
        [JsonProperty("all_envs")]
        public Dictionary<string, string> Environments { get; set; }
    }
}

Performing a POST call

The POST call almost exactly goes in the same format, except that we will have to pass a body to it.

To do this we create a Request POCO class in its own namespace such as:

using Newtonsoft.Json;

namespace OpenAI.SDK.Models.Request
{
    public class EnvCreateRequest
    {
        [JsonProperty("env_id")]
        public string EnvId { get; set; }
    }
}

That we can then initialize and send after serializing it with SerializeObject through the PostAsync call in our HttpClient object:

public async Task<EnvCreateResponse> EnvCreate(string envID)
{
    var requestBody = new EnvCreateRequest {
        EnvId = envID
    };
    var requestJson = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

    var res = await client.PostAsync($"{this.host}/v1/envs/", requestJson);
    var resContent = await res.Content.ReadAsStringAsync();
    var resParsed = JsonConvert.DeserializeObject<EnvCreateResponse>(resContent);
    return resParsed;
}
xavier

Xavier Geerinck

Xavier works as a Cloud Solution Architect at Microsoft, helping its customer unlock the full potential of the cloud. Even though he is still considered a young graduate, he achieved his first success at the age 16, by creating and selling his first startup. He then took this knowledge to create and help more startups in different markets such as technology, social media, philanthropy and home care. While in the meantime gaining more enterprise insights at renowned enterprises such as Nokia, Cisco and now Microsoft.

Read More