couchbase-sdk-net-1.2.pdf

57
Couchbase Client Library: .NET (C#) 1.2

description

couchbase-sdk-net-1.2.pdf

Transcript of couchbase-sdk-net-1.2.pdf

Page 1: couchbase-sdk-net-1.2.pdf

Couchbase Client Library: .NET (C#) 1.2

Page 2: couchbase-sdk-net-1.2.pdf

Couchbase Client Library: .NET (C#) 1.2

Abstract

This is the manual for 1.2 of the Couchbase .NET client library, which is compatible with Couchbase Server 1.8.

This manual provides a reference to the key features and best practive for using the .NET Couchbase Client Library (Enyim). Thecontent constitutes a reference to the core API, not a complete guide to the entire API functionality.

Table 1. Product Compatibility for Couchbase SDK .NET

Product Compatible All Features

Couchbase Server 1.8 ✓ ✓

Couchbase Server 2.0 ✓ ✓

External Community Resources.

Wiki: .NET Client LibraryDownload Client Library.NET Client LibrarySDK Forum

Note. The following document is still in production, and is not considered complete or exhaustive.

Last document update: 10 Dec 2012 11:15; Document built: 10 Dec 2012 11:15.

Documentation Availability and Formats. This documentation is available online: HTML Online . For other documentation fromCouchbase, see Couchbase Documentation Library

Contact: [email protected] or couchbase.com

Copyright © 2010, 2011 Couchbase, Inc. Contact [email protected].

For documentation license information, see Section D.1, “Documentation License”. For all license information, see Appendix D, Licenses.

Page 3: couchbase-sdk-net-1.2.pdf

iii

Table of Contents1. Getting Started ....................................................................................................................................... 1

1.1. Get a Server ................................................................................................................................ 11.2. Get a Client Library ..................................................................................................................... 11.3. Try it Out! .................................................................................................................................. 1

1.3.1. Project Setup ..................................................................................................................... 11.3.2. Adding Configuration ......................................................................................................... 11.3.3. Instantiating the Client ........................................................................................................ 11.3.4. CRUD Operations .............................................................................................................. 21.3.5. Storing JSON Documents .................................................................................................... 31.3.6. CouchbaseClient JSON Extension Methods ............................................................................ 31.3.7. Working with Views .......................................................................................................... 5

2. Couchbase and ASP.NET MVC ................................................................................................................ 72.1. Prerequisites ................................................................................................................................ 72.2. Visual Studio Project Setup ............................................................................................................ 72.3. Working with Models ................................................................................................................... 72.4. Encapsulating Data Access ............................................................................................................. 82.5. Working with Views ..................................................................................................................... 92.6. The Views and Controller ............................................................................................................ 112.7. Brewery CRUD .......................................................................................................................... 122.8. Brewery Forms .......................................................................................................................... 142.9. Collated Views ........................................................................................................................... 162.10. Paging ..................................................................................................................................... 182.11. Spatial Indexes ......................................................................................................................... 232.12. Conclusion ............................................................................................................................... 25

3. .NET Method Summary .......................................................................................................................... 264. .Net Connection Operations ..................................................................................................................... 295. Store Operations ................................................................................................................................... 30

5.1. Store Methods ............................................................................................................................ 306. Retrieve Operations ............................................................................................................................... 32

6.1. Get Methods .............................................................................................................................. 327. Update Operations ................................................................................................................................. 34

7.1. Append Methods ........................................................................................................................ 367.2. Decrement Methods .................................................................................................................... 377.3. Remove Methods ........................................................................................................................ 397.4. Increment Methods ..................................................................................................................... 397.5. Prepend Methods ........................................................................................................................ 417.6. Touch Methods .......................................................................................................................... 427.7. Sync Methods ............................................................................................................................ 43

8. View/Query Interface ............................................................................................................................. 44A. Configuring Logging ............................................................................................................................. 46B. Configuring the .NET CLient Library ....................................................................................................... 48C. Release Notes ...................................................................................................................................... 49

C.1. Release Notes for 1.2.0 Couchbase Client Library .NET BA (12 December 2012) .................................. 49C.2. Release Notes for 1.2.0-BETA-3 Couchbase Client Library .NET Beta (28 November 2012) ..................... 49C.3. Release Notes for 1.2.0-DP4 Couchbase Client Library .NET Alpha (27 August 2012) ............................ 50C.4. Release Notes for 1.2.0-DP3 Couchbase Client Library .NET Alpha (27 August 2012) ............................ 51C.5. Release Notes for 1.2.0-DP2 Couchbase Client Library .NET Alpha (25 July 2012) ................................ 51C.6. Release Notes for 1.2-DP Couchbase Client Library .NET Alpha (27 March 2012) ................................. 51

D. Licenses .............................................................................................................................................. 52D.1. Documentation License ............................................................................................................... 52

Page 4: couchbase-sdk-net-1.2.pdf

iv

List of Figures2.1. Figure 1, New Project. .......................................................................................................................... 72.2. Figure 2, Select Empty Template. ........................................................................................................... 72.3. Figure 3, CouchbaseNetClient NuGet Package ........................................................................................... 72.4. Figure 4, CouchbaseModelViews NuGet Package ...................................................................................... 92.5. Figure 5, Couchbase web console Views tab ........................................................................................... 102.6. Figure 6, inflector_extensions NuGet package .......................................................................................... 102.7. Figure 7, Create the BreweriesController ................................................................................................ 112.8. Figure 8, Index view with list scaffolding ............................................................................................... 112.9. Figure 9, List breweries page ................................................................................................................ 122.10. Figure 10, Create the Edit view ........................................................................................................... 142.11. Figure 11, Create the Edit view ........................................................................................................... 142.12. Figure 12, Create the Details view ....................................................................................................... 152.13. Figure 13, Create the details view ........................................................................................................ 152.14. Figure 14, Paging .............................................................................................................................. 192.15. Figure 15, Add CountriesController ...................................................................................................... 202.16. Figure 16, Add Countries Index view ................................................................................................... 202.17. Figure 17, Brewery counts by country .................................................................................................. 212.18. Figure 18, Brewery counts by province ................................................................................................. 212.19. Figure 19, Brewery counts by city ....................................................................................................... 222.20. Figure 20, Brewery counts by poastal code ............................................................................................ 222.21. Figure 21, Breweries by poastal code ................................................................................................... 222.22. Figure 22, Mapped breweries .............................................................................................................. 25

Page 5: couchbase-sdk-net-1.2.pdf

v

List of Tables1. Product Compatibility for Couchbase SDK .NET .......................................................................................... 23.1. .NET Client Library ............................................................................................................................ 264.1. .Net Client Library Connection Methods ................................................................................................. 295.1. .NET Client Library Store Methods ........................................................................................................ 306.1. .NET Client Library Retrieve Methods ................................................................................................... 327.1. .NET Client Library Update Methods ..................................................................................................... 34

Page 6: couchbase-sdk-net-1.2.pdf

1

Chapter 1. Getting StartedThis chapter will get you started with using Couchbase Server and the .NET (C#) Client Library.

1.1. Get a ServerGet & Install Couchbase Server. Come back here when you're done

1.2. Get a Client LibraryIt can either be downloaded as a zipfile or run the following in the NuGet Package Manger console:

PM> Install-Package CouchbaseNetClient

Note

Note that the current NuGet release is compatible with Couchbase Server 1.8.x. When the 1.2 clientlibrary is released, the NuGet package will be refreshed.

1.3. Try it Out!

1.3.1. Project Setup

Create a new console project in Visual Studio. Add a references to the Couchbase.dll and Enyim.Memcached.dll assem-blies found in the preview release zip file.

Visual Studio console applications target the .NET Framework Client Profile by default, so you'll need to update theproject properties to instead target the full .NET Framework. If you skip this step, you'll have compilation errors.

1.3.2. Adding Configuration

You can configure your Couchbase client either programmatically or using the app.config file with the appropriate Couch-base config section. Using app.config is more flexible and is the preferred approach. Modify your app.config file as fol-lows:

<?xml version="1.0"?><configuration> <configSections> <section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/> </configSections> <couchbase> <servers bucket="default" bucketPassword=""> <add uri="http://192.168.0.2:8091/pools"/> <add uri="http://192.168.0.3:8091/pools"/> </servers> </couchbase></configuration>

The URIs in the servers list are used by the client to obtain information about the cluster configuration. If you're runningon your local dev machine, include only a single URI using 127.0.0.1 as the address.

The default Couchbase Server installation creates a bucket named "default" without a password, therefore the bucket andbucketPassword attributes are optional. If you created an authenticated bucket, you should specify those values in place ofthe default settings above.

1.3.3. Instantiating the Client

Add the following using statements to Program.cs:

Page 7: couchbase-sdk-net-1.2.pdf

Getting Started

2

using Couchbase;using Enyim.Caching.Memcached;

Couchbase is the namespace containing the client and configuration classes with which you'll work.Enyim.Caching.Memcached contains supporting infrastructure. Recall that Couchbase supports the Memcached protocoland is therefore able to make use of the popular Enyim Memcached client for many of its core key/value operations.

Next create an instance of the client in the Main method. Use the default constructor, which depends on the configurationfrom app.config.

var client = new CouchbaseClient();

In practice, it's expensive to create clients. The client incurs overhead as it creates connection pools and sets up the threadto get cluster configuration. Therefore, the best practice is to create a single client instance, per bucket, per AppDomain.Creating a static property on a class works well for this purpose. For example:

public static class CouchbaseManager{ private readonly static CouchbaseClient _instance; static CouchbaseClient() { _instance = new CouchbaseClient(); }

public static CouchbaseClient Instance { get { return _instance; } }}

However, for the purpose of this getting started guide the locally scoped client variable created above is sufficient.

1.3.4. CRUD Operations

The primary CRUD API used by the .NET Client is that of a standard key/value store. You create and update documentsby supplying a key and value. You retrieve or remove documents by supplying a value. For example, consider the JSONdocument that you'll find in the "beer-sample" bucket that's available when you install Couchbase Server and setup yourcluster. The key for this document is "110fc0f765."

{ "name": "Sundog", "abv": 5.25, "ibu": 0, "srm": 0, "upc": 0, "type": "beer", "brewery_id": "110f1bb0ee", "updated": "2010-07-22 20:00:20", "description": "Sundog is an amber ale as deep as the copper glow of a Lake Michigan sunset. Its biscuit malt give Sundog a toasty character and a subtle malty sweetness. Sundog brings out the best in grilled foods, caramelized onions, nutty cheese, barbecue, or your favorite pizza.", "style": "American-Style Amber/Red Ale", "category": "North American Ale"}

To retrieve this document, you simply call the Get method of the client.

var savedBeer = client.Get("110fc0f765");

If you add a line to print the savedBeer to the console, you should see a JSON string that contains the data above. If youadd a line to print the savedBeer to the console, you should see a JSON string that contains the data above.

var savedBeer = client.Get("110fc0f765");Console.WriteLine(savedBeer);

In this example, savedBeer would be of type Object. To get a string back instead and avoid having to cast, simply use thegeneric version of Get.

var savedBeer = client.Get<string>("110fc0f765");

To add a document, first create a JSON string.

Page 8: couchbase-sdk-net-1.2.pdf

Getting Started

3

var newBeer =@"{ ""name"": ""Old Yankee Ale"", ""abv"": 5.00, ""ibu"": 0, ""srm"": 0, ""upc"": 0, ""type"": ""beer"", ""brewery_id"": ""110a45622a"", ""updated"": ""2012-08-30 20:00:20"", ""description"": "".A medium-bodied Amber Ale"", ""style"": ""American-Style Amber"", ""category"": ""North American Ale""}";

For a key, we'll simply compute a SHA-1 hash of our document and then grab the first 10 characters, formatted to lower-case and without dashes. The exact mechanism by which you create your keys need only be consistent. If you are going toquery documents by key (not just through views) you should choose predictable keys (e.g., beer_Old_Yankee_Ale).

var key = BitConverter.ToString(HashAlgorithm.Create("SHA1") .ComputeHash(Encoding.UTF8.GetBytes(newBeer))) .Replace("-", "").Substring(0, 10).ToLower();

With this new key, the JSON document may easily be stored.

var result = client.Store(StoreMode.Add, "5c26a734c9", newBeer);

There are three arguments to Store. The first is the store mode. Use StoreMode.Add for new keys, StoreMode.Replace toupdate existing keys and StoreMode.Set to add when a key doesn’t exist or to replace it when it does. Store will fail if Addis used with an existing key or Replace is used with a non-existent key. The second and third arguments are the key andvalue, respectively. The return value, assigned to the result variable, is a Boolean indicating whether the operation suc-ceeded.

Removing a document simply entails calling the Remove method with the key to be removed. Like the other methodswe've seen so far, Remove returns a Boolean indicating operation success.

var result = client.Remove("5c26a734c9");

1.3.5. Storing JSON Documents

While storing and retreiving JSON strings is a straightforward process, documents in a typical application are likely atsome point to be represented by domain objects (i.e., POCOs). More mileage will come from storing some representationof these data objects. For example, the beer documents could be represented by an instance of a Beer class in memory.The .NET Client Library will allow for serializable objects to be persisted using .NET's standard over-the-wire serializa-tion. However, on the server, these objects will be stored as binary attachments to a JSON document. The impact of beingan attachment is that it will not be indexed in a view. A better solution then, is to serialize data objects to JSON strings be-fore storing them and deserializing JSON document strings to objects when retreiving them.

1.3.6. CouchbaseClient JSON Extension Methods

If you want an easy way to read and write JSON, the CouchbaseClientExtensions class under the Couchbase.Extensionsnamespace provides two very basic methods, StoreJson and GetJson. Both methods depend on the open sourceNewtonsoft.Json library, which is already a dependency of the Couchbase .NET Library. Both methods wrap only themost basic Get and Store overloads and don't currently support CAS or TTL operations. They are included with the libraryfor convenience and will likely be augmented in the future by a Couchbase Labs extension library.

To improve the way beer data is managed in this getting started project, add a new file Beer.cs to the project. It will con-tain a plain-old-CLR-object (POCO) with mappings from class properties to JSON properties. For brevity, some documentproperties have been omitted. Notice also that the Type property has been made read-only and forces all beer instances tobe marked with the type "beer." This type information will be useful when creating views and wanting to find all "beer"documents.

Page 9: couchbase-sdk-net-1.2.pdf

Getting Started

4

public class Beer{ [JsonProperty("name")] public string Name { get; set; }

[JsonProperty("abv")] public float ABV { get; set; }

[JsonProperty("type")] public string Type { get { return "beer"; } }

[JsonProperty("brewery_id")] public string BreweryId { get; set; }

[JsonProperty("style")] public string Style { get; set; }

[JsonProperty("category")] public string Category { get; set; }}

By default, Json.NET will serialize the properties of your class in the case you created them. Because we want our proper-ties to match the casing of the documents in the beer-sample bucket, we're going to set JSON property names in JsonProp-erty attributes (in the Newtonsoft.Json namespace). Again, we could store instances of this Beer class without convertingthem first to JSON (requires marking the class with a Serializable attribute), but that would prevent those documents frombeing indexed in views.

Persisting an instance as JSON is similar to how we persisted the JSON document string above. Replace the code where aJSON string was created with the code below.

var newBeer = new Beer{ Name = "Old Yankee Ale", ABV = 5.00f, BreweryId = "110a45622a", Style = "American-Style Amber", Category = "North American Ale"};

And to store the new instance, simply use the extension method. Result will return a Boolean indicating operation success.

var result = client.StoreJson(StoreMode.Set, key, newBeer);

Retrieving the Beer instance is also similar to retrieving a document as was demonstrated above.

var savedBeer = client.GetJson<beer><Beer>(key);</beer>

At this point, your simple Program.cs file should look something like the following:

class Program{ static void Main(string[] args) { var client = new CouchbaseClient(); var key = "110fc0f765";

var newBeer = new Beer { Name = "Old Yankee Ale", ABV = 5.00f, BreweryId = "110a45622a", Style = "American-Style Amber", Category = "North American Ale" };

var result = client.StoreJson(StoreMode.Set, key, newBeer);

if (result)

Page 10: couchbase-sdk-net-1.2.pdf

Getting Started

5

{ var savedBeer = client.GetJson<Beer>(key); Console.WriteLine("Found beer: " + savedBeer.Name); }

}}

1.3.7. Working with Views

Map/Reduce Views are used to create queryable, secondary indexes in Couchbase Server. The primary index for docu-ments is the key you use when performing the standard CRUD methods described above. See the view documentation formore information on writing views.

For this example, the by_name view in the beer design document will be queried. This view simply checks whether a doc-ument is a beer and has a name. If it does, it emits the beer's name into the index. This view will allow for beers to bequeried for by name. For example, it's now possible to ask the question "What beers start with A?"

function (doc, meta) { if (doc.type && doc.type == "beer" && doc.name) { emit(doc.name, null); }}

Querying a view through the .NET Client Library requires calling the GetView method and providing the name of the de-sign document and the name of the view.

var view = client.GetView("beer", "by_name");

The return type of GetView is an enumerable IView, where each enumerated value is an IViewRow. The actual viewquery isn't run until you enumerate over the view. For example, if you wanted to print out each of the keys that have beenindexed, you could use the IViewRow instance's Info dictionary. This particular view emits null as the value, so that willbe empty when this snippet runs.

foreach (var row in view){ Console.WriteLine("Key: {0}, Value: {1}", row.Info["key"], row.Info["value"]);}

The code above should give you a list of beer names for all beer documents that exist in the beer-sample bucket. If youwant to filter that list, there are fluent methods that may be chained off of the IView instance before iterating over it. Mod-ifying the GetView call above as follows will find all beers whose names start with "A" and limits the results to 50 rows.See the API reference for other fluent methods. Please note that these methods return an IView instance, which is an IEnu-merable, but is not an IQueryable. Therefore, using LINQ extension methods on the IView will not reduce the results inthe query. Only the IView fluent methods will affect the query before it is run.

var view = client.GetView("beer", "by_name").StartKey("A").EndKey("B").Limit(50);

Also included in the IViewRow instance, is the original ID (the key from the k/v pair) of the document. It is accessible byway of the IViewRow's ItemId property. Taking that ID, it is possible to retrieve the original document. Using the JSONextension methods, it's also possible to get a Beer instance for each row. If it seems expensive to perform these lookups,recall that Couchbase Server has a Memcached layer built in and these queries are unlikely to be pulling data from disk.The documents are likely to be found in memory.

foreach (var row in view){ var doc = client.GetJson<Beer>(row.ItemId); Console.WriteLine(doc.Name);}

Finally, there is a generic version of GetView which encapsulates the details of the view row data structures. To retrieveBeer instances automatically by ID as you iterate over the view, you need to add the generic parameter to GetView alongwith the third Boolean argument to tell the client to perform the by ID lookup. If you omit the third parameter, the client

Page 11: couchbase-sdk-net-1.2.pdf

Getting Started

6

will attempt to deserialize the value emitted by the index into an instance of the specified generic type. Again, in this ex-ample the value was null. Therefore, deserialization must be done by way of ID lookup.

var view = client.GetView<Beer>("beer", "by_name", true).StartKey("A").EndKey("B").Limit(50);

foreach (var beer in view){ Console.WriteLine(beer.Name);}

Page 12: couchbase-sdk-net-1.2.pdf

7

Chapter 2. Couchbase and ASP.NET MVCThis tutorial will walk you through the steps of creating an ASP.NET MVC app using Couchbase. It will focus on practi-cal design patterns, best practices along with general SDK usage.

2.1. PrerequisitesThis tutorial assumes that you have Visual Studio 2010 installed, along with ASP.NET MVC 4. You may use any editionof Visual Studio or you may use Visual Web Developer. Visual Studio 2012 will also work for this tutorial, but the screen-shots included will be from Visual Studio 2012 Professional.

You will also need to have an installation of Couchbase Server 2.0 and have obtained the latest Couchbase .NET ClientLibrary, version 1.2 or higher. See “Getting Started” for more information on client installation.

You also may use an older version of ASP.NET MVC if you do not have MVC 4 installed, but as with using Visual WebDeveloper or Visual Studio 2012, the templates shown in the screenshots will vary from what you see.

You should also have installed the beer-sample database on your Couchbase Server. If you haven't, simply open the webconsole and navigate to the “Settings” tab. There, you'll find an option to add a sample bucket to your cluster.

2.2. Visual Studio Project SetupThis project will be based on an ASP.NET MVC 4 application template. After opening Visual Studio, select File -> NewProject and then select Web -> ASP.NET MVC 4 Application under the Visual C# project templates. Name the project“CouchbaseBeersWeb” and click “OK” to create the solution.

Figure 2.1. Figure 1, New Project.

Start with an “Empty” application using the Razor view engine for the MVC template.

Figure 2.2. Figure 2, Select Empty Template.

Next you'll need to add a reference to the Couchbase .NET Client Library. You could either download the assembliesfrom the getting started pageor obtain them using the NuGet package manager. When you install via Nuget, your projectwill automatically get references to Couchbase.dll, Enyim.Caching.dll and the dependencies Newtonsoft.Json.dll andHammock.dll. These assemblies are also found in the zip file and should be referenced in your project if not using Nuget.

Figure 2.3. Figure 3, CouchbaseNetClient NuGet Package

2.3. Working with ModelsThe first task to solve is displaying a list of breweries in from our beer-sample bucket. To add this functionality, there issome plumbing to setup in our application. These tasks are enumerated below.

• Create a Brewery model class to represent beer documents

• Create a BreweryRepository to encapsulate data access for Brewery instances

• Create a BreweriesController with an Index action used to show a Brewery list

• Create a Razor view to display our list of breweries

Page 13: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

8

As a JSON document database, Couchbase supports a natural mapping of domain objects to data items. In other words,there's very little difference between the representation of your data as a class in C# and the representation of your data asa document in Couchbase. Your object becomes the schema defined in the JSON document.

When working with domain objects that will map to documents in Couchbase, it's useful, but not required, to define abase class from which your model classes will derive. This base class will be abstract and contain two properties, “Id” and“Type.”

Right click on the “Models” directory and add a new class named “ModelBase” and include the following code.

public abstract class ModelBase{ public virtual string Id { get; set; } public abstract string Type { get; }}

Note that the Type method is abstract and readonly. It will be implemented by subclasses simply by returning a hard-cod-ed string, typically matching the class name, lower-cased. The purpose of the Type property is to provide taxonomy to theJSON documents stored in your Couchbase bucket. The utility will be more obvious when creating views.

Next, create a new class named in the “Models” directory of your project. This class will be a plain old CLR object(POCO) that simply has properties mapping to the properties of brewery documents in the beer-sample bucket. It will alsoextend ModelBase.

public class Brewery : ModelBase{ public string Name { get; set; } public string City { get; set; } public string State { get; set; } public string Code { get; set; } public string Country { get; set; } public string Phone { get; set; } public string Website { get; set; } public DateTime Updated { get; set; } public string Description { get; set; } public IList<string> Addresses { get; set; } public IDictionary<string, object> Geo { get; set; } public override string Type { get { return "brewery"; } }}

{ "name": "Thomas Hooker Brewing", "city": "Bloomfield", "state": "Connecticut", "code": "6002", "country": "United States", "phone": "860-242-3111", "website": "http://www.hookerbeer.com/", "type": "brewery", "updated": "2010-07-22 20:00:20", "description": "Tastings every Saturday from 12-6pm, and 1st and 3rd Friday of every month from 5-8.", "address": [ "16 Tobey Road" ], "geo": { "accuracy": "RANGE_INTERPOLATED", "lat": 41.8087, "lng": -72.7108 }}

2.4. Encapsulating Data Access

After creating the Brewery class, the next step is to create the data access classes that will encapsulate our CouchbaseCRUD and View operations. Create a new file in “Models” named “RepositoryBase`1.cs” with a class name of “Reposito-

Page 14: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

9

ryBase.” This will be an abstract class, generically constrained to work with ModelBase instances. Note the “`1” suffixon the file name is a convention used for generic classes in C# projects.

public abstract class RepositoryBase<T> where T : ModelBase{ //CRUD methods}

The process of creating an instance of a CouchbaseClient is expensive. There is a fair amount of overhead as theclient establishes connections to the cluster. It is therefore recommended to minimize the number of times that a client in-stance is created in your application. The simplest approach is to create a static property or singleton that may be accessedfrom data access code. Using the RepositoryBase, setting up a protected static property will provide access for sub-classes.

public abstract class RepositoryBase<T> where T : ModelBase{ protected static CouchbaseClient _Client { get; set; } static RepositoryBase() { _Client = new CouchbaseClient(); }}

The code above requires setting configuration in web.config. It is of course equally valid to define the configuration incode if that is your preference. See getting started for more details.

<configSections> <section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/></configSections><couchbase> <servers bucket="beer-sample"> <add uri="http://127.0.0.1:8091/pools"/> </servers> </couchbase>

2.5. Working with ViewsTo display a list of all breweries, a view will be necessary. This map function for this view will simply emit null keys andvalues for each of the brewery documents in the database. This view will live in a “breweries” design document and benamed “all.”

function(doc, meta) { if (doc.type == "brewery") { emit(null, null); }}

A null-key index still provides access to each of the document's keys when the view is queried. Note however that rangequeries on keys would not be supported with this view.

You could create the “all” view above by creating a new design document in the Couchbase web console or you could usethe CouchbaseCluster API found in Couchbase.dll to create and to save a design document. However, an easier ap-proach is to use the CouchbaseLabs project Couchbase Model Views.

The Couchbase Model Views project is not part of the Client Library, but makes use of its design doc management API tocreate views from attributes placed on model classes. Using NuGet, add a reference to the CouchbaseModelViews pack-age.

Figure 2.4. Figure 4, CouchbaseModelViews NuGet Package

Once installed, modify the Brewery class definition to have two class level attributes, CouchbaseDesignDoc andCouchbaseAllView.

Page 15: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

10

[CouchbaseDesignDoc("breweries")][CouchbaseAllView]public class Brewery : ModelBase{ //props omitted for brevity}

The CouchbaseDesignDoc attribute instructs the Model Views framework to create a design document with the givenname. The CouchbaseAllView will create the “all” view as shown previously.

To get the Model Views framework to create the design doc and view, you'll need to register the assembly containing themodels with the framework. In Global.asax, create a static RegisterModelViews method for this purpose.

public static void RegisterModelViews(IEnumerable<Assembly> assemblies){ var builder = new ViewBuilder(); builder.AddAssemblies(assemblies.ToList()); var designDocs = builder.Build(); var ddManager = new DesignDocManager(); ddManager.Create(designDocs);}

Then in the existing Application_Start method, add a line to register the current assembly.

RegisterModelViews(new Assembly[] { Assembly.GetExecutingAssembly() });

Note that in production, you will likely not want to recreate design docs each time your app starts, since that will retriggerindex creation. An alternative would be to place this code in an admin controller of some sort.

To test that the Model Views framework is working, simply run the application (Ctrl + F5). If all went well, you should beable to navigate to the “Views” tab in the Couchbase web console and see the new design doc and view in the “ProductionViews” tab (as shown below).

Figure 2.5. Figure 5, Couchbase web console Views tab

If you click the link next to the “Filter Results” button, you will see the JSON that is returned to the CouchbaseClientwhen querying a view. Notice the “id” property found in each row. That is the key that was used to store the document.

{"total_rows":1412,"rows":[{"id":"21st_amendment_brewery_cafe","key":null,"value":null},{"id":"357","key":null,"value":null},{"id":"3_fonteinen_brouwerij_ambachtelijke_geuzestekerij","key":null,"value":null},{"id":"512_brewing_company","key":null,"value":null},{"id":"aass_brewery","key":null,"value":null},{"id":"abbaye_de_leffe","key":null,"value":null},{"id":"abbaye_de_maredsous","key":null,"value":null},{"id":"abbaye_notre_dame_du_st_remy","key":null,"value":null},{"id":"abbey_wright_brewing_valley_inn","key":null,"value":null},{"id":"aberdeen_brewing","key":null,"value":null}]}

With the view created, the next step is to modify the RepositoryBase to have a GetAll method. This method willuse some conventions to allow for reuse across subclasses. One of those conventions is that queries will be made to designdocs with camel-cased and pluralized names (e.g., Brewery to breweries). To aid in the pluralization process, create a ref-erence to inflector_extension using NuGet. Note that in .NET 4.5, there is a PluralizationService class that willprovide some of the same support.

Figure 2.6. Figure 6, inflector_extensions NuGet package

To the RepositoryBase class, add a readonly private field and initialize it to the inflected and pluralized name of thetype of T. The inflector extension methods will require an additional using statement.

Page 16: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

11

using inflector_extension;private readonly string _designDoc;public RepositoryBase(){ _designDoc = typeof(T).Name.ToLower().InflectTo().Pluralized;}

The initial implementation of GetAll will simply return all breweries using the generic GetView<T> method ofCouchbaseClient. The third parameter instructs CouchbaseClient to retrieve the original document rather thandeserialize the value of the view row.

public virtual IEnumerable<T> GetAll(){ return _Client.GetView<T>(_designDoc, "all", true);}

RepositoryBase is a generic and abstract class, so obviously it cannot be used directly. Create a new class in “Mod-els” named “BreweryRepository.” The code for this class is very minimal, as it will rely on its base class for most func-tionality.

public class BreweryRepository : RepositoryBase<Brewery>{}

2.6. The Views and ControllerWith the models and repository coded, the next step is to create the controller. Right click on the “Controllers” directoryin the project and select Add -> Controller. Name the controller “BreweriesController” and select the template “Controllerwith empty read/write actions,” which will create actions for creating, updating, deleting, showing and listing breweries.

Figure 2.7. Figure 7, Create the BreweriesController

The Index method of the BreweriesController will be used to display the list of breweries. To allow the new con-troller to access brewery data, it will need an instance of a BreweryRepository. Create a public property of typeBreweryRepository and instantiate it in the default constructor.

public BreweryRepository BreweryRepository { get; set; }public BreweriesController(){ BreweryRepository = new BreweryRepository();}

Then inside of the Index method, add a call to BreweryRepository's GetAll method and pass its results to theview as its model.

public ActionResult Index(){ var breweries = BreweryRepository.GetAll(); return View(breweries);}

The last step to displaying the list of breweries is to create the Razor view (as in MVC views, not Couchbase views). Inthe “Views” directory, create a new directory named “Breweries.” Right click on that new directory and select “Add” ->“View.” Name the view “Index” and create it as a strongly typed (to the Brewery class) view with List scaffolding. Thistemplate will create a Razor view that loops over the brewery results, displaying each as a row in an HTML table.

Figure 2.8. Figure 8, Index view with list scaffolding

At this point, you should build your application and navigate to the Breweries path (e.g., http://localhost:52962/breweries).If all went well, you should see a list of breweries.

Page 17: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

12

Figure 2.9. Figure 9, List breweries page

There are quite a few breweries being displayed in this list. Paging will be an eventual improvement, but for now limitingthe results by modifying the defaults of the GetAll method will be sufficient.

//in RepositoryBasepublic IEnumerable<T> GetAll(int limit = 0){ var view = _Client.GetView<T>(_designDoc, "all", true); if (limit > 0) view.Limit(limit); return view;}//in BreweriesControllerpublic ActionResult Index(){ var breweries = BreweryRepository.GetAll(50); return View(breweries);}

2.7. Brewery CRUDThe MVC scaffolding that created the Razor template to list breweries also included links to create, show, edit and deletebreweries. Using more scaffolding, these CRUD features are easily implemented.

Create and Update methods require a bit of effort to encapsulate. One decision to make is whether to use the detailed re-sult ExecuteStore method or the Boolean Store method of the Client. ExecuteStore returns an instance of an IS-toreOperationResult, which contains a success status and error message properties, among others.

Since it is likely important to know whether operations succeeded, ExecuteStore will be used in our Repository-Base. However, that interface will be hidden from the application and instead a two item Tuple will be returned by eachmethod. The first item is a Boolean indicating success or failure and the second item contains possible error messagesfrom the server. The signatures are shown below.

public virtual Tuple<bool, string> Create(T value){}public virtual Tuple<bool, string> Update(T value) {}public virtual Tuple<bool, string> Save(T value) {}

There are other implementation details that need to be considered when implementing these methods, namely key creationand JSON serialization.

CRUD operations in Couchbase are performed using a key/value API. The key that is used for these operations may be ei-ther meaningful (i.e., human readable) or arbitrary (e.g., a GUID). When made human readable, your application may beable to make use of predictable keys to perform key/value get operations (as opposed to secondary indexes by way of viewoperations).

A common pattern for creating readable keys is to take a unique property, such as Brewery.Name, and replace itsspaces, possibly normalizing to lowercase. So “Thomas Hooker Brewery” becomes “thomas_hooker_brewery.”

Add the following BuildKey method to the RepositoryBase to allow for default key creation based on the Id prop-erty.

protected virtual string BuildKey(T model){ if (string.IsNullOrEmpty(model.Id)) { return Guid.NewGuid().ToString(); } return model.Id.InflectTo().Underscored;}

BuildKey will default to a GUID string when no Id is provided. It's also virtual so that subclasses are able to override thedefault behavior. The BreweryRepository needs to override the default behavior to provide a key based on breweryname.

Page 18: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

13

protected override string BuildKey(Brewery model){ return model.Name.InflectTo().Underscored;}

When storing a Brewery instance in Couchbase Server, it first has to be serialized into a JSON string. An important con-sideration is how to map the properties of the Brewery to properties of the JSON document.

JSON.NET (from Newtonsoft.Json) will by default serialize all properties. However, ModelBase objects all have an Idproperty that shouldn't be serialized into the stored JSON. That Id is already being used as the document's key (in the key/value operations), so it would be redundant to store it in the JSON.

JSON.NET supports various serialization settings, including which properties should be included in serialization. InRepositoryBase, create a serializAndIgnoreId method and a private DocumentIdContractResolverclass as shown below.

private string serializeAndIgnoreId(T obj){ var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings() { ContractResolver = new DocumentIdContractResolver(), }); return json;}private class DocumentIdContractResolver : CamelCasePropertyNamesContractResolver{ protected override List<MemberInfo> GetSerializableMembers(Type objectType) { return base.GetSerializableMembers(objectType).Where(o => o.Name != "Id").ToList(); }}

The DocumentIdContractResolver will prevent the Id property from being saved into the JSON. It also extendsCamelCasePropertyNamesContractResolver to provide camel-cased properties in the JSON output.

Note that there is a JsonIgnore attribute that could be added to properties that should be omitted from the serializedJSON, however it is less global in its application. For example, if a class overrides the Id property of ModelBase, itwould have to add the attribute.

With this new plumbing in place, it's now possible to complete the Create, Update and Save methods.

public virtual Tuple<bool, string> Create(T value){ var result = _Client.ExecuteStore(StoreMode.Add, BuildKey(value), serializeAndIgnoreId(value)); return Tuple.Create (result.Success, result.Message);}public virtual Tuple<bool, string> Update(T value){ var result = _Client.ExecuteStore(StoreMode.Replace, value.Id, serializeAndIgnoreId(value)); return Tuple.Create (result.Success, result.Message);}public virtual Tuple<bool, string>Save(T value){ var key = string.IsNullOrEmpty(value.Id) ? BuildKey(value) : value.Id; var result = _Client.ExecuteStore(StoreMode.Set, key, serializeAndIgnoreId(value)); return Tuple.Create (result.Success, result.Message);}

The Get method of RepositoryBase requires similar considerations. CouchbaseClient.ExecuteGet returnsan IGetOperationResult. To be consistent with the goal of not exposing Couchbase SDK plumbing to the app, Getwill also return a Tuple. Notice also that the Id property of the model is set to the value of the key, since it's not beingstored in the JSON.

public virtual Tuple<T, bool, string> Get(string key){ var result = _Client.ExecuteGet<string>(key); if (result.Value == null)

Page 19: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

14

{ return Tuple.Create(default(T), result.Success, result.Message); } var model = JsonConvert.DeserializeObject<T>(result.Value); model.Id = key; return Tuple.Create(model, result.Success, result.Message);}

Completing the CRUD operations is the Delete method. Delete will also hide its SDK result data structure (IRe-moveOperationResult).

public virtual Tuple<bool, string> Delete(string key){ var result = _Client.ExecuteRemove(key); return Tuple.Create(result.Success, result.Message);}

2.8. Brewery Forms

With the new methods implemented, it's time to create the scaffolding for the CRUD forms. The first task will be to createan edit form. Open the BreweriesController and locate the Edit methods that were generated by the Add Con-troller wizard.

In the HTTP GET override of Edit, modify it as shown below. This action will retrieve the Brewery and pass it to theview as the model. Note the change from an int id parameter to a string id parameter.

public ActionResult Edit(string id){ var brewery = BreweryRepository.Get(id).Item1; return View(brewery);}

Update the Edit method that handles POSTs as shown below. Validation and error handling are intentionally being omit-ted for brevity.

[HttpPost]public ActionResult Edit(string id, Brewery brewery){ BreweryRepository.Update(brewery); return RedirectToAction("Index");}

The edit form will be created using scaffolding, as was the case with the listing page. Right click on the “Breweries” fold-er in the “Views” directory and click Add -> View. Name the view “Edit” and strongly type it to a Brewery with Edit scaf-folding.

Figure 2.10. Figure 10, Create the Edit view

Rebuild the application and return to the brewery listing page. Click on an “Edit” link and you should see the edit formloaded with the details for that brewery. Edit some values on the form and click save. You should see your changes per-sisted on the listing page.

Figure 2.11. Figure 11, Create the Edit view

The Details action looks very much like Edit. Get the Brewery and provide it as the model for the view.

public ActionResult Details(string id){ var brewery = BreweryRepository.Get(id).Item1; return View(brewery);}

Page 20: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

15

Create a scaffolding form for Details using the same process as was used with Edit.

Figure 2.12. Figure 12, Create the Details view

Rebuild and return to the list page. Click on a "Details" link. You should see a page listing the data for that brewery.

Figure 2.13. Figure 13, Create the details view

The Create and Edit actions of the BreweriesController are quite similar, save for the fact that Create's GETmethod doesn't provide a model to the view. Again, error handling and validation are being omitted for brevity's sake.

public ActionResult Create(){ return View();}[HttpPost]public ActionResult Create(Brewery brewery){ BreweryRepository.Create(brewery); return RedirectToAction("Index");}

Go through the scaffolding process again to add a create view for the Create action. Rebuild and click the “CreateNew” link on the list page to test the new form. Breweries (for now) are sorted by key and limited to 50, so you might notsee yours in the list. If you want to verify your create action worked, use brewery name that starts with a numeric value(e.g., 123 Brewery).

Another reason you wouldn't see your new brewery appear in the list of breweries is that the view is set to allow stale(eventually consistent) results. In other words, the incremental update to the “all” index would be performed after thequery. If you refresh, you should see your brewery in the list.

Allowing the breweries to be sorted by key is convenient, since the key is based on the breweries name. However, if case-sensitivity is important in sorting or the key creation strategy changes, then explicitly sorting on the brewery's name is abetter idea. To that end, creating a new view indexed on the Brewery name is the right approach.

The new map function will look similar to the “all” map function, but will add tests on “doc.name” and will emit thedoc.name as the key.

function(doc, meta) { if (doc.type == "brewery" && doc.name) { emit(doc.name, null); }}

If you are using the web console to manage your design documents, save the map function above as “by_name” in the“breweries” design document. If you are using the Model Views framework, add an attribute to the Name property ofBrewery>. Then compile and run your application.

Adding the CouchbaseViewKey attribute will create the view above. The first argument is the name of the view. Thesecond is the name of the JSON document property to emit as key.

[CouchbaseViewKey("by_name", “name”)]public string Name { get; set; }

The next step is to replace the GetAll call with a call to the new view. First, add a protected method in RepositoryBasethat returns a typed view instance, set with the design doc for that model type. The isProjection flag is set when the typeof T does not properties of the JSON to properties of the class. It must be used with explicit JSON.NET mappings.

protected IView<T> GetView(string name, bool isProjection = false){

Page 21: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

16

return _Client.GetView<T>(_designDoc, name, ! isProjection);}

Then in BreweryRepository, implement GetAllByName as shown below. This new method simply returns theview, optionally allowing a limit and stale results.

public IEnumerable<Brewery> GetAllByName(int limit = 0, bool allowStale = false){ var view = GetView("by_name"); if (limit > 0) view.Limit(limit); if (! allowStale) view.Stale(StaleMode.False); return view;}

Next, modify the BreweriesController so that the Index action calls the new GetAllByName method.

public ActionResult Index(){ var breweries = BreweryRepository.GetAllByName(50); return View(breweries);}

Compile and run your application. The list page might be ordered a little differently as the sample database did scrubsome keys of punctuation and other non-word or non-digit characters. Also now (because of the stale setting), if you cre-ate a new Brewery, it should appear after a redirect and should not require a refresh.

Note that it is still possible that the view didn't consider the new Brewery when it was executed with state set to false. Ifthe document hadn't persisted to disk before the index was updated, it wouldn't have been included.

If that level of consistency is important to your application, you should use an overload of ExecuteStore that includesdurability requirements. See the documentation on ExecuteStore for more information.

The last piece required to complete the CRUD functionality for breweries is to implement the delete form. Update theDelete actions in BreweriesController as shown below.

public ActionResult Delete(string id){ var brewery = BreweryRepository.Get(id).Item1; return View(brewery);}[HttpPost]public ActionResult Delete(string id, Brewery brewery){ BreweryRepository.Delete(id); return RedirectToAction("Index");}

To create the delete form, simply go through the Add View process again and choose scaffolding for delete (don't forget tochoose the Brewery model).

2.9. Collated Views

At this point, you've written a full CRUD app for breweries in the beer-sample database. Another optimization we mightwant to include is to show the names of beers that belong to a particular brewery. In the relational world, this is typicallyaccomplished using a join between two tables. In Couchbase, the solution is to use a collated view.

Before looking at the map function for this view, it's useful to inspect a beer document.

{ "name": "Old Stock Ale 2004", "abv": 11.4, "ibu": 0, "srm": 0, "upc": 0, "type": "beer", "brewery_id": "north_coast_brewing_company",

Page 22: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

17

"updated": "2010-07-22 20:00:20", "description": "", "style": "Old Ale", "category": "British Ale"}

Note the “brewery_id” property. This is the key of a brewery document and can be thought of as a “foreign key.” Notethat this type of document foreign key relationship is not enforced by Couchbase.

The basic idea behind a collated view is to produce an index in which the keys are ordered so that a parent id appears first,followed by its children. In the beer-sample case that means a brewery appears in a row followed by rows of beers.

The basic algorithm for the map function is to check the doc.type. If a brewery is found, emit its key (meta.id). If a childis found, emit its parent id (brewery_id). The map function for the view “all_with_beers” is shown below.

function(doc, meta) { switch(doc.type) { case "brewery": emit([meta.id, 0]); break; case "beer": if (doc.name && doc.brewery_id) { emit([doc.name, doc.brewery_id, 1], null); } }}

The trick to ordering properly the parent/child keys is to use a composite key in the index. Parent ids are paired with a 0and children with a 1. The collated order of the view results is shown conceptually below.

A Brewery, 0A Brewery, 1A Brewery, 1B Brewery, 0B Brewery, 1

To use Model Views to create this view, simply add an attribute to an overridden Id property on the Brewery class.

[CouchbaseCollatedViewKey("all_with_beers", "beer", "brewery_id", "name")]public override string Id { get; set; }

This is a good time to introduce a simple Beer class, of which Brewery will have a collection. Create a new model classnamed "Beer." For now, include only the Name property.

public class Beer : ModelBase{ public string Name { get; set; } public override string Type { get { return "beer"; } }}

Then add a Beer list property to Brewery. This property shouldn't be serialized into the doc, so add the JsonIgnoreattribute.

private IList<Beer> _beers = new List<Beer>();[JsonIgnore]public IList<Beer> Beers{ get { return _beers; } set { _beers = value; }}

Since the collated view has a mix of beers and breweries, the generic GetView<T> method won't work well for dese-rializing rows. Instead, we'll use the GetView method that returns IViewRow instances. First add a new GetViewRawmethod to RepositoryBase.

protected IView<IViewRow> GetViewRaw(string name)

Page 23: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

18

{ return _Client.GetView(_designDoc, name);}

Then in BreweryRepository, add a GetWithBeers method to build the object graph. This new method performs arange query on the view, starting with the brewery id and including all possible beer names for that brewery.

public Tuple<Brewery, bool, string> GetWithBeers(string id){ var rows = GetViewRaw("all_with_beers") .StartKey(new object[] { id, 0 }) .EndKey(new object[] { id, "\uefff", 1 }) .ToArray(); var result = Get(rows[0].ItemId); result.Item1.Beers = rows.Skip(1) .Select(r => new Beer { Id = r.ItemId, Name = r.ViewKey[1].ToString() }) .ToList(); return result;}

Update the Details method of BreweriesController to use this method.

public ActionResult Details(string id){ var brewery = BreweryRepository.GetWithBeers(id).Item1; return View(brewery);}

Before the closing fieldset tag in details template, add a block of Razor code to display the beers.

<div class="display-field">Beers</div> <div> @foreach (var item in Model.Beers) { <div style="margin-left:10px;">- @item.Name</div> }</div>

2.10. PagingThe final feature to implement on the brewery CRUD forms is paging. It's important to state up front that paging inCouchbase does not work like paging in a typical RDBMS. Though views have skip and limit filters that could be usedcreate the standard paging experience, it's not advisable to take this approach.

The skip filter still results in a read of index data starting with the first row of the index. For example, if an index has5000 rows and skip is set to 500 and limit is set to 50, 500 records are read and 50 returned. Instead, linked-list style pagi-nation is the recommended approach. Paging should also consider the document ids because keys may collide. However,in the breweries example, paging on name is safe because name is the source of the unique key.

First add an HTML footer to the list table in the Index view, right before the final closing table tag. There is a link back tothe first page and links to the previous and next pages. A default page size of 10 is also used. Each time the page is ren-dered, it sets the previous key to the start key of the previous page. The next key will be explained shortly.

<tr> <td colspan="4"> @Html.ActionLink("List", "Index", new { pagesize = 10 }) @Html.ActionLink("< Previous", "Index", new { startKey = Request["previousKey"], pagesize = Request["pagesize"] ?? "10" }) @Html.ActionLink("Next >", "Index", new { startKey = ViewBag.NextStartKey, previousKey = ViewBag.StartKey, pagesize = Request["pagesize"] ?? "10"}) </td></tr>

Modify the GetAllByName method in BreweryRepository to be able to handle range queries (startkey, endkey).

public IEnumerable<Brewery> GetAllByName(string startKey = null, string endKey = null, int limit = 0, bool allowStale = false){ var view = GetView("by_name"); if (limit > 0) view.Limit(limit); if (! allowStale) view.Stale(StaleMode.False);

Page 24: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

19

if (! string.IsNullOrEmpty(startKey)) view.StartKey(startKey); if (! string.IsNullOrEmpty(endKey)) view.StartKey(endKey); return view;}

For the actual paging, modify the BreweryController's Index method to keep track of pages. The trick is to selectpage size + 1 from the view. The last element is not rendered, but its key is used as the start key of the next page. In sim-pler terms, the start key of the current page is the next page's previous key. The last element's key is not displayed, but isused as the next page's start key.

public ActionResult Index(string startKey, string nextKey, int pageSize = 25){ var breweries = BreweryRepository.GetAllByName(startKey: startKey, limit: pageSize+1); ViewBag.StartKey = breweries.ElementAt(0).Name; ViewBag.NextStartKey = breweries.ElementAt(breweries.Count()-1).Name; return View(breweries.Take(pageSize));}

Figure 2.14. Figure 14, Paging

At this point, breweries may be created, detailed (with Children), listed, updated and deleted. The next step is to look atthe brewery data from a different perspective, namely location.

Brewery documents have multiple properties related to their location. There are state and city properties, as well as de-tailed geospatial data. The first question to ask of the data is how many breweries exist for a given country. Then withineach country, the counts can be refined to see how many breweries are in a given state, then city and finally zip code. Allof these questions will be answered by the same view.

Create a view named “by_country” with the code below. This view will not consider documents that don’t have all loca-tion properties. The reason for this restriction is so that counts are accurate as you drill into the data.

function (doc, meta) { if (doc.country && doc.state && doc.city && doc.code) { emit([doc.country, doc.state, doc.city, doc.code], null); }}

For this view, you’ll also want a reduce function, which will count the number of rows for a particular grouping by count-ing how many rows appear for that grouping. So for example, when the group_level parameter is set to 2 brewery countswill be returned by city and state. For an analogy, think of a SQL statement selecting a COUNT(*) and having a GROUPBY clause with city and state columns.

Couchbase has three built in reduce functions - _count, _sum and _stats. For this view, _count and _sum will perform thesame duties. Emitting a 1 as a value means that _sum would sum the 1s for a grouping. _count would simply count 1 foreach row, even with a null value.

If you are using Model Views, then simply add CouchbaseViewKeyCount attributes to each of the properties thatshould be produced in the view.

[CouchbaseViewKeyCount("by_country", "country", 0, null)]public string Country { get; set; }

[CouchbaseViewKeyCount("by_country", "state", 1)]public string State { get; set; }

[CouchbaseViewKeyCount("by_country", "city", 2)]public string City { get; set; }

[CouchbaseViewKeyCount("by_country", "code", 3)]public string Code { get; set; }

This view demonstrates how to create ordered, composite keys from domain object properties using the Model Viewsframework.

Page 25: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

20

The next step is to modify the BreweryRepository to include methods that will return aggregated results grouped atthe appropriate levels. This new method will return key value pairs where the key is the lowest grouped part of the keyand the value is the count. Also add an enum for group levels.

public IEnumerable<KeyValuePair<string, int>> GetGroupedByLocation(BreweryGroupLevels groupLevel, string[] keys = null){ var view = GetViewRaw("by_country") .Group(true) .GroupAt((int)groupLevel); if (keys != null) { view.StartKey(keys); view.EndKey(keys.Concat(new string[] { "\uefff" })); } foreach (var item in view) { var key = item.ViewKey[(int)groupLevel-1].ToString(); var value = Convert.ToInt32(item.Info["value"]); yield return new KeyValuePair<string, int>(key, value); }}

Create a new controller named "CountriesController" to contain the actions for the new grouped queries. Use the emptycontroller template.

Figure 2.15. Figure 15, Add CountriesController

Modify the new controller to include the code below, which sets up the BreweryRepositoryReference and loadssends the view results to the MVC View.

public class CountriesController : Controller{ public BreweryRepository BreweryRepository { get; set; } public CountriesController() { BreweryRepository = new BreweryRepository(); } public ActionResult Index() { var grouped = BreweryRepository.GetGroupedByLocation(BreweryGroupLevels.Country); return View(grouped); }}

Next create a new directory under “Views” named “Countries.” Add a view named “Index” that is not strongly typed.

Figure 2.16. Figure 16, Add Countries Index view

To the new view, add the Razor code below, which will simply display the keys and values as a list. It also links to theProvinces action, which you’ll create next.

@model dynamic<h2>Brewery counts by country</h2><ul>@foreach (KeyValuePair<string, int> item in Model){ <li> @Html.ActionLink(item.Key, "Provinces", new { country = item.Key}) (@item.Value) </li>}</ul>

Build and run your application and you should see a page like below.

Page 26: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

21

Figure 2.17. Figure 17, Brewery counts by country

Next, add the Provinces action to the CountriesController. This action will reuse the repository method, butwill change the group level to Province (2) and pass the selected country to be used as a key to limit the query results.

public ActionResult Provinces(string country){ var grouped = BreweryRepository.GetGroupedByLocation( BreweryGroupLevels.Province, new string[] { country } ); return View(grouped);}

Create another empty view named “Provinces” in the “Countries” directory under the “Views” directory. Include the con-tent below, which is similar to the index content.

@model dynamic<h2>Brewery counts by province in @Request["country"]</h2><ul>@foreach (KeyValuePair<string, int> item in Model){ <li> @Html.ActionLink(item.Key, "Cities", new { country = Request["country"], province = item.Key}) (@item.Value) </li>}</ul>

Compile and run the app. You should see the Provinces page below.

Figure 2.18. Figure 18, Brewery counts by province

Creating the actions and views for cities and codes is a similar process. Modify CountriesController to includenew action methods as shown below.

public ActionResult Cities(string country, string province){ var grouped = BreweryRepository.GetGroupedByLocation( BreweryGroupLevels.City, new string[] { country, province }); return View(grouped);}public ActionResult Codes(string country, string province, string city){ var grouped = BreweryRepository.GetGroupedByLocation( BreweryGroupLevels.PostalCode, new string[] { country, province, city }); return View(grouped);}

Then add a view named "Cities" with the Razor code below.

@model dynamic<h2>Breweries counts by city in @Request["province"], @Request["country"]</h2><ul>@foreach (KeyValuePair<string, int> item in Model){ <li> @Html.ActionLink(item.Key, "Codes", new { country = Request["country"], province = Request["province"], city = item.Key}) (@item.Value) </li>}</ul>

Then add a view named "Codes" with the Razor code below.

Page 27: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

22

@model dynamic<h2>Brewery counts by postal code in @Request["city"], @Request["province"], @Request["country"]</h2><ul>@foreach (KeyValuePair<string, int> item in Model){ <li> @Html.ActionLink(item.Key, "Details", new { country = Request["country"], province = Request["province"], city = Request["city"], code = item.Key}) (@item.Value) </li>}</ul>@Html.ActionLink("Back to Country List", "Index")

Compile and run the app. Navigate through the country and province listings to the cities listing. You should see the pagebelow.

Figure 2.19. Figure 19, Brewery counts by city

Click through to the codes page and you should see the page below.

Figure 2.20. Figure 20, Brewery counts by poastal code

The last step for this feature is to display the list of breweries for a given zip code. To implement this page, you need toadd a new method to BreweryRepository named GetByLocation. This method will use the same view that we’vebeen using, except it won’t execute the reduce step. Not executing the reduce step means that the results come back un-grouped and individual items are returned.

public IEnumerable<Brewery> GetByLocation(string country, string province, string city, string code){ return GetView("by_country").Key(new string[] { country, province, city, code }).Reduce(false);}

Then add a Details action method to the BreweriesController that calls this method and returns its results to theview.

public ActionResult Details(string country, string province, string city, string code){ var breweries = BreweryRepository.GetByLocation(country, province, city, code); return View(breweries);}

Create a Details view in the “Countries” folder with the Razor code below.

@model IEnumerable<CouchbaseBeersWeb.Models.Brewery><h2>Breweries in @Request["code"], @Request["city"], @Request["province"], @Request["country"]</h2><ul>@foreach (var item in Model){ <li> @Html.ActionLink(item.Name, "Details", "Breweries", new { id = item.Id }, new { }) </li>}</ul>@Html.ActionLink("Back to Country List", "Index")

Compile and run the app. Click through country, province and state on to the Codes view. The code above already has alink to this new Details page. When you click on a postal code, you should see a list of breweries as below.

Figure 2.21. Figure 21, Breweries by poastal code

Page 28: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

23

2.11. Spatial IndexesThe last feature to add to the brewery app is the ability to search for breweries using its longitude and latitude. The ex-perimental spatial indexes in Couchbase allow for bounding box searches. Spatial indexes are created by emitting a Geo-JSON document as the key. This document must contain a coordinates property to be indexed.

Using the web console, create a new spatial view (click “Add Spatial View”) in the breweries design document named“points” using the spatial function below. Note that spatial views do not use the same map/reduce process as standardviews.

function (doc, meta) { if (doc.type == "brewery" && doc.geo.lng && doc.geo.lat) { emit({ "type": "Point", "coordinates": [doc.geo.lng, doc.geo.lat]}, null);

}}

If you are using Model Views, then you’ll need to modify the Brewery class. Currently, the Model Views frameworkdoesn’t support object graph navigation, so you’ll need to flatten the “geo” property of the JSON document into theBrewery as shown below. These flattened properties provide Model Views with a way to build the spatial index.

[JsonIgnore]public string GeoAccuracy{ get { return Geo != null && Geo.ContainsKey("accuracy") ? Geo["accuracy"] as string : ""; }}[CouchbaseSpatialViewKey("points", "geo.lng", 0)][JsonIgnore]public float Longitude{ get { return Geo != null && Geo.ContainsKey("lng") ? Convert.ToSingle(Geo["lng"]) : 0f; }}[CouchbaseSpatialViewKey("points", "geo.lat", 1)][JsonIgnore]public float Latitude{ get { return Geo != null && Geo.ContainsKey("lat") ? Convert.ToSingle(Geo["lat"]) : 0f; }}

Next, RepositoryBase should be modified to provide support for generic views. As with GetView andGetViewRaw, these new methods are to provide some code reuse to subclasses.

protected virtual ISpatialView<T> GetSpatialView(string name, bool isProjection = false){ return _Client.GetSpatialView<T>(_designDoc, name, !isProjection);}protected virtual ISpatialView<ISpatialViewRow> GetSpatialViewRaw(string name){ return _Client.GetSpatialView(_designDoc, name);}

Then update BreweryRepository with a method to call the new “points” view. Spatial views expect a bounding boxwith lower left and upper right coordinates, ordered longitude then latitude. The UI will work with a delimited string, sothose points must be parsed and parsed as floats.

public IEnumerable<Brewery> GetByPoints(string boundingBox){ var points = boundingBox.Split(',').Select(s => float.Parse(s)).ToArray(); return GetSpatialView("points").BoundingBox(points[0], points[1], points[2], points[3]);}

Page 29: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

24

Then add a new class named “LocationsController” to the “Controllers” folder using the code below.

public class LocationsController : Controller{ public BreweryRepository BreweryRepository { get; set; } public LocationsController() { BreweryRepository = new BreweryRepository(); } [HttpGet] public ActionResult Details() { return View(); } [HttpPost] public ActionResult Details(string bbox) { var breweriesByPoints = BreweryRepository.GetByPoints(bbox) .Select(b => new { id = b.Id, name = b.Name, geo = new float[] { b.Longitude, b.Latitude } }); return Json(breweriesByPoints); }}

Most of the code above is boilerplate. A BreweryRepository is declared and initialized in the default constructor.The Details action that handles GET requests simply returns the view. The Details request that handles POST requestscalls the new BreweryRepository method and renders a JSON array of brewery projections that will be used in the view.

Next create a new Views folder called “Locations” and add a new view named “Details” to it. This new view will makeuse of Nokia’s HERE location services API. You can register for free at http://here.com. Add the Razor and JavaScriptcode below to your view.

@model CouchbaseBeersWeb.Models.Brewery<style type="text/css"> #mapContainer { width: 80%; height: 768px; margin-left:10%; margin-right:10%; }</style><script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.3/jsl.js?with=all"></script><h2>View Breweries</h2><div id="mapContainer"></div><script type="text/ecmascript"> nokia.Settings.set("appId", "<YOUR APP ID>"); nokia.Settings.set("authenticationToken", "<YOUR TOKEN>"); var mapContainer = document.getElementById("mapContainer"); var map = new nokia.maps.map.Display(mapContainer, { center: [41.763309, -72.67408], zoomLevel: 8, components: [ new nokia.maps.map.component.Behavior() ] }); $().ready(function () { var loadBreweries = function () { var mapBoundingBox = map.getViewBounds(); var queryBoundingBox = mapBoundingBox.topLeft.longitude + "," + mapBoundingBox.bottomRight.latitude + "," +mapBoundingBox.bottomRight.longitude + "," + mapBoundingBox.topLeft.latitude; $.post("@Url.Action("Details")", { bbox: queryBoundingBox }, function (data) { var coordinates = new Array(); $.each(data, function (idx, item) { var coordinate = new nokia.maps.geo.Coordinate(item.geo[1], item.geo[0]); var standardMarker = new nokia.maps.map.StandardMarker(coordinate); map.objects.add(standardMarker); }); }); };

Page 30: couchbase-sdk-net-1.2.pdf

Couchbase and ASP.NET MVC

25

map.addListener("dragend", function (evt) { loadBreweries(); }); loadBreweries();});</script>

The details of the HERE API are beyond the scope of this tutorial. The basic idea though is that when the map is ren-dered, the bounding box coordinates are obtained and passed (via AJAX) to the Details POST method on the Location-sController. The coordinates that come back are used to render points on the map via a standard marker.

Compile and run these last changes. Navigate to /locations/details and you should see a map such as the one shown be-low.

Figure 2.22. Figure 22, Mapped breweries

2.12. Conclusion

At this point, the brewery features are complete. Creating a set of pages for the beer documents is a similar exercise that isleft to the reader. Using scaffolding and reusing the patterns from working with breweries, it shouldn’t take much effort tobuild those features.

The code for this sample app is on GitHub at https://github.com/couchbaselabs/beer-sample-net. It contains all the codefrom this tutorial, plus the beer pages. It also contains some very minor style and navigation improvements (such as ahome page).

Finally, a single tutorial can address only so many concerns. Clearly some shortcuts were taken with validation, excep-tion handling and the like. Certain architectural patterns, such as dependency injection and MVVM were also omittedfor the sake of brevity. The intent of this tutorial was to provide an intermediate introduction to Couchbase developmentwith .NET. Your app should be able to make use of some or all of the patterns described.

Page 31: couchbase-sdk-net-1.2.pdf

26

Chapter 3. .NET Method Summary

Table 3.1. .NET Client Library

Method Title

object.Append(key, value) Append a value to an existing key

object.Append(key, casunique, value) Append a value to an existing key

object.ExecuteAppend(key, value) Append a value to an existing key and return a concat oper-ation result

object.ExecuteAppend(key, casunique,value)

Append a value to an existing key and return a concat oper-ation result

object.Cas(storemode, key, value) Compare and set a value providing the supplied CAS keymatches

object.Cas(storemode, key, value, ca-sunique)

Compare and set a value providing the supplied CAS keymatches

object.Cas(storemode, key, value, ex-piresat, casunique)

Compare and set a value using the specified key with a spe-cific expiry time

object.Cas(storemode, key, value, valid-for, casunique)

Compare and set the specified key with expiry time

object.ExecuteCas(storemode, key, value) Store a value using the specified key and return a store op-eration result

object.ExecuteCas(storemode, key, value,casunique)

Compare and set a value using the specified key and returna store operation result

object.ExecuteCas(storemode, key, value,expiresat, casunique)

Compare and set a value using the specified key with a spe-cific expiry time and return a store operation result

object.ExecuteCas(storemode, key, value,validfor, casunique)

Compare and set a value using the specified key with expirytime and return a store operation result

object.new CouchbaseClient([ url ] [,username ] [, password ])

Create connection to Couchbase Server

object.Decrement(key, defaultvalue, off-set)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, casunique)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, expiresat, casunique)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, validfor, casunique)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, expiresat)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, validfor)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, casunique)

