Today Couchbase is happy to announce the GA release of the official LINQ provider for Couchbase Server and the hot query language for JSON documents, N1QL! The goal of the provider is to provide a simple, easy to use ORM/ODM that is closer to Linq2SQL than EntityFramework or NHibernate, which are verbose and can be complex.

While simplicity is the goal, don't underestimate the power of Linq2Couchbase; it's a fully functional Linq implementation with extended support for all of N1QL's awesome features!

In this post we will go over the basics of getting started with Linq2Couchbase, the major actors in the API, and integration with ASP.NET and Owin/Katana. In later posts we will go into more detail about specifics and details of Linq2Couchbase!

The Architecture

The provider is really just another layer over the SDK; the provider handles query parsing and query generation and the SDK handles the request and mapping of the results. The provider uses Re-linq internally to create an abstract syntax tree (AST) from the Linq query, which is then used to emit the N1QL statement. Note that Re-linq is used by both NHibernate and the EntityFramework, so your in good hands!

Getting Started

The source is available on Github and the package is available on NuGet; if you use NuGet package manager to install Linq2Couchbase all dependencies, including the Couchbase.NET SDK will be handled for you.

To install Linq2Couchbase using the NuGet package manager (assuming you have already created a Visual Studio project) open the package manager by right clicking the “Manage Nuget Packages” and searching for Linq2Couchbase or using the package manager command line:

Once you have done this the project will have all the necessary dependencies. Next you'll need to install Couchbase Server either locally or via VM's. The download link for Couchbase Server is here. For the VM's, use vagrants which uses Puppet and Vagrant to install a cluster of Couchbase servers. Make sure you install Couchbase 4.0! If you are using Vagrants, then provision up the cluster:

Once you have a Couchbase Server or cluster, setup up the Server or Cluster and make sure at least one node is an Index node and one node is a Query node. You will do this on the the first step of the “Server Setup” or when you add an additional server to your cluster. Also, add the “beer-sample” data set to the cluster during setup or from the Settings>Samples tab after you have setup up the cluster or instance.

Now that your Couchbase instance or cluster is setup, you will need to create a primary index in the “beer-sample” bucket. To do this either navigate C:Program FilesCouchbaseServerbin or if using vagrants (or linux) /opts/couchbase/binusing a command prompt. Then type either cbq or ./cbq (on linux) to start the query CIL and then:

This will create a primary index on the beer-sample bucket. Note the backticks “`”, these are required to escape the “-” in beer-sample. Now you are ready to write some code!

Creating the BucketContext

The main object for working with the bucket is the BucketContext. The BucketContext is analogous to the DataContext in Linq2Sql and the DbContext in the EntityFramework. It's primary purpose is to provide and interface for building queries and submitting them to the Couchbase server.

You can use the BucketContext as a stand-alone object or you can derive from it and create a strongly typed object that maps properties to the types in your bucket and domain model. In this example I will do the latter:

The beer-sample bucket (a bucket is similar to a database in a RDBMS system) contains documents that are “typed” as a “brewery” and a “beer”, this informal type system will allow to query the bucket and return either brewery documents or beer documents via a predicate(WHERE type=”beer” for example). In the code above we have defined explicit properties which return IQueryable

An Example Query

You will find that using Linq2Couchbase is pretty much identical to Linq2SQL or the EF:

Once you have a BucketContext reference all you do is query it like you would any other Linq provider. All Linq keywords are supported as well as N1QL constructs like ON KEYS, NEST and UNNEST! In a later post, we will go over all of this is much greater detail!

The Document Model

In the BeerSample context above, the Beer and Brewery objects will be the targets for our Linq projections and they correspond or map to equivalent JSON documents in our bucket (beer-sample). Here is a listing for each (note that this is partial listing, the classes in their entirety can be found here):

This of course maps to “beer” documents; note that DocumentTypeFilter attribute. This will “auto-magically” add a predicate or WHERE clause that filters by the type “beer” to every query that targets that document. The DocumentTypeFilter attribute is one of two ways to apply a filter, unless you manually add the predicate to each query.

This is the object that “brewery” documents will be mapped to. Note that there is no DocumentTypeFilter attribute explicitly defined; that is because the constructor for the BeerSample context will add the filter to the DocumentFilterManager. This is purely a different approach to the same problem; adding a predicate to a query to filter by type.

Integrating with ASP.NET or Owin/Katana

Their is a very distinct pattern for using the Couchbase .NET SDK in ASP.NET or Katana/OWin projects. Since the BucketContext uses the Couchbase .NET SDK, you will need to follow this pattern so that you have take advantage object caching within the SDK and shared TCP connections. Fortunately, its a very simple pattern:

Using Global.asax in ASP.NET

In an ASP.NET application using Global.asax, you will take advantage of the Application_Start and Application_End event handlers to create and destroy the Cluster and Bucket objects that the BucketContext depends on.

Here we are creating the configuration (btw this could come from the App.Config as well) and then initializing the ClusterHelper object. Finally when the application shuts down, we are destroying the long-lived Cluster and Bucket objects in the Application_End handler. This will be a graceful shutdown and OS level constructs will be returned back to the OS in a timely manner.

Using Setup.cs in Owin/Katana

In Owin/Katana hosted applications, we follow a similar pattern, we just use a different method, the Setup.cs class.

Here we create and initialize the ClusterHelper when the Configuration method runs at startup and then we register a delegate that will fire when the application shuts down, closing our ClusterHelper and freeing up resources.

Injecting into your Controllers

The BucketContext itself takes on the characteristics of the Unit of Work pattern; you can create one for each request and since the ClusterHelper manages the references (assuming you are following the advice above) the instance will simply be GC'd at the end of the request.

The simplest way to do this is by simply using Dependency Injection (the pattern) within your Controllers to create the instance when the controller is created, for example:

Now, you simply use the BucketContext within your Action methods:

Once again, since the context a short-lived lightweight object, you could scope it to the request and inject it there reusing it for all controllers invoked within that request.

What's coming up?

Very quickly, the next major feature will be Change Tracking using proxies. Additionally, expect bug fixes, performance enhancements and other featured aimed at making Linq2Couchbase a fully featured, lightweight ODM/ORM!

If there is a feature you want, a bug fix or perhaps you want to contribute we welcome all kinds of feedback both good and bad.

  • The Jira is here.
  • The Githb project is here.

Linq2Couchbase is an community-driven, open source project, so please take a look at it and if you would like to contribute, please do!

Special Thanks

A special thanks to all of those who contributed to the project (it is Open Source after all!), especially to Brant Burnett of Centeredge Software who contributed significantly to the project and documentation on NuGet!

Posted by Jeff Morris, Software Engineer, Couchbase

Leave a reply