danielwertheim

danielwertheim


notes from a passionate developer

Developer that lives by the mantra "code is meant to be shared".

Share


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

The release of v1.0.0 of MyCouch

Daniel WertheimDaniel Wertheim

MyCouch is the simple asynchronous CouchDb and Cloudant client for .Net – building on top of the asynchronous HTTP client and uses JSON.Net to provide flexible serialization behaviour. It is distributed via NuGet(CouchDb package, Cloudant package) and supports .Net4.0, .Net4.5 and Windows Store 8 & 8.1.

the simple asynchronous CouchDb and Cloudant client for .Net

The current API is the result of 363 days (on & off), 757 commits and 23 previous releases with feedback from users. From now on, focus will be on adding new features and build stuff around MyCouch, like the MyCouch.AspNetIdentity project.

Why MyCouch?

Aren’t there already others? Sure, not sure how active they are and what path they have taken. MyCouch uses the async HTTP-client and has no synchronous API, yet it supports .Net 4.0. It’s contextual and most stuff are changeable.

BigCouch compatible, MyCouch is also tested against Cloudant, who is contributing back their BigCouch (in short: scalable CouchDb) to CouchDb. So when that reaches the mainstream, MyCouch should be compatible from the gecko.

But, CouchDb is so simple, do I need a client? Well, I don’t know but MyCouch is simple and gives you a good starting point. You have access to the underlying connection via client.Connection.SendAsync, so if something isn’t supported in MyCouch, you can issue a simple HTTP-request and process the response as you would outside of MyCouch, but using the configured serializers in MyCouch.

Fork and tweak. The code-base is small and the API is contextual, so you could easily pick e.g. the stuff that has to do with Documents and/or Views and/or Entities etc.

The goal has been to stay simple, and if you would remove the MyCouchStore (an opinionated abstraction over the MyCouchClient) it would, for .Net 4.5, only have dependencies on Json.Net and Ensure.That. It tries to keep the domain language of CouchDb and it does not try to hide the fact that you are working in a HTTP oriented environment. It uses requests and responses and tries to group the API of the client in contexts which hopefully makes sense to a CouchDb user, e.g. client.Database; client.Documents; client.Entities; client.Views;

It tries to keep the domain language of CouchDb and it does not try to hide the fact that you are working in a HTTP oriented environment

Modular

If you think the Client carries to much and want to compose your own, it’s quite easy. Just pick the contextual module you want. Lets say you only want to work with native JSON and documents, you could make use of the MyCouch.Contexts.Documents class. You could easily have a look in the MyCouchClientBootstrapper to see how it’s bootstrapped, or you could use the bootstrapper:

var bootstrapper = new MyCouchClientBootstrapper();

using(var cn = new BasicHttpConnection("http://localhost:5984/samples"))  
{
    var documents = bootstrapper.DocumentsFn(cn);
}

Encryption, Caching etc.

Currently there is nothing built in for this. The plan is to offer this via separate NuGets. But again, you do have access to the underlying HTTP-connection, which can be switched out so that you can do stuff like: "How To Customize Quorum With Cloudant Using MyCouch"

MyCouchClient, MyCouchServerClient & MyCouchStore

There are three ways to interact with Cloudant or CouchDb.

Quick samples

There are tests and documentation at the project site on GitHub, which also contains samples etc on how to use MyCouch, but I have included a few below so that you get a feeling of how MyCouch works.

It all starts with NuGet:

install-package MyCouch  

or

install-package MyCouch.Cloudant  

CRUD samples

Some selective and quick samples. But there is more.

using(var client = new MyCouchClient("http://localhost:5984/mydb"))  
{
  //POST with server generated id
  var r1 = await client.Documents
    .PostAsync("{"name":"Daniel"}");

  //POST with client generated id
  var r2 = await client.Documents
    .PostAsync("{"_id":"someId", "name":"Daniel"}");

  //PUT for client generated id
  var r3 = await client.Documents
    .PutAsync("someId",
              "{"name":"Daniel"}");

  //PUT for updates
  var r4 = await client.Documents
    .PutAsync("someId", "docRevision", "{"name":"Daniel Wertheim"}");

  //PUT for updates with _rev in JSON
  var r5 = await client.Documents
    .PutAsync("someId",
              "{"_rev": "docRevision", "name":"Daniel Wertheim"}");

  //GET for documents
  var r6 = await client.Documents
    .GetAsync(someId, [someRev]);

  //DELETE for documents
  var r7 = await client.Documents
    .DeleteAsync(someId, someRev);

  //Using entities
  var me = new Person {Id = "SomeId", Name = "Daniel"};

  var r8 = await client.Entities.PutAsync(me);

  var r9 = await client.Entities
    .GetAsync<Person>(someId, [someRev]);

  var r10 = await client.Entities
    .DeleteAsync(me);

  //Using anonymous entities
  var r11 = await client.Entities
    .PostAsync(new { Name = "Daniel" });

  //Using dynamic
  var r12 = await client.Entities
    .GetAsync<dynamic>(someId);
}