Decrement the value of an existing numeric key

Page 32: couchbase-sdk-net-1.2.pdf

.NET Method Summary

27

Method Title

object.ExecuteDecrement(key, defaultval-ue, offset, expiresat, casunique)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, validfor, casunique)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, expiresat)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, validfor)

Decrement the value of an existing numeric key

object.ExecuteRemove(key) Delete a key/value and return a remove operation result

object.Remove(key) Delete a key/value

object.ExecuteGet(key, expiry) Get a get operation result and update the expiration time fora given key

object.Get(key, expiry) Get a value and update the expiration time for a given key

object.ExecuteGet(key) Get a single value and return a get operation result

object.ExecuteGet(keyarray) Get multiple get operation result values

object.Get(key) Get a single value

object.Get(keyarray) Get multiple values

object.GetWithCas(key) Get a single value with Cas

object.ExecuteIncrement(key, defaultval-ue, offset)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, casunique)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, expiresat, casunique)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, validfor, casunique)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, expiresat)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, validfor)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, casunique)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, expiresat, casunique)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, validfor, casunique)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, expiresat)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, validfor)

Increment the value of an existing numeric key

Page 33: couchbase-sdk-net-1.2.pdf

.NET Method Summary

28

Method Title

object.ExecutePrepend(key, value) Prepend a value to an existing key and return a concat oper-ation result

