Using C# with Desktopcouch == desktopcouchsharp

by mandel on April 30th, 2010

As I have mentioned several times, during my everyday job I deal with C# and although I every now and then I complain about it, I consider the language to be quite good overall. One of the things that has been bothering me is the lack of a lib to used Desktopcouch from C#! This is a pity since there are some great application on Gnome written on the Mono stack that will really could take advantage of Desktopcouch. I have noticed that most of the time people in the blog-sphere complaining about things, but really do not do anything to solve them. Trying to avoid to be a one of “those whiners” I give you desktopcouchsharp.

Basic API

Writing a nice API is nothing but easy and I’ve got to recognized that I’m not such a great hacker (and I wrote the lib in 2 days and spent 2/3 trying to compile the lib in Ubuntu ’til I got desperate and I used openSUSE on VirtualBox… sorry Ubuntu but it is for the greater good :( ). I used Divan as an initial starting point for my code. Although Divan is a great library, I have decided to use its code as the base/starting point for desktopcouchsharp

To be able to use desktopcouchsharp you have first to define your document classes, for example:

Address

using System;
using Newton.Json;
 
namespace Desktopcouch
{
    // define an enum for the address type.
    public enum AddressType{
        HOME,
        WORK,
        OTHER
    }
 
    // and address is not a document but we need to state how
    // to serialize it we use the attribute to let the serializer
    // know we want to add attributes
    // with info of how to serialize the object
    [JsonObject(MemberSerialization.OptIn)]
    public class Address
    {
         #region Serialized Properties
 
         [JsonProperty("country")]
         public string Country { get; set; }
 
         [JsonProperty("state")]
         public string State { get; set; }
 
         [JsonProperty("city")]
         public string City { get; set; }
 
         [JsonProperty("postalcode")]
         public string Postalcode { get; set; }
 
         [JsonProperty("street")]
         public string Street { get; set; }
 
         // because the type is an enum, we will let the
         // serializer know that we will provide him with
         //  the logic to convert it to the correct values
         // rather than ints
         [JsonProperty("type")]
         [JsonConverter(typeof(AddressTypeConverter))]
         public AddressType AddressType { get; set; }
 
         #endregion
 
         #region Constructors
 
         Address ()
         {
              // required for deserialize
         }
 
         #endregion
    }
}

AddressTypeConverter

The AddressTypeConverte lets the Serializer know how to serialize, deserialize our AddressType enum.

Contact

using System;
using Desktopcouch;
 
namespace MacacoExample
{
 
    public class Contact : Record{
 
        #region Properties
 
        [JsonProperty("first_name")]
        public string FirstName { get; set; }
 
        [JsonProperty("middle_name")]
        public string MiddleName { get; set; }
 
        [JsonProperty("last_name")]
        public string LastName { get; set; }
 
        [JsonProperty("addresses")]
        public MergeableList<Address> Addresses{ get; set; }
 
        #endregion
 
        #region Constructors
 
        Contact ()
            : base ("record_type_url"){
            // desrialization reasons
        }
 
        public Contact (string name, 
                            string middleName, 
                            string lastName)
            : base ("record_type_url") {
             Name = name;
             MiddleName= middleName;
             LastName = lastName; 
             Addresses = new MergeableList<Address>();
        }
        #endregion
    }// class
}// namesapce

DB usage

(I always use the contact example because is the most recognized record in desktopcouch for know… calendar talk in UDS? ;) )

There is a very important thing to mention, desktopcouchsharp it is not a ORM/ODM library, it is a library to be able to use Desktopcouch from C#. While using dictionary is a natural way to interact with Desktopcouch from python, the same cannot be said for C#. In the .Net realm the Object is the king (Clearly visible in the implementation of IronPython and its “late method binding”. Desktopcouchsharp will not create a proxy for you to use, it will not deduce how to serialize you objects etc… The idea of the lib is very simple, you create a document and you mark/decorate the properties to be serialize with the appropriate Json.Net attributes.

using System;
using Desktopcouch;
 
namespace Desktopcouch.Example
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			Console.WriteLine ("Create db with name sharp_test");
			var db = new DesktopcouchDatabase ("sharp_test", true);
			// create some contacts to add
			var first = new Contact ("Manuel", "", "de la Pena");
			var firstId = db.PutRecord (first);
			Console.WriteLine ("Added contact with id {0}", firstId);
			var second = new Contact ("Jaime", "", "de la Pena");
			var secondId = db.PutRecord (second);
			Console.WriteLine ("Added contact with id {0}", secondId);
			// Delete the db
			db.DeleteRecord (firstId);
			Console.ReadKey ();
		}
	}
}

Vie operations are possible through the use of the CouchDesignDocument object that allows to add and remove view.

var design = db.NewDesignDocument("contacts");
			design.AddView("by_letter", @"function(doc) {
        if (doc.record_type == 'http://www.freedesktop.org/wiki/Specifications/desktopcouch/contact') {
        function first_letter(name) {
            var first_letter = name.substring(0, 1);
            return first_letter.match(/[A-Za-z]/) ? first_letter.toLowerCase() : '0';
        }
 
        try {
            deleted = doc['application_annotations']['Ubuntu One']
                ['private_application_annotations']['deleted']}
        catch (e) {
            deleted = false;}
        if (! deleted) {
            var raw_last = doc.last_name || '';
            var raw_first = doc.first_name || '';
            var raw_company = doc.company || '';
 
            var name_to_test = raw_last || raw_first || raw_company;
 
            var emit_letter = first_letter(name_to_test);
            var emit_last = raw_last.toLowerCase();
            var emit_first = raw_first.toLowerCase();
            var emit_company = raw_company.toLowerCase();
 
            emit([emit_letter, emit_last, emit_first, emit_company], 1);}}}");
			// very important, otherwhise it will not be in the db
			design.Synch();
			var result = db.Query("contacts");
			Console.WriteLine("Count the result", result.Result.Count);

Record class

The record class is just a small abstract class that implements a number of basic properties to be implemented by records. There is no need to extend that class but you do have to implement the IRecord interface.

Constructors

I know that this is not the “most” POCO implementation but you DO have to implement an empty constructor for the classes that you have to serialize. The constructor does NOT have to be public, but it has to be present.

Collections

I believe that the serialization of collections deserves a special mention. As you might now the python implementation (that is, desktopcouch.records) will make sure that all the collections you use in your records are MergeableLists. This is a “trivial” thing to do in a dynamic language but not so easy in a dynamically typed language such as C#. Ideally I could have written the library in such a way that dictionaries that used collection will not be able to be serialized, or just create a runtime proxy that will use a MergeableList instead but that is not trivial and adds a big amount of overhead. Instead i have taken the decision to allow anyone to serialize their object in anyway and explicitly use a MergeableList when required. The serialization of any collection class in C# will be a json array (besides dictionaries of course!).

I nice way to make a more C# API for your objects will be to provide a non-public property used for serialization that has in its signature the MergeableList type and use a public one that uses a generic type such as IList:

Attachments

I have added the ability to add attachments to record just as the python lib does unfortunalty this has not yet been added to the lib (did I mention this is a version 0.1?)

Code

All code is available in launchpad (lp:desktopcouchsharp) under BSD license. I will be adding documentation in this page to ensure that people can easily use the code.

Rant

The compilation of the code has been a pain in the ass due to a bug in the Mono compiler version 2.4, if you want to compile the code you DO NEED 2.6 to do it, otherwise you will waste the time as I did.

From .Net/Mono

Leave a Reply

You must be logged in to post a comment.