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.


Anonymous Type .map() projection

The following is only possible with C#

var games = new[]
    {
        new Game {id = 2, player = "Bob", points = 15, type = "ranked"},
        new Game {id = 5, player = "Alice", points = 7, type = "free"},
        new Game {id = 11, player = "Bob", points = 10, type = "free"},
        new Game {id = 12, player = "Alice", points = 2, type = "free"},
    };

List<TopPlayer> result =
    r.expr(games)
        .filter(g => g["points"].gt(9))
        .map(g => new { PlayerId = g["id"] }) // Anonymous Type Projection
        .run<List<TopPlayer>>(conn);

result.Dump();

result.ShouldBeEquivalentTo(new[]
    {
        new TopPlayer {PlayerId = 2},
        new TopPlayer {PlayerId = 11}
    });

Happy ReQL-ing! 🚀

Clone this wiki locally