object.ExecutePrepend(key, casunique,value)

Prepend a value to an existing key and return a concat oper-ation result

object.Prepend(key, value) Prepend a value to an existing key

object.Prepend(key, casunique, value) Prepend a value to an existing key

object.ExecuteStore(storemode, key, val-ue)

Store a value using the specified key and return a store op-eration result

object.ExecuteStore(storemode, key, val-ue, expiresat)

Store a value using the specified key with a specific expirytime and return a store operation result

object.ExecuteStore(storemode, key, val-ue, validfor)

Store a value using the specified key with expiry time andreturn a store operation result

object.Store(storemode, key, value) Store a value using the specified key

object.Store(storemode, key, value, ex-piresat)

Store a value using the specified key with a specific expirytime

object.Store(storemode, key, value,validfor)

Store a value using the specified key with expiry time

object.Touch(key, expiry) Update the expiry time of an item

Page 34: couchbase-sdk-net-1.2.pdf

29

Chapter 4. .Net Connection OperationsTable 4.1. .Net Client Library Connection Methods

Method Title

object.new CouchbaseClient([ url ] [,username ] [, password ])

Create connection to Couchbase Server

API Call object.new CouchbaseClient([ url ] [, username ] [, pass-word ])

Description Create a connection to Couchbase Server with given parameters, such as node URL. MostSDKs accept a list of possible URL's to avoid an error in case one node is down. After ini-tial connection a Couchbase Client uses node topology provided by Couchbase Server toreconnect after failover or rebalance.

