Couchbase CRUD with .Net Core
Tags: couchbase, dotnet, csharp, tutorial
Published: 2021-09-17 19:26:34

![CRUD](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k5fki6qjvdcsyug5n41e.jpg)

Recently, I got an opportunity to work on .Net Core 3.1 with Couchbase. I wanted to share steps to create a new .Net Core Web API and implement CRUD operations with Couchbase as backend.  

<b>Pre-requisites:</b> 

Install Couchbase Community Edition 7.0.0 
Install sample bucket – “travel-sample” 
DotNet Core 3.1 SDK 
Visual Studio Code 

<b>What is Couchbase? </b>

Couchbase Server is an open source, distributed, JSON document database. It exposes a scale-out, key-value store with managed cache for sub-millisecond data operations, purpose-built indexers for efficient queries and a powerful query engine for executing SQL-like queries. More details can be found at their site [here](https://docs.couchbase.com/server/current/introduction/why-couchbase.html) 

<b>Create new WebAPI project: </b>

Execute below command to create a new .Net 3.1 project of type webapi 
```
dotnet new webapi --name CouchbaseWebAPI 
```
<b>Add Package reference: </b>

Package References and versions added for this example: 

Couchbase.Extensions.DependencyInjection Version 3.0.5.931 
CouchbaseNetClient Version 3.2 

Run below command in the project folder -  
```
dotnet add package Couchbase.Extensions.DependencyInjection --version 3.2.0 

dotnet add package CouchbaseNetClient --version 3.2 

dotnet restore 
```

<b>Update appsettings.json: </b>

Update appsettings.json to include required configuration for Couchbase – 

In default generated appsettings.json file you can add below configuration after the setting for AllowedHosts 
```json
  "Couchbase" : { 
    "ConnectionString": "couchbase://127.0.0.1", 
    "UseSsl": false, 
    "UserName": "<Couchbase userid>", 
    "Password": "<Couchbase password>" 
  } 
```
  

Update ConfigureSevices to add Couchbase 
```csharp
public void ConfigureServices(IServiceCollection services) 
{ 
      services.AddControllers();
      services.AddCouchbase(Configuration.GetSection("Couchbase"));
      services.AddCouchbaseBucket<INamedBucketProvider>("travel-sample"); 
} 
```  

*AddCouchbase* is an extension method from “Couchbase.Extensions.DependencyInjection” which allows us to add couchbase  configurations to your application. This is reading all the settings from appsettings.json which we updated in last step. 

*AddCouchbaseBucket* is an extension method which allows us to add a particular bucket to the system. While accessing documents, application will be looking at travel-sample bucket to work with documents. This has wired up dependency injection for the application. Now, based on this in our controller class, where we want to work with travel-sample bucket we update the class. As we are about to interact with Airport documents from the travel-sample bucket, lets create a Controller class as AirportController. 
```csharp
private readonly IBucket _bucket;  

public AirportController(INamedBucketProvider bucketProvider) 
{
       _bucket = bucketProvider.GetBucketAsync().GetAwaiter().GetResult(); 
} 
```
  
Here, in AirportController’s constructor, dependency injection is requesting for INamedBucketProvider (part of “Couchbase.Extensions.DependencyInjection”) object, which based on ConfigureServices above, gets couchbase bucket “travel-sample” assigned to _bucket which is of type IBucket (part of “Couchbase”). Now the controller is wired up to work with buckets. We can dive into individual endpoints for CRUD operations. 

Let’s start with Read (CRUD): - 

<b>Read: </b>

Create model classes named Airport and Geo:  
```csharp
public class Airport 
{         
    public string airportname { get; set; } 
    public string city { get; set; } 
    public string country { get; set; } 
    public string faa { get; set; } 
    public Geo geo { get; set; } 
    public string icao { get; set; } 
    public int id { get; set; } 
    public string type { get; set; } 
    public string tz { get; set; } 
} 

public class Geo 
{ 
    public double alt { get; set; } 
    public double lat { get; set; } 
    public double lon { get; set; } 
} 
```

Add below function in AirportController for HttpGet verb 
  
```csharp
[HttpGet] 
[Route("{Id}")] 
public async Task<Airport> Get(string Id) 
{ 
      // Get default collection object 
      var collection = await _bucket.DefaultCollectionAsync(); 

      // Get single document using KV search 

      var getResult = await collection.GetAsync(Id); 
      return getResult.ContentAs<Airport>(); 
} 
```
  
Access below link to get response from above functionality –  

https://localhost:5001/Airport/airport_3494 

airport_3494 is a key in the travel-sample bucket we installed. The key format in this sample bucket is type_Id, where type is the document type, here airport. And the Id is the unique identifier of the document, here 3494. The same format has been used in Create, update and delete below. 

Next, let us look at Create (CRUD): - 

<b>Create: </b>

Add an HttpPut endpoint as below 
```csharp
[HttpPut] 
public async Task put([FromBody]Airport airport) 
{ 
      if (airport.id != 0)
      {
           throw new Exception("Error in input data, Id should not be set!");
      } 

      // get default collection of the bucket
      var collection = await _bucket.DefaultCollectionAsync(); 

      // defaulting the id value to insert. New Id generation has different approaches which is not discussed here.
       airport.id = 1; 

       // using the collection object insert the new airport object
        await collection.InsertAsync<Airport>($"airport_{airport.id}", airport); 
} 
```

Here, we’ve hard coded airport Id as 1 and the same value is being passed for the key name in InsertAsync call. This will throw an error (Couchbase.Core.Exceptions.KeyValue.DocumentExistsException) for subsequent calls as soon as one record is entered in the couchbase DB with Id as 1. **Note:** generating a new Id has couple of different approaches which is outside scope of this article. 

Next up is update (CRUD): - 

<b>Update: </b>

Add an HttpPost endpoint as below  
```csharp
[HttpPost] 
public async Task post([FromBody]Airport airport) 
{ 
      if (airport.id == 0) 
      {
           throw new Exception("Error in input data, Id is required!"); 
      } 

       // get default collection of the bucket
       var collection = await _bucket.DefaultCollectionAsync(); 

       // call ReplaceAsync function to save the modified version of the document 
      await collection.ReplaceAsync<Airport>($"airport_{airport.id}", airport);
} 
``` 

ReplaceAsync function of collection object can be used to modify / update / replace the document. The first parameter is the KV search Key for the given document. Same as read, the key is in format type_id. 

Additionally, Couchbase SDK provides us with UpsertAsync function as well, which as names suggest is for update or insert. 

Lastly, let us look at Delete (CRUD): - 

<b>Delete: </b>

Add HttpDelete endpoint to our controller  
```csharp
[HttpDelete] 
[Route("{Id}")] 
public async Task delete(string Id) 
{ 
    if (string.IsNullOrEmpty(Id)) 
    {     
        throw new Exception("Error in input data, Id is required!"); 
    } 

    var collection = await _bucket.DefaultCollectionAsync(); 

    // Id contains key in required k/v search. e.g. airport_1 
    await collection.RemoveAsync(Id); 
} 
```

RemoveAsync function of collection object removes the document associated with key provided in input “Id”. 

<b>Bonus</b>, Custom Bucket Provider: 

Additionally, if we want to access multiple buckets from the system, we can use below method -  

If we want to access multiple buckets, we need to create interfaces which extend from INamedBucketProvider and use that provider while adding it to controllers. 

e.g.  
```csharp
public interface ICustomBucketProvider1 : Couchbase.Extensions.DependencyInjection.INamedBucketProvider { 

} 

public interface ICustomBucketProvider2 : Couchbase.Extensions.DependencyInjection.INamedBucketProvider { 

} 
```
And change the Configure services function as below –  

```csharp
services.AddCouchbaseBucket<ICustomBucketProvider1>("travel-sample"); 

services.AddCouchbaseBucket<ICustomBucketProvider2>("gamesim-sample"); 
```
 
Now in controller we can add reference to both these buckets as  

 ```csharp
private readonly IBucket _bucket1; 
private readonly IBucket _bucket2; 

public AirportController(ICustomBucketProvider1 bucketProvider1, 
  ICustomBucketProvider2 bucketProvider2) 
{ 
      _bucket1 = bucketProvider1.GetBucketAsync().GetAwaiter().GetResult();
      _bucket2 = bucketProvider2.GetBucketAsync().GetAwaiter().GetResult(); 
} 
```

The above code will wireup _bucket1 with travel-sample bucket and _bucket2 with gamesim-sample.  

Please refer below link for complete code sample -
https://github.com/parasmm/CouchbaseWebAPI
<13-Oct-2023> Updated github code to refer to latest nuget versions

<b>Conclusion: </b>

So we went through wiring up .Net Core web api with Couchbase SDK and Couchbase dependency injection. Created endpoint to work with Airport documents from travel-sample bucket provided by Couchbase. Created all the usual endpoints for CRUD operations. Additionally, we saw how to connect to more than one bucket in the application. 

<b>References: </b>

https://docs.couchbase.com/server/current/introduction/why-couchbase.html 
https://blog.couchbase.com/dependency-injection-aspnet-couchbase/ 
https://docs.couchbase.com/dotnet-sdk/current/hello-world/start-using-sdk.html 
https://docs.couchbase.com/tutorials/quick-start/quickstart-dotnet27-aspnetcore31-visualstudio-firstquery-cb65.html