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.
MyCouchClient
: used for working against a specific database to insert documents, query views etc.MyCouchServerClient
: used for working against a specific server node to e.g. create databases etc.MyCouchStore
: if you don’t want to work with http-requests and responses, this API offers a simple abstraction for you.
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:
ViewQueryResponse
– Eachrow
inresponse.Rows
will be represented as a JSON-string.ViewQueryResponse<string>
– Eachrow
inresponse.Rows
will be represented as a JSON-string.ViewQueryResponse<string[]>
– Eachrow
inresponse.Rows
will be represented as a JSON-string array.ViewQueryResponse<Album>
– Eachrow
inresponse.Rows
will be represented as an entity.ViewQueryResponse<Album[]>
– Eachrow
inresponse.Rows
will be represented as an entity array.ViewQueryResponse<dynamic>
– Eachrow
inresponse.Rows
will be represented as an dynamic object.ViewQueryResponse<int>
– E.g. if you have a reduce that returns e.g. the sum of some mapped values.
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