Returns (none)

Arguments

string url URL for Couchbase Server Instance, or node.

string username Username for Couchbase bucket.

string password Password for Couchbase bucket.

The easiest way to specify a connection, or a pool of connections is to provide it in the App.config file of your .Netproject. By doing so, you can change the connection information without having to recompile. You can updateApp.config in Visual Studio as follows:

<servers bucket="private" bucketPassword="private"> <add uri="http://10.0.0.33:8091/pools/default"/> <add uri="http://10.0.0.34:8091/pools/default"/></servers>

You should change the URI above to point at your server by replacing 10.0.0.33 with the IP address or hostname of yourCouchbase server machine. Be sure you set your bucket name and password. You can also set the connection to use thedefault bucket, by setting the bucket attribute to default and leaving the bucketPassword attribute empty. In thiscase we have configured the server with a bucket named 'private' and with a password 'private.'

Connections that you create with the .Net SDK are also thread-safe objects; for persisted connections, you can use a con-nection pool which contains multiple connection objects. You should create only a single static instance of a Couchbaseclient per bucket, in accordance with .Net framework. The persistent client will maintain connection pools per server node.For more information, see MSDN: AppDomain Class

Page 35: couchbase-sdk-net-1.2.pdf

30

Chapter 5. Store OperationsThe Couchbase .NET Client Library store operations set information within the Couchbase database. These are distinctfrom the update operations in that the key does not have to exist within the Couchbase database before being stored.

