-
-
Notifications
You must be signed in to change notification settings - Fork 131
Extra C# Driver Features
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.
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);
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"]
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. ❤️
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.
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
*/
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.
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))
// Anonymous Type Projection Below
.map(g => new { PlayerId = g["id"] })
.run<List<TopPlayer>>(conn);
result.Dump();
result.ShouldBeEquivalentTo(new[]
{
new TopPlayer {PlayerId = 2},
new TopPlayer {PlayerId = 11}
});
Happy ReQL-ing! 🚀
- Home
- Query Examples
- Logging
- Connections & Pooling
- Extra C# Features
- GOTCHA Goblins!
- LINQ to ReQL Provider
- Differences
- Java ReQL API Documentation