Skip to content

Extra C# Driver Features

Brian Chavez edited this page Nov 17, 2015 · 107 revisions

In general, the C# driver and Java driver are both API compatible. You should be able to follow the Java documentation without much trouble.

The C# driver, however, offers additional language syntax that goes beyond the original Java driver. The additional language features are highlighted here.


Optional Arguments

Java and C#

r.table("marvel").getAll("man_of_steel").optArg("index", "code_name")
                 .run(conn);

Additionally, C# supports indexer operator overloads that allows passing in an anonymous type for optional arguments.

r.table("marvel").getAll("man_of_steel")[new {index="code_name"}]
                 .run(conn);

Bracket

Java, Python, JavaScript and C#

r.table("marvel").get("12345")["field"]["subfield"] // Python
r.table("marvel").get("12345")("field")("subfield") // JavaScript
r.table("marvel").get("12345").getField("field").getField("subfield") //Java
r.table("marvel").get("12345").getField("field").getField("subfield") // C#

Additionally, C# also supports .bracket() via indexer overload:

// C# similar to Python syntax
r.table("marvel").get("12345")["field"]["subfield"]

Implicit Conversion Operator Overload

The following is only possible with C#

//Objects inside Foobar table:
new Foobar {id = "a", Baz = 1, Qux = 1}
new Foobar {id = "b", Baz = 2, Qux = 2}
new Foobar {id = "c", Baz = 3, Qux = 3}

var exprA = r.table("foobar").get("a")["Baz"]; // 1
var exprB = r.table("foobar").get("b")["Qux"]; // 2

int result = (exprA + exprB + 1).run<int>(conn);

// Everything between (...) executes on the server
// and returns result 4.

The last line, (exprA + exprB + 1) is converted into an AST and sent to the server for evaluation including the + 1) part. The + 1) is not evaluated on the client. Here's what happens:

The compiler/run-time knows exprA is type ReqlExpr, moves right to exprB (also of type ReqlExpr), applies the + operator overload for adding two ReqlExprs who's sum is also ReqlExpr (under the hood, all we're doing is exprA.add(exprB)). Lastly, the evaluation moves right again to the last + 1) but encounters an int type. The implicit conversion operator kicks in (int -> ReqlExpr) and converts int into a Datum(1) (which inherits from ReqlExpr). Finally, the last + Datum(1) can be evaluated. The final equivalent ReQL sequence is: exprA.add(exprB).add(new Datum(1)). Beautiful. ❤️


Dynamic Language Runtime (DLR) Integration

The Python, JavaScript, and Java drivers all expect the developer to know the shape of the returned results. The C# driver follows a similar paradigm. However, if you supply additional generic type information, the DLR can use this information to perform a cast into a specific requested type.

new Foobar {id = "a", Baz = 4, Qux = 4}

// DLR dynamic
/* var result is 4 of type dynamic */
var result = r.table("foobar").get("a")["Baz"].run(conn);

/* long result is 4 of type long */
long result = r.table("foobar").get("a")["Baz"].run(conn);

// Give the compiler and run-time more type information with run<T>()
/* int result is 4 of type int */
int  result = r.table("foobar").get("a")["Baz"].run<int>(conn);

Notice the result declarations long and int and their respective run and run<T> calls. The underlying deserializer, Newtonsoft.Json determined the underlying type (without T) is a long. Given T as int, the deserializer can make a more specific deserialization of the result.


Cursor<T> support

If your query returns a sequence, be sure to declare that you expect a cursor as a result of your query. The following example shows how to expect a cursor:

Cursor<int> result = r.range(1,4).run<int>();

Notice the DLR magic above, run<int> returns Cursor<int> not int, because the server's response for the query above is a SEQUENCE. It is the responsibility of the driver to return a cursor when the query result is a SEQUENCE. In the example above, run<T> specifies the cursor item type.

There is a slight performance cost to run<T> since the execution context involves the DLR. As a best practice, if you expect your query to return a cursor sequence you should run .runCursor<T>(). .runCursor<T> bypasses the DLR and the execution context remains within the CLR type system to return a Cursor<T> just as any normal CLR method call.

Cursor<int> result = r.range(1,4).runCursor<int>(conn);

foreach(var i in result){
   Console.WriteLine(i);
}
/* Output:
1
2
3
*/

Poco Support

This C# driver supports POCO serialization to RethinkDB via Newtonsoft.Json. If you have very unique POCO serialization requirements, you can override the default PocoConverter method.

Converter.PocoConverter = (poco) =>
      {
         return JObject.FromObject(poco, /*with custom serialization*/);
      }

Keep in mind, there are some native types that RethinkDB expects in a specific JSON format. Native ReQL types like time, dates, and binary (DateTime, DateTimeOffset, byte[]) need to be in a specific format in order to perform ReQL operations on them. By default, native ReQL types are converted automatically if the default PocoConverter is used; however, if you override the default PocoConverter you will need to ensure DateTime, DateTimeOffset, byte[], and other ReQL native types are converted correctly. JsonConverters for these types are ready for you to use in PocoConverter.TimeConverter and PocoConverter.BinaryConverter.

Happy ReQL-ing! 🚀

Clone this wiki locally