Table 5.1. .NET Client Library Store Methods

Method Title

object.ExecuteStore(storemode, key, val-ue)

Store a value using the specified key and return a store op-eration result

object.ExecuteStore(storemode, key, val-ue, expiresat)

Store a value using the specified key with a specific expirytime and return a store operation result

object.ExecuteStore(storemode, key, val-ue, validfor)

Store a value using the specified key with expiry time andreturn a store operation result

object.Store(storemode, key, value) Store a value using the specified key

object.Store(storemode, key, value, ex-piresat)

Store a value using the specified key with a specific expirytime

object.Store(storemode, key, value,validfor)

Store a value using the specified key with expiry time

5.1. Store MethodsThe Store() methods adds a value to the database with the specified key, but will fail if the key already exists in the data-base and the StoreMode is set to Add.

API Call object.Store(storemode, key, value)

Description Store a value using the specified key, whether the key already exists or not. Will overwritea value if the given key/value already exists.

Returns Boolean ( Boolean (true/false) )

Arguments

StoreMode storemode Storage mode for a given key/value pair

string key Document ID used to identify the value

object value Value to be stored

Note

JavaScript can store numbers up to a maximum size of 2 53 . If you are storing 64-bit integers with-in Couchbase and want to use the numbers through the Map/Reduce engine, numbers larger than 2 53

