DEV Community

parasmm
parasmm

Posted on • Updated on

Couchbase CRUD with .Net Core

CRUD

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.

Pre-requisites:

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

What is Couchbase?

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

Create new WebAPI project:

Execute below command to create a new .Net 3.1 project of type webapi

dotnet new webapi --name CouchbaseWebAPI 
Enter fullscreen mode Exit fullscreen mode

Add Package reference:

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 
Enter fullscreen mode Exit fullscreen mode

Update appsettings.json:

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

  "Couchbase" : { 
    "ConnectionString": "couchbase://127.0.0.1", 
    "UseSsl": false, 
    "UserName": "<Couchbase userid>", 
    "Password": "<Couchbase password>" 
  } 
Enter fullscreen mode Exit fullscreen mode

Update ConfigureSevices to add Couchbase

public void ConfigureServices(IServiceCollection services) 
{ 
      services.AddControllers();
      services.AddCouchbase(Configuration.GetSection("Couchbase"));
      services.AddCouchbaseBucket<INamedBucketProvider>("travel-sample"); 
} 
Enter fullscreen mode Exit fullscreen mode

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.

private readonly IBucket _bucket;  

public AirportController(INamedBucketProvider bucketProvider) 
{
       _bucket = bucketProvider.GetBucketAsync().GetAwaiter().GetResult(); 
} 
Enter fullscreen mode Exit fullscreen mode

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): -

Read:

Create model classes named Airport and Geo:

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; } 
} 
Enter fullscreen mode Exit fullscreen mode

Add below function in AirportController for HttpGet verb

[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>(); 
} 
Enter fullscreen mode Exit fullscreen mode

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): -

Create:

Add an HttpPut endpoint as below

[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); 
} 
Enter fullscreen mode Exit fullscreen mode

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): -

Update:

Add an HttpPost endpoint as below

[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);
} 
Enter fullscreen mode Exit fullscreen mode

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): -

Delete:

Add HttpDelete endpoint to our controller

[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); 
} 
Enter fullscreen mode Exit fullscreen mode

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

Bonus, 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.

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

} 

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

} 
Enter fullscreen mode Exit fullscreen mode

And change the Configure services function as below –

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

services.AddCouchbaseBucket<ICustomBucketProvider2>("gamesim-sample"); 
Enter fullscreen mode Exit fullscreen mode

Now in controller we can add reference to both these buckets as

private readonly IBucket _bucket1; 
private readonly IBucket _bucket2; 

public AirportController(ICustomBucketProvider1 bucketProvider1, 
  ICustomBucketProvider2 bucketProvider2) 
{ 
      _bucket1 = bucketProvider1.GetBucketAsync().GetAwaiter().GetResult();
      _bucket2 = bucketProvider2.GetBucketAsync().GetAwaiter().GetResult(); 
} 
Enter fullscreen mode Exit fullscreen mode

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

Conclusion:

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.

References:

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

Top comments (2)

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez • Edited

Good post! I love to see things other than MSSQL in .NET I've used couchbase in js before, I think I'll give it a try on C# thanks!

as an aside, in your C# samples try to change the markdown to the following so you get syntax highlighting
( replace ' with a backtick)
'''csharp

Collapse
 
parasmm profile image
parasmm

@tunaxor , Thanks for the suggestion! I was myself looking for the option for syntax highlighting. Updated the post!