Query sample

You query views via: Client.Views.Query<>(QueryViewRequest request).

By using a QueryViewRequest you get the possibility to perform the query against multiple databases. To do that, you would create the query once and then just execute it against whatever database(s) you want. Hence, the query is not attached to a certain database.

Configure the QueryViewRequest via the QueryViewRequest itself, using the Configure method. You could of course also set values directly on the request.

request.Configure(q => q  
    .Stale(bool value)
    .IncludeDocs(bool value)
    .Descending(bool value)
    .Key(string value)
    .Keys(params string[] value)
    .StartKey(string value)
    .StartKeyDocId(string value)
    .EndKey(string value)
    .EndKeyDocId(string value)
    .InclusiveEnd(bool value)
    .Skip(int value)
    .Limit(int value)
    .Reduce(bool value)
    .UpdateSeq(bool value)
    .Group(bool value)
    .GroupLevel(int value));

ViewQueryResponse

All queries will return a ViewQueryResponse<>. It could take any form of the following:

If you configure the query to IncludeDocs you can also make use of the generic overload that lets you specify the type of the returned Value and the IncludedDoc.

The response also contains information about: TotalRowCount, Offset, RowCount etc.

Bulk sample

MyCouch supports simple bulk operations. It works by using JSON representations of your documents. You use it like this:

var request = new BulkRequest()  
    .Delete("someId", "someRev")
    .Delete("someId", "someRev")
    .Include(newJsonDoc1)
    .Include(newJsonDoc2)
    .Include(existingJsonDoc);

BulkResponse response = await client.Documents.BulkAsync(request);  

MyCouchStore sample

Within the MyCouch library there is a MyCouchStore : IMyCouchStore, which wraps an IMyCouchClient and hides the http-requests and http-responses and instead lets you work directly with JSON and Entities. And instead of having to inspect the status of http-responses, it throws exception (MyCouchResponseException) if the response doesn’t succeed. It’s async as well, except from the Query which instead lets you work with an IObservable.

using(var store = new MyCouchStore(uri | client))  
{
    await store.StoreAsync(json);
    await store.StoreAsync(entity);

    var json = await store.GetByIdAsync(someId);
    var customer = await store.GetByIdAsync<Customer>(someId);

    ///and so on...
}

Basic authentication

Just format an URI according to: {scheme}://[{username}:{password}]/{authority}/{localpath}. There’s also a MyCouchUriBuilder to help you out with the generation of the URI.

new MyCouchClient("http://sa:p%40ssword@localhost:5984/testdb")

//OR

var uriBuilder = new MyCouchUriBuilder("http://localhost:5984/")  
    .SetDbName("foo")
    .SetBasicCredentials("sa", "p@ssword");

new MyCouchClient(uriBuilder.Build());  

Every response is a HTTP-response

The client uses HTTP to communicate with CouchDb. So every command you invoke will generate a HTTP-request which will return a HTTP-response. What you will get in return is also a response. There are different kind of response: ViewQueryResponse, DocumentResponse, DocumentHeaderResponse ....

Lets look at an example:

var getDocumentResponse = client.Documents.GetAsync("someid");

//or

var getEntityResponse = client.Entities.GetAsync<Artist>("someid");  

If you use a DEBUG compiled version of the driver, and invoke you invoke a ToString() on a response, you will see something like this:

{
    RequestUri: http://127.0.0.1:5984/test/1
    RequestMethod: GET
    Status: OK(200)
    IsSuccessful: true
    Error:<NULL>
    Reason: <NULL>
    ContentType: application/json
    Id: 1
    Rev: 34-e0813c9f99766efcf679468adb02c6ce
    Content: {"_id":"1","_rev":"...","$doctype":"artist","name":"Fake artist 1","albums":[...]}
}

You can also call response.ToStringDebugVersion() to get this dump.

The End

That’s it for this post. Can’t show everything. Hope it can assist you with using CouchDb and Cloudant. Feel free to contribute,

Cheers,

//Daniel

Developer that lives by the mantra "code is meant to be shared".

Comments