should be stored as a string to prevent number rounding errors.

The Store() method is used to persist new values by key. Any class decorated with the Serializable attribute maybe stored.

StoreMode.Set will behave like StoreMode.Add when the key doesn't exist and StoreMode.Replace when it does.

client.Store(StoreMode.Add, "beer", new Beer() { Brewer = "Thomas Hooker Brewing Company", Name = "American Ale"});

API Call object.Store(storemode, key, value, validfor)

Page 36: couchbase-sdk-net-1.2.pdf

Store Operations

31

Description Store a value using the specified key, whether the key already exists or not. Will overwritea value if the given key/value already exists.

Returns Boolean ( Boolean (true/false) )

Arguments

StoreMode storemode Storage mode for a given key/value pair

string key Document ID used to identify the value

object value Value to be stored

TimeSpan validfor Expiry time (in seconds) for key

client.Store(StoreMode.Set, "beer", new Beer() { Brewer = "Peak Organic Brewing Company", Name = "IPA"}, TimeSpan.FromSeconds(60));

API Call object.Store(storemode, key, value, expiresat)

Description Store a value using the specified key, whether the key already exists or not. Will overwritea value if the given key/value already exists.

Returns Boolean ( Boolean (true/false) )

Arguments

StoreMode storemode Storage mode for a given key/value pair

string key Document ID used to identify the value

object value Value to be stored

DateTime expiresat Explicit expiry time for key

client.Store(StoreMode.Replace, "beer", new Beer() { Brewer = "Six Point Craft Ales", Name = "Righteous Rye"}, DateTime.Now.Addhours(1));

Page 37: couchbase-sdk-net-1.2.pdf

32

Chapter 6. Retrieve OperationsThe retrieve operations get information from the Couchbase database. A summary of the available API calls is listed be-low.

Table 6.1. .NET Client Library Retrieve Methods

Method Title

object.ExecuteGet(key, expiry) Get a get operation result and update the expiration time fora given key

object.Get(key, expiry) Get a value and update the expiration time for a given key

object.ExecuteGet(key) Get a single value and return a get operation result

object.ExecuteGet(keyarray) Get multiple get operation result values

object.Get(key) Get a single value

object.Get(keyarray) Get multiple values

object.GetWithCas(key) Get a single value with Cas

6.1. Get MethodsThe Get() methods allow for direct access to a given key/value pair.

API Call object.Get(key)

Description Get one or more key values

Returns Object ( Binary object )

Arguments

string key Document ID used to identify the value

var beer = client.Get("beer") as Beer;

The generic form of the Get method allows for retrieval without the need to cast. If the stored type cannot be serialized tothe generic type provided, an InvalidCastException will be thrown.

var beer = client.Get<Beer>("beer");

API Call object.Get(keyarray)

Description Get one or more key values

Returns Object ( Binary object )

Arguments

List <string> ke-yarray

Array of keys used to reference one or more values.

Calling Get() with multiple keys returns a dictionary with the associated values.

client.Store(StoreMode.Set, "brewer", "Cottrell Brewing Co.");client.Store(StoreMode.Set, "beer", "Old Yankee Ale");

var dict = client.Get(new string[] { "brewer", "beer" });Console.WriteLine(dict["brewer"]);Console.WriteLine(dict["beer"]);

API Call object.Get(key, expiry)

Page 38: couchbase-sdk-net-1.2.pdf

Retrieve Operations

33

Description Get a value and update the expiration time for a given key

Returns (none)

Arguments

string key Document ID used to identify the value

object expiry Expiry time for key. Values larger than 30*24*60*60 seconds (30 days) are interpreted asabsolute times (from the epoch).

Calling the Get() method with a key and a new expiration value will cause get and touch operations to be performed.

var val = client.Get("beer", DateTime.Now.AddMinutes(5));

Page 39: couchbase-sdk-net-1.2.pdf

34

Chapter 7. Update OperationsThe update methods support different methods of updating and changing existing information within Couchbase. A list ofthe available methods is listed below.

Table 7.1. .NET Client Library Update Methods

Method Title

object.Append(key, value) Append a value to an existing key

object.Append(key, casunique, value) Append a value to an existing key

object.ExecuteAppend(key, value) Append a value to an existing key and return a concat oper-ation result

object.ExecuteAppend(key, casunique,value)

Append a value to an existing key and return a concat oper-ation result

object.Cas(storemode, key, value) Compare and set a value providing the supplied CAS keymatches

object.Cas(storemode, key, value, ca-sunique)

Compare and set a value providing the supplied CAS keymatches

object.Cas(storemode, key, value, ex-piresat, casunique)

Compare and set a value using the specified key with a spe-cific expiry time

object.Cas(storemode, key, value, valid-for, casunique)

Compare and set the specified key with expiry time

object.ExecuteCas(storemode, key, value) Store a value using the specified key and return a store op-eration result

object.ExecuteCas(storemode, key, value,casunique)

Compare and set a value using the specified key and returna store operation result

object.ExecuteCas(storemode, key, value,expiresat, casunique)

Compare and set a value using the specified key with a spe-cific expiry time and return a store operation result

object.ExecuteCas(storemode, key, value,validfor, casunique)

Compare and set a value using the specified key with expirytime and return a store operation result

object.Decrement(key, defaultvalue, off-set)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, casunique)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, expiresat, casunique)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, validfor, casunique)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, expiresat)

Decrement the value of an existing numeric key

object.Decrement(key, defaultvalue, off-set, validfor)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, casunique)

Decrement the value of an existing numeric key

Page 40: couchbase-sdk-net-1.2.pdf

Update Operations

35

Method Title

object.ExecuteDecrement(key, defaultval-ue, offset, expiresat, casunique)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, validfor, casunique)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, expiresat)

Decrement the value of an existing numeric key

object.ExecuteDecrement(key, defaultval-ue, offset, validfor)

Decrement the value of an existing numeric key

object.ExecuteRemove(key) Delete a key/value and return a remove operation result

object.Remove(key) Delete a key/value

object.ExecuteIncrement(key, defaultval-ue, offset)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, casunique)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, expiresat, casunique)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, validfor, casunique)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, expiresat)

Increment the value of an existing numeric key

object.ExecuteIncrement(key, defaultval-ue, offset, validfor)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, casunique)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, expiresat, casunique)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, validfor, casunique)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, expiresat)

Increment the value of an existing numeric key

object.Increment(key, defaultvalue, off-set, validfor)

Increment the value of an existing numeric key

object.ExecutePrepend(key, value) Prepend a value to an existing key and return a concat oper-ation result

object.ExecutePrepend(key, casunique,value)

Prepend a value to an existing key and return a concat oper-ation result

object.Prepend(key, value) Prepend a value to an existing key

object.Prepend(key, casunique, value) Prepend a value to an existing key

object.Touch(key, expiry) Update the expiry time of an item

Page 41: couchbase-sdk-net-1.2.pdf

Update Operations

36

7.1. Append Methods

The Append() methods allow you to add information to an existing key/value pair in the database. You can use this toadd information to a string or other data after the existing data.

The Append() methods append raw serialized data on to the end of the existing data in the key. If you have previous-ly stored a serialized object into Couchbase and then use Append, the content of the serialized object will not be extend-ed. For example, adding an List of integers into the database, and then using Append() to add another integer will re-sult in the key referring to a serialized version of the list, immediately followed by a serialized version of the integer. Itwill not contain an updated list with the new integer appended to it. De-serialization of objects that have had data append-ed may result in data corruption.

API Call object.Append(key, value)

Description Append a value to an existing key

Returns Object ( Binary object )

Arguments

string key Document ID used to identify the value

object value Value to be stored

The Append() method appends information to the end of an existing key/value pair.

The sample below demonstrates how to create a csv string by appending new values.

client.Store(StoreMode.Set, "beers", "Abbey Ale");Func<string, byte[]> stringToBytes = (s) => Encoding.Default.GetBytes(s);client.Append("beers", new ArraySegment<byte>(stringToBytes(",Three Philosophers")));client.Append("beers", new ArraySegment<byte>(stringToBytes(",Witte")));

You can check if the Append operation succeeded by using the checking the return value.

var result = client.Append("beers", new ArraySegment<byte>(stringToBytes(",Hennepin")));if (result) { Console.WriteLine("Append succeeded");} else { Console.WriteLine("Append failed");}

API Call object.Append(key, casunique, value)

Description Append a value to an existing key

Returns Object ( Binary object )

Arguments

string key Document ID used to identify the value

ulong casunique Unique value used to verify a key/value combination

object value Value to be stored

