services.AddCouchbase(Configuration.GetSection("Couchbase"));
services.AddCouchbaseBucket<INamedBucketProvider>(Configuration["Couchbase:BucketName"]);
services.AddTransient<PlaylistRepository>();
services.AddTransient<UserProfileRepository>();
services.AddTransient<TrackRepository>();
Implementing the K/V operations
Introduction
In this lab you will gain experience with fetching an entry from the Couchbase server using the key. You will first merely fetch the JSON data and return it as a JSON String. Later, you will convert the JSON object into a Customer entity and return that.
What you will gain experience with:
-
Getting and returning an entry
-
Handling unexpected errors
-
Creating a new entry
-
Updating an existing entry
-
Deleting an entry
Estimated time to complete: 45 minutes
Instructions
Introducing the project
In your IDE, open the lab-04 project (closing other projects). Note that this project is quite a bit different from the one used in lab-03. This is due to move to an ASP.NET project. This will the project used from here on.
This project is an ASP.NET application that is designed to expose the core functionality of the Couchify application via REST endpoints. The REST layer has been implemented already and makes calls to the Repository layer of the application. This is where you will implement the logic to interact with the Couchbase .NET SDK to perform key/value operations, and later, the queries, sub-document operations and more. Take a moment to acquaint yourself with the key files in the project. These include:
-
appsettings.json - Notice that the same settings you configured in the prior lab are now found in this project.
-
Startup.cs - Observe the way in which the Couchbase dependencies have been configured to be injected into the ConfigureServices() method. In particular, this part of the method configures the ClusterProvider and BucketProvider, which abstract the details of obtaining the Cluster and Bucket references. Additionally, note that the repository classes have been configured as dependencies that can be injected elsewhere in the application code.
-
PlaylistController.cs - Handles all the REST requests for the Playlist. Will have the PlaylistRepository dependency injected into the constructor.
-
PlaylistRepository.cs - Implements the calls to the Couchbase SDK to perform K/V operations. Will have the INamedBucketProvider instance dependency injected into the constructor.
Implementing the RepositoryBase methods
Open the RepositoryBase.cs
file and locate the two methods there; GetBucket() and GetDefaultCollection().
Notice that the constructor provides access to the INamedBucketProvider, which is
dependency injected into the constructor by .NET Dependency Injection.
Use this property to implement the two methods.
Implement GetBucket()
Note that INamedBucketProvider offers a method, GetBucketAsync(). Implement the GetBucket() method to utilize the INamedBucketProvider property to fetch and return the bucket reference. The call to the GetBucketAsync() method could also throw a CouchbaseException, so be sure to catch this and re-throw as a project-specific RepositoryException with an appropriate message. This exception is defined in the Exceptions subfolder of the project.
Implement GetDefaultCollection()
Next, locate the GetDefaultCollection() method and implement the necessary code to obtain the default collection reference from the bucket. You can use the already implemented GetBucket() method to obtain the bucket. Realize that this call will also potentially throw a CouchbaseException, so be sure to catch this exception and re-throw as a RepositoryException with an appropriate message.
Implementing the Playlist Key/Value operations
When working with playlists, you will need to manage the lifecycle of individual playlists. This will usually involve a specific playlist document with a specific id. In working with playlists, it is not always easy to be certain of the initial state. For example, when attempting to create a new document for a given ID, it is possible that an entry already exists for that ID. The converse could also be true. When attempting to read a document for a given ID, it may be the document doesn’t actually exist. In both of these cases and in others, you must be prepared to not only handle the ideal scenarios but also to handle the less then ideal scenarios as well. matching entry.
Open the PlaylistRepository.cs
file and notice that the class extends RepositoryBase
You just completed implementing the methods of this class, which will be used extensively in the
Key/Value operations.
Get and return an existing playlist as string
-
Open the
Lab04Tests.cs
class and run it. You will find all tests fail. -
Return to the
PlaylistRepository.cs
file and locate the FindByIdAsString() method. -
Notice that this is an async method and therefore returns a Task
-
Add necessary code to FindByIdAsString() to fetch an entry given an id. Remember that K/V operations are performed against the Collection. You can use the GetDefaultCollection() method on the base class to obtain a reference to this Collection object. Given that you are merely returning a string representation of the Playlist, it is sufficient to obtain the dynamic type for the document and convert it using the ToString() method. Use the appropriate method on GetResult to obtain the dynamic variable and then return as a String
-
Be sure to add code to catch the exception(s) thrown by the call to get the entry and re-throw it as a
RepositoryException
. -
Verify your implementation by running the Lab04Tests. You should see that the GetAsString_Should_Return_a_Playlist() and GetAsString_ShouldReturnError() will now pass while the remaining two tests fail.
Get and return an existing playlist as a domain object
Recall from the course presentation that it is possible to utilize the default serializer and transcoder to handle the more basic conversion. Also recall that it is possible to serialize and deserialize a Playlist domain object using the DefaultJsonSerializer as long as the domain object and the associated JSON document are relatively aligned.
-
Take a moment to open the Playlist, Owner and Picture domain objects and note the property names found there as compared to the JSON documents found in Couchbase. Note that the property names found in the domain objects match the JSON document element names.
-
Next, locate the FindByIdAsPlaylist() method and add the necessary code to fetch an entry given the id. This time, use the appropriate method on the GetResult to convert the result to a Playlist object and return that.
-
Be sure to add the necessary code to catch the exception(s) and re-throw as a
RepositoryException
. -
Verify your implementation by re-running the Lab04Tests. This time GetAsPlaylist_Should_Return_a_Playlist() and GetAsPlaylist_ShouldReturnError() should also pass.
Inserting a document
-
Return to the
PlaylistRepository.cs
class and locate the empty Create() method. -
Add necessary code to this method to insert a new Playlist document. There are a couple of things to keep in mind here.
-
Remember that K/V operations are performed against the Collection.
-
The key will need to be calculated using the combination of the type and key properties. Locate the GenKey() method on Playlist.
-
Also remember that you are given a Playlist but will ultimately store a JSON document.
-
-
Be sure to add code to catch the exception(s) thrown by the call to insert the entry and re-throw it as a
RepositoryException
. What exceptions are expected to be thrown by the call? -
Verify your implementation by running the Lab04Tests. You should see that the Create_Success() and Create_Document_Exists() will now pass.
Updating a document
-
Locate the empty Update() method.
-
Add necessary code to this method to update the Playlist document based on the key Many of the guidelines for the prior step will also apply here.
-
Verify your implementation by running the Lab04Tests. You should now see that the additional tests, Update_Success() and Update_Document_Failure(), pass.
Upserting a document
As you learned in the lesson, there is a method that combines the behavior of insert and replace, called UpsertAsync. This mechanism can improve performance of insert and update operations when the prior state of the document is irrelevant. The reason for this is that in order to fulfill the semantics of both InsertAsync() and ReplaceAsync(), Couchbase may potentially need to search through the disk store where data is persisted to ensure the entry is or is not found there.
While the REST layer currently will not use this feature, you will still implement the functionality and ensure it’s correct behavior via tests.
-
Locate the empty InsertOrUpdate(() method
-
Add necessary code to perform the semantics of this operation. Verify your implementation by running the Lab04Tests.
-
You should now see that the additional tests, Upsert_Document_Doesnt_Exist() and Upsert_Document_Exists() pass.
Deleting a document
In the remaining step, utilize what you have learned about implementing the Insert() and Update() methods to now implement the Delete() method. Note that all you are provided is the document ID to perform this task.
When complete implementing this method, run the tests again and verify that all tests should now pass.
Make note of the total number of passing tests. This will be one of the quiz questions in the course.
Verifying functionality by executing the the REST interface
Now that you have successfully implemented and tested the K/V operations in the Repository layer, test the full functionality by starting the application and invoking these requests from the REST layer.
-
Locate the
CouchifyApplication.cs
class and run it in your IDE. -
Alternatively, you can run from the command line. From the
lab-04
folder, execute the following commanddotnet run --project Couchify.Service
-
Once the application starts up, execute a requests as follows
-
From an operating system that offers the curl command, such as Mac OS/X or Linux, you can execute the following commands directly.
Performing a Create
curl -i -X POST -H "Content-Type: application/json" localhost:8080/playlist -d '{ "owner": { "firstName": "Somebody", "lastName": "Important", "title": "Ms", "picture": { "thumbnail": "https://randomuser.me/api/portraits/thumb/women/64.jpg", "large": "https://randomuser.me/api/portraits/women/64.jpg", "medium": "https://randomuser.me/api/portraits/med/women/64.jpg" }, "username": "somebodyimportant12345" }, "visibility": "PRIVATE", "name": "My first playlist", "id":"mynewId", "type": "playlist"}'
Performing a Read
curl -i http://localhost:8080/playlist/playlist::mynewId
Performing an Update
curl -i -X PUT -H "Content-Type: application/json" localhost:8080/playlist/playlist::mynewId -d '{ "owner": { "firstName": "SomebodyElse", "lastName": "Important", "title": "Ms", "picture": { "thumbnail": "https://randomuser.me/api/portraits/thumb/women/64.jpg", "large": "https://randomuser.me/api/portraits/women/64.jpg", "medium": "https://randomuser.me/api/portraits/med/women/64.jpg" }, "username": "somebodyimportant12345" }, "visibility": "PRIVATE", "name": "My first playlist", "id":"mynewId", "type": "playlist"}'
Performing a Delete
curl -i -X DELETE http://localhost:8080/playlist/playlist::mynewId
-
On Windows and other platforms, you can use Postman to execute the commands. A Postman Collection file has been provided for you that contains all the commands. Open Postman and then select File→Import from the menu. From there, you can browse to your lab files project folder and import the postman_collections.json file. This file contains the collections for the various labs you will work through from here on. Each collection is named for the associated lab number and contains the various HTTP invocations to test the functional for that lab. They are also organized in the order in which you should invoke them, beginning with the 'Create Playlist' invocation. You can look at the details of the message before sending the request if you like by clicking on the message Headers and Body.
You should see all operations can be invoked from Postman and will return the anticipated results. It is important to keep in mind the order of execution though. You can’t 'Get' the playlist until you have created it. Likewise, update an delete also depend on the document existing. However, you should expect the appropriate response back if you request a document that doesn’t exist, or attempt to create a document that already exists.
-
Lab summary
In this lab, you implemented the first set of operations that directly interact with the Couchbase server by performing a Read based on a provided key. You had the chance to see two different ways to obtain the entry and return it. One was simply returning a C# string. This makes a sense when you want to simply return the data that was stored as a JSON document structure. In cases where you may want to fetch the entry and then operate on the data as a Domain object, you had the chance to also convert a stored JSON document into a Playlist and back as necessary. In most cases, this serialization/deserialization can be performed using the default serializer DefaultJsonSerializer that uses the Jackson JSON library.
You also had the chance to handle cases where errors can occur. The most common error in this operation is due to a DocumentNotFoundException or DocumentExistException that is thrown when trying to perform an operation on an entry where the supplied key is (or is not) not found.