Append() may also be used with a CAS value. With this overload, the return value is a CasResult, where success isdetermined by examining the CasResult's Result property.

var casv = client.GetWithCas("beers");var casResult = client.Append("beers", casv.Cas, new ArraySegment<byte>(stringToBytes(",Adoration")));

if (casResult.Result) { Console.WriteLine("Append succeeded");} else { Console.WriteLine("Append failed");}

Page 42: couchbase-sdk-net-1.2.pdf

Update Operations

37

7.2. Decrement Methods

The Decrement() methods reduce the value of a given key if the corresponding value can be parsed to an integer val-ue. These operations are provided at a protocol level to eliminate the need to get, update, and reset a simple integer valuein the database. All the .NET Client Library methods support the use of an explicit offset value that will be used to reducethe stored value in the database.

API Call object.Decrement(key, defaultvalue, offset)

Description Decrement the value of an existing numeric key. The Couchbase Server stores numbers asunsigned values. Therefore the lowest you can decrement is to zero.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

Decrement the inventory counter by 1, defaulting to 100 if the key doesn't exist.

client.Decrement("inventory", 100, 1);

API Call object.Decrement(key, defaultvalue, offset, validfor)

Description Decrement the value of an existing numeric key. The Couchbase Server stores numbers asunsigned values. Therefore the lowest you can decrement is to zero.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

TimeSpan validfor Expiry time (in seconds) for key

Decrement the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 60 seconds.

client.Decrement("inventory", 100, 1, TimeSpan.FromSeconds(60));

API Call object.Decrement(key, defaultvalue, offset, expiresat)

Description Decrement the value of an existing numeric key. The Couchbase Server stores numbers asunsigned values. Therefore the lowest you can decrement is to zero.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

DateTime expiresat Explicit expiry time for key

Decrement the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 5 minutes.

client.Decrement("inventory", 100, 1, DateTime.Now.AddMinutes(5));

Page 43: couchbase-sdk-net-1.2.pdf

Update Operations

38

API Call object.Decrement(key, defaultvalue, offset, casunique)

Description Decrement the value of an existing numeric key. The Couchbase Server stores numbers asunsigned values. Therefore the lowest you can decrement is to zero.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

ulong casunique Unique value used to verify a key/value combination

Decrement the inventory counter by 1, defaulting to 100 if the key doesn't exist.

var casv = client.GetWithCas("inventory");client.Decrement("inventory", 100, 1, cas.Cas);

API Call object.Decrement(key, defaultvalue, offset, validfor, ca-sunique)

Description Decrement the value of an existing numeric key. The Couchbase Server stores numbers asunsigned values. Therefore the lowest you can decrement is to zero.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

TimeSpan validfor Expiry time (in seconds) for key

ulong casunique Unique value used to verify a key/value combination

Decrement the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 60 seconds.

var casv = client.GetWithCas("inventory");client.Decrement("inventory", 100, 1, TimeSpan.FromSeconds(60), cas.Cas);

API Call object.Decrement(key, defaultvalue, offset, expiresat, ca-sunique)

Description Decrement the value of an existing numeric key. The Couchbase Server stores numbers asunsigned values. Therefore the lowest you can decrement is to zero.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

DateTime expiresat Explicit expiry time for key

ulong casunique Unique value used to verify a key/value combination

Decrement the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 5 minutes.

var casv = client.GetWithCas("inventory");

Page 44: couchbase-sdk-net-1.2.pdf

Update Operations

39

client.Decrement("inventory", 100, 1, DateTime.Now.AddMinutes(5), cas.Cas);

7.3. Remove Methods

API Call object.Remove(key)

Description Delete a key/value

Returns Object ( Binary object )

Arguments

string key Document ID used to identify the value

The Remove() method deletes an item in the database with the specified key.

Remove the item with a specified key

client.Remove("badkey");

7.4. Increment Methods

The Increment() methods increase the value of a given key if the corresponding value can be parsed to an integer val-ue. These operations are provided at a protocol level to eliminate the need to get, update, and reset a simple integer valuein the database. All the .NET Client Library methods support the use of an explicit offset value that will be used to reducethe stored value in the database.

API Call object.Increment(key, defaultvalue, offset)

Description Increment the value of an existing numeric key. Couchbase Server stores numbers as un-signed numbers, therefore if you try to increment an existing negative number, it willcause an integer overflow and return a non-logical numeric result. If a key does not exist,this method will initialize it with the zero or a specified value.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

Increment the inventory counter by 1, defaulting to 100 if the key doesn't exist.

client.Increment("inventory", 100, 1);

API Call object.Increment(key, defaultvalue, offset, validfor)

Description Increment the value of an existing numeric key. Couchbase Server stores numbers as un-signed numbers, therefore if you try to increment an existing negative number, it willcause an integer overflow and return a non-logical numeric result. If a key does not exist,this method will initialize it with the zero or a specified value.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

Page 45: couchbase-sdk-net-1.2.pdf

Update Operations

40

TimeSpan validfor Expiry time (in seconds) for key

Increment the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 60 seconds.

client.Increment("inventory", 100, 1, TimeSpan.FromSeconds(60));

API Call object.Increment(key, defaultvalue, offset, expiresat)

Description Increment the value of an existing numeric key. Couchbase Server stores numbers as un-signed numbers, therefore if you try to increment an existing negative number, it willcause an integer overflow and return a non-logical numeric result. If a key does not exist,this method will initialize it with the zero or a specified value.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

DateTime expiresat Explicit expiry time for key

Increment the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 5 minutes.

client.Increment("inventory", 100, 1, DateTime.Now.AddMinutes(5));

API Call object.Increment(key, defaultvalue, offset, casunique)

Description Increment the value of an existing numeric key. Couchbase Server stores numbers as un-signed numbers, therefore if you try to increment an existing negative number, it willcause an integer overflow and return a non-logical numeric result. If a key does not exist,this method will initialize it with the zero or a specified value.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

ulong casunique Unique value used to verify a key/value combination

Increment the inventory counter by 1, defaulting to 100 if the key doesn't exist.

var casv = client.GetWithCas("inventory");client.Increment("inventory", 100, 1, cas.Cas);

API Call object.Increment(key, defaultvalue, offset, validfor, ca-sunique)

Description Increment the value of an existing numeric key. Couchbase Server stores numbers as un-signed numbers, therefore if you try to increment an existing negative number, it willcause an integer overflow and return a non-logical numeric result. If a key does not exist,this method will initialize it with the zero or a specified value.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

Page 46: couchbase-sdk-net-1.2.pdf

Update Operations

41

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

TimeSpan validfor Expiry time (in seconds) for key

ulong casunique Unique value used to verify a key/value combination

Increment the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 60 seconds.

var casv = client.GetWithCas("inventory");client.Increment("inventory", 100, 1, TimeSpan.FromSeconds(60), cas.Cas);

API Call object.Increment(key, defaultvalue, offset, expiresat, ca-sunique)

Description Increment the value of an existing numeric key. Couchbase Server stores numbers as un-signed numbers, therefore if you try to increment an existing negative number, it willcause an integer overflow and return a non-logical numeric result. If a key does not exist,this method will initialize it with the zero or a specified value.

Returns CasResult<ulong> ( Cas result of bool )

Arguments

string key Document ID used to identify the value

object defaultvalue Value to be stored if key does not already exist

Integer offset Integer offset value to increment/decrement (default 1)

DateTime expiresat Explicit expiry time for key

ulong casunique Unique value used to verify a key/value combination

Increment the inventory counter by 1, defaulting to 100 if the key doesn't exist and set an expiry of 5 minutes.

var casv = client.GetWithCas("inventory");client.Increment("inventory", 100, 1, DateTime.Now.AddMinutes(5), cas.Cas);

7.5. Prepend Methods

The Prepend() methods allow you to add information to an existing key/value pair in the database. You can use this toadd information to a string or other data before the existing data.

The Prepend() methods prepend raw serialized data on to the end of the existing data in the key. If you have previous-ly stored a serialized object into Couchbase and then use Prepend, the content of the serialized object will not be extended.For example, adding an List of integers into the database, and then using Prepend() to add another integer will resultin the key referring to a serialized version of the list, immediately preceded by a serialized version of the integer. It willnot contain an updated list with the new integer prepended to it. De-serialization of objects that have had data prependedmay result in data corruption.

API Call object.Prepend(key, value)

Description Prepend a value to an existing key

Returns Object ( Binary object )

Arguments

string key Document ID used to identify the value

object value Value to be stored

The Prepend() method prepends information to the end of an existing key/value pair.

Page 47: couchbase-sdk-net-1.2.pdf

Update Operations

42

The sample below demonstrates how to create a csv string by prepending new values.

client.Store(StoreMode.Set, "beers", "Abbey Ale");Func<string, byte[]> stringToBytes = (s) => Encoding.Default.GetBytes(s);client.Prepend("beers", new ArraySegment<byte>(stringToBytes("Three Philosophers,")));client.Prepend("beers", new ArraySegment<byte>(stringToBytes("Witte,")));

You can check if the Prepend operation succeeded by using the checking the return value.

var result = client.Prepend("beers", new ArraySegment<byte>(stringToBytes("Hennepin,")));if (result) { Console.WriteLine("Prepend succeeded");} else { Console.WriteLine("Prepend failed");}

API Call object.Prepend(key, casunique, value)

Description Prepend a value to an existing key

Returns Object ( Binary object )

Arguments

string key Document ID used to identify the value

ulong casunique Unique value used to verify a key/value combination

object value Value to be stored

Prepend() may also be used with a CAS value. With this overload, the return value is a CasResult, where success isdetermined by examining the CasResult's Result property.

var casv = client.GetWithCas("beers");var casResult = client.Prepend("beers", casv.Cas, new ArraySegment<byte>(stringToBytes("Adoration,")));

if (casResult.Result) { Console.WriteLine("Prepend succeeded");} else { Console.WriteLine("Prepend failed");}

7.6. Touch MethodsThe Touch() methods allow you to update the expiration time on a given key. This can be useful for situations whereyou want to prevent an item from expiring without resetting the associated value. For example, for a session database youmight want to keep the session alive in the database each time the user accesses a web page without explicitly updating thesession value, keeping the user's session active and available.

API Call object.Touch(key, expiry)

Description Update the expiry time of an item

Returns Boolean ( Boolean (true/false) )

Arguments

string key Document ID used to identify the value

object expiry Expiry time for key. Values larger than 30*24*60*60 seconds (30 days) are interpreted asabsolute times (from the epoch).

The Touch method provides a simple key/expiry call to update the expiry time on a given key. For example, to update theexpiry time on a session for another 60 seconds:

client.Touch("session", TimeSpan.FromSeconds(60));

To update the expiry time on the session for another day:

Page 48: couchbase-sdk-net-1.2.pdf

Update Operations

43

client.Touch("session", DateTime.Now.AddDays(1));

7.7. Sync Methods

API Call object.Sync(mode, keyn, replicationcount)

Description Sync one or more key/value pairs on a Membase cluster

Returns (none)

Arguments

mode

keyn One or more keys used to reference a value

replicationcount

Sync operations

Page 49: couchbase-sdk-net-1.2.pdf

44

Chapter 8. View/Query InterfaceThe View methods support Couchbase Server 2.0's HTTP View API. For more information on working with views inCouchbase Server, please see http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing.html

View Interface

GetView(designName, viewName)

GetView takes a

• designName

Design document name.

• viewName

View name.

There is also a generic version of GetView, which returns view items that are stongly typed.

GetData<T>(designName, viewName)

Both versions of GetView return an IView, which implements IEnumerable. Therefore, when you query for the items ina view, you can iterate over the returned collection as folows:

var beersByNameAndABV = client.GetView<Beer>("beers", "by_name_and_abv");foreach(var beer in beersByNameAndABV) { Console.WriteLine(beer.Name);}

or the non-generic version:

var beersByNameAndABV = client.GetView("beers", "by_name_and_abv");foreach(var row in beersByNameAndABV) { Console.WriteLine((row.GetItem() as Beer).Name);}

As you can see, when you iterate over a strongly typed view each item is of the type you specified. If you use the non-generic version, each item you enumerate over will be of type IViewRow. IViewRow provides methods for accessing de-tails of the row that are not present when using strongly typed views.

To get the original document from the datastore:

row.GetItem();

To get a Dictionary representation of the row:

row.Info;

To get the original document's ID (key):

row.ItemId;

To get the key emitted by the map function:

row.ViewKey;

Before iterating over the view results, you can modify the query that is sent to the server by using the fluent methods ofIView. Refer to the sample document and view below when exploring the IView API.

//map functionfunction(doc) {

Page 50: couchbase-sdk-net-1.2.pdf

View/Query Interface

45

if (doc.type == "beer") { emit([doc.name, doc.abv], doc); }}

//sample json document{ "_id" : "beer_harpoon_ipa", "name" : "Harpoon IPA", "brewery" : "brewery_harpoon", "abv" : 5.9}

To find beers with names starting with "H" and an ABV of at least 5:

var beersByNameAndABV = client.GetView("beers", "by_name_and_abv") .StartKey(new object[] { "H", 5 });

To limit the number of beers returned by the query to 10:

var beersByNameAndABV = client.GetView("beers", "by_name_and_abv") .Limit(10);

To group the results (when using _count for example):

var beersByNameAndABV = client.GetView("breweries", "breweries_by_state") .Group(true);

To disallow stale results in the view:

var beersByNameAndABV = client.GetView("beers", "by_name_and_abv") .Stale(StaleMode.False);

IView API methods may be chained. To limit the number of results to 5 and order the results descending:

var beersByNameAndABV = client.GetView("beers", "by_name_and_abv") .Limit(5).Descending(true);

Page 51: couchbase-sdk-net-1.2.pdf

46

Appendix A. Configuring LoggingThe following sections provide details on how to enable logging for the .NET Client Library

To enable logging, you can tap into the logging capabilities provided by the Enyim.Caching dependency. Enyim loggingcurrently supports either log4net or NLog.

Start by adding a reference to either Enyim.Caching.Log4NetAdapter or Enyim.Caching.NLogAdapter. Both are availableas part of the Couchbase .NET Client Nuget package or as part of the client library zip file.

You could also get the projects from https://github.com/enyim/EnyimMemcached. If you use these Visual Studio projects,you'll also need to add a reference to log4net or NLog (depending on which adapter you choose). Each of these dependen-cies is located in a "binaries" directory under the root "EnyimMemcached" directory.

For log4net, your configuration should include an enyim.com section that defines which log factory to use along withstandard log4net configuration.

The log4net configuration will vary by the type of appender you are using. For more information on log4net configuration,see http://logging.apache.org/log4net/release/manual/configuration.html.

<?xml version="1.0" encoding="utf-8"?><configuration> <configSections> <sectionGroup name="enyim.com"> <section name="log" type="Enyim.Caching.Configuration.LoggerSection, Enyim.Caching" /> </sectionGroup> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <enyim.com> <log factory="Enyim.Caching.Log4NetFactory, Enyim.Caching.Log4NetAdapter" /> </enyim.com> <log4net debug="false"> <appender name="LogFileAppender" type="log4net.Appender.FileAppender,log4net"> <param name="File" value="c:\\temp\\error-log.txt" /> <param name="AppendToFile" value="true" /> <layout type="log4net.Layout.PatternLayout,log4net"> <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] &lt;%X{auth}&gt; - %m%n" /> </layout> </appender> <root> <priority value="ALL" /> <level value="DEBUG" /> <appender-ref ref="LogFileAppender" /> </root> </log4net></configuration>

You'll also need to initialize (only once in your app) log4net in your code with the standard log4net initializer.

log4net.Config.XmlConfigurator.Configure();

NLog configuration requires setting the log factory to NLogAdapter and including the appropriate NLog configurationsection.

<?xml version="1.0" encoding="utf-8"?><configuration> <configSections> <sectionGroup name="enyim.com"> <section name="log" type="Enyim.Caching.Configuration.LoggerSection, Enyim.Caching" /> </sectionGroup> <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" /> </configSections> <enyim.com> <log factory="Enyim.Caching.NLogFactory, Enyim.Caching.NLogAdapter" /> </enyim.com> <nlog> <targets> <target name="logfile" type="File" fileName="c:\temp\error-log.txt" />

Page 52: couchbase-sdk-net-1.2.pdf

Configuring Logging

47

</targets> <rules> <logger name="*" minlevel="Info" writeTo="logfile" /> </rules> </nlog> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" /> </startup></configuration>

See http://nlog-project.org/wiki/Configuration_file for more NLog configuration details.

Page 53: couchbase-sdk-net-1.2.pdf

48

Appendix B. Configuring the .NET CLient LibraryThe following sections provide details on the App|Web.config configuration options for the .NET Client Library

The CouchbaseClientSection class is the configuration section handler.

<section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>

The minimum configuration options are to include a couchbase section with a servers element with at least oneURI, which is used to bootstrap the client. At least two node URIs should be provided, so that in the event that the clientcan't reach the first, it will try the second.

<couchbase> <servers> <add uri="http://127.0.0.1:8091/pools"/> </servers></couchbase>

The "bucket" and "bucketPassword" attributes of the servers element default to "default" and an empty string respec-tively.

<couchbase> <servers bucket="beers" bucketPassword="H0p$"> <add uri="http://127.0.0.1:8091/pools"/> </servers></couchbase>

The client will periodically check the health of its connection to the cluster by performing a heartbeat check. By default,this test is done every 10 seconds against the bootstrap URI defined in the servers element.

The "uri", "enabled" and "interval" attributes are all optional. The "interval" is specified in milliseconds. Setting "enabled"to false will cause other settings to be ignored and the heartbeat will not be checked.

<heartbeatMonitor uri="http://127.0.0.1:8091/pools/heartbeat" interval="60000" enabled="true" />

Page 54: couchbase-sdk-net-1.2.pdf

49

Appendix C. Release NotesThe following sections provide release notes for individual release versions of Couchbase Client Library .NET. To browseor submit new issaues, see Couchbase Client Library .NET Issues Tracker.

C.1. Release Notes for 1.2.0 Couchbase Client Library .NET BA (12 De-cember 2012)

Couchbase Client 1.2 GA is the first GA release to support Couchbase Server 2.0. 1.2 is backwards compatible withCouchbase Server 1.8.

In addition to support for new features of Couchbase Server 2.0, the Couchbase .NET Client Library 1.2 adds stability im-provements to iteself and its dependent Enyim.Caching library.

Fixes in 1.2.0

• NCBC-161: Run views only on nodes in cluster supporting couchApiBase (Couchbase nodes)

• NCBC-168: Socket errors were previously being swallowed and did not bubble up through ExecuteXXX method returnvalues.

Known Issues in 1.2.0

• NCBC-170: If an exception occurs before data are read, the PooledSocket may be returned to the pool marked still aliveand with a dirty buffer. In some situations, a wrong magic value error may result.

• NCBC-176: Flushing of buckets is not yet supported in Couchbase.Management API

• NCBC-172: During a rebalance or fail over, view queries may result in an unhandled NullReferenceException. This ex-ception is raised by a thread in the dependency Hammock.

C.2. Release Notes for 1.2.0-BETA-3 Couchbase Client Library .NETBeta (28 November 2012)

New Features and Behaviour Changes in 1.2.0-BETA-3

• New CouchbaseCluster GetItemCount method (NCBC-92)

• View timeout is now configuragble (NCBC-158)

• Implemented remove with observe (NCBC-163)

• ListBucket object graph now matches full server JSON (NCBC-142)

• New UpdateBucket method on CouchbaseCluster (NCBC-143)

• New CouchbaseCluster GetBucket and TryGetBucket methods to get single bucket (NCBC-72)

• ICouchbaseClient interface completed to match CouchbaseClient public methods (NCBC-151)

• Debug now supported as view parameter (NCBC-159)

• Add support to build under Mono (NCBC-132)

• (Experimental) support for spatial views (NCBC-47).

Page 55: couchbase-sdk-net-1.2.pdf

Release Notes

50

• Auto-map Id property to "id" field in view rows on generic view queries (NCBC-154)

Fixes in 1.2.0-BETA-3

• Don't swallow pooled socket errors (NCBC-168)

• Null reference exceptions now longer (occasionally) thrown during rebalancing (NCBC-121).

• Pre-fetch views to cache network pools for view requests (NCBC-149)

• ExecuteGet no longer reports "failed to locate node" on cache misses (NCBC-130)

• No longer disposing Timer in heartbeat check when it's disabled (NCBC-136)

• View requests are now made to a randomly selected node from cluster (NCBC-146)

• Client now handles correctly -1 vbucket indexes in cluster config (NCBC-148)

• Updated Enyim submodule reference to latest commit (NCBC-167)

• Null reference exceptions now longer (occasionally) thrown during rebalancing.

• Deleted keys return null during generic view queries with non-stale iterations (NCBC-157)

• Failed bootstrap node no longer puts client in invalid state (NCBC-134).

• HTTP and connection timeouts are now separate (NCBC-34)

• Observe reliability fixes (NCBC-129, NCBC-128, NCBC-124, NCBC-127)

• Delete bucket handles 500 error from server (NCBC-119)

Known Issues in 1.2.0-BETA-3

• Delete bucket request succeeds but exception is thrown.

C.3. Release Notes for 1.2.0-DP4 Couchbase Client Library .NET Alpha(27 August 2012)

New Features and Behaviour Changes in 1.2.0-DP4

• New, basic JSON conversion extension methods for serializing objects to and from JSON. Methods useNewtonsoft.Json for JSON conversions.

using Couchbase.Extensions; var result = client.StoreJson<Beer>(StoreMode.Set, "foo", new Beer { ... }); var beer = client.GetJson<Beer>("foo");

• New bucket administration methods

var cluster = new CouchbaseCluster("couchbase"); //name of config section with credentials cluster.CreateBucket(new Bucket { ... }); var buckets = cluster.ListBuckets(); cluster.DeleteBucket();

• New design document administration methods

var cluster = new CouchbaseCluster("couchbase"); //name of config section with credentials cluster.CreateDesignDocument("bucketname", "{ ... }"); var designDoc = cluster.RetrieveDesignDocument("bucketname", "designdocname");

Page 56: couchbase-sdk-net-1.2.pdf

Release Notes

51

cluster.DeleteDesignDocument("bucketname", "designdocname");

• 1.2.0 specific configuration elements (HttpClientFactory and DocumentNameTransformer) now have defaults and 1.1configuration will work with 1.2.0.

using Couchbase.Extensions; var result = client.StoreJson<Beer>(StoreMode.Set, "foo", new Beer { ... }); var beer = client.GetJson<Beer>("foo");

Fixes in 1.2.0-DP4

• Observe tests no longer fail on multi-node persistence/replication checks.

C.4. Release Notes for 1.2.0-DP3 Couchbase Client Library .NET Alpha(27 August 2012)

New Features and Behaviour Changes in 1.2.0-DP3

• Initial implementation of Observe and Store with durability checks.

//check for master persistence var result = client.ExecuteStore(StoreMode.Set, "foo", "bar", PersistTo.One); //check for master persistence with replication to 2 nodes var result = client.ExecuteStore(StoreMode.Set, "foo", "bar", PersistTo.One, ReplicateTo.Two);

Known Issues in 1.2.0-DP3

• Multi-node persistence/replication checks fail sporadically on observe.

C.5. Release Notes for 1.2.0-DP2 Couchbase Client Library .NET Alpha(25 July 2012)

Fixes in 1.2.0-DP2

• Generic view requests no longer emitting the original document as value. Client Get method is used instead to retrieveoriginal document.

• Reduced views no longer break from missing "id" field in row.

• Paging no longer breaks.

• DevelopmentModeNameTransformer now correctly prepends dev_ on view requests.

C.6. Release Notes for 1.2-DP Couchbase Client Library .NET Alpha (27March 2012)

New Features and Behaviour Changes in 1.2-DP

• Initial support for Couchbase Server 2.0 view API.

var view = client.GetView("designdoc", "viewname"); foreach(var item in view) { Console.WriteLine(item.ItemId); }

• Couchbase.dll is now compiled against the .NET Framework 4.0

Page 57: couchbase-sdk-net-1.2.pdf

52

Appendix D. LicensesThis documentation and associated software is subject to the following licenses.

D.1. Documentation License

This documentation in any form, software or printed matter, contains proprietary information that is the exclusive prop-erty of Couchbase. Your access to and use of this material is subject to the terms and conditions of your Couchbase Soft-ware License and Service Agreement, which has been executed and with which you agree to comply. This document andinformation contained herein may not be disclosed, copied, reproduced, or distributed to anyone outside Couchbase with-out prior written consent of Couchbase or as specifically provided below. This document is not part of your license agree-ment nor can it be incorporated into any contractual agreement with Couchbase or its subsidiaries or affiliates.

Use of this documentation is subject to the following terms:

You may create a printed copy of this documentation solely for your own personal use. Conversion to other formats is al-lowed as long as the actual content is not altered or edited in any way. You shall not publish or distribute this documenta-tion in any form or on any media, except if you distribute the documentation in a manner similar to how Couchbase dis-seminates it (that is, electronically for download on a Web site with the software) or on a CD-ROM or similar medium,provided however that the documentation is disseminated together with the software on the same medium. Any other use,such as any dissemination of printed copies or use of this documentation, in whole or in part, in another publication, re-quires the prior written consent from an authorized representative of Couchbase. Couchbase and/or its affiliates reserveany and all rights to this documentation not expressly granted above.

This documentation may provide access to or information on content, products, and services from third parties. CouchbaseInc. and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to third-partycontent, products, and services. Couchbase Inc. and its affiliates will not be responsible for any loss, costs, or damages in-curred due to your access to or use of third-party content, products, or services.

The information contained herein is subject to change without notice and is not warranted to be error-free. If you find anyerrors, please report them to us in writing.