C# Driver Version 1.4.1 Release Notes
This minor release fixes a few issues found by users of the LINQ support added
in v1.4 of the C# driver and also adds support for a few new LINQ query
operators and where clauses.
File by file change logs are available at:
https://github.com/mongodb/mongo-csharp-driver/blob/master/Release%20Notes/Change%20Log%20v1.4.1-Bson.txt
https://github.com/mongodb/mongo-csharp-driver/blob/master/Release%20Notes/Change%20Log%20v1.4.1-Driver.txt
These release notes describe the changes at a higher level, and omit describing
some of the minor changes.
Breaking changes
There are no breaking changes in this release.
JIRA issues resolved
The full list of JIRA issues resolved in this release is available at:
https://jira.mongodb.org/secure/IssueNavigator.jspa?mode=hide&requestId=11397
LINQ query support
The main purpose of this minor release is to fix some issues found by users of
the new LINQ support added in v1.4.
One bug that many have encountered is a NullReferenceException when writing a
query against an inherited property.
https://jira.mongodb.org/browse/CSHARP-418
You would hit this error if you had any queries that were similar to this:
public class B
{
public ObjectId Id;
}
public class C : B
{
public int X;
}
var query =
from c in collection.AsQueryable<C>()
where c.Id = id // class C inherits Id from class B
select c;
Another bug that a few users have encountered is an ArgumentOutOfRangeException
when writing a LINQ query that consists of a bare AsQueryable and nothing else:
https://jira.mongodb.org/browse/CSHARP-419
as in this sample:
var query = collection.AsQueryable<C>(); // no where clause
Normally a query would contain something else besides the call to AsQueryable
(like a where clause), but this is a legal query and is now supported.
BSON library changes
MaxSerializationDepth
The BSON serialization mechanism does not support circular references in your
object graph. In earlier versions of the C# driver if you attempted to
serialize an object with circular references you would get a
StackOverflowExpection. The 1.4.1 version now tracks the serialization depth
as it serializes an object and if it exceeds MaxSerializationDepth a
BsonSerializationException is thrown. The problem with StackOverflowException
was that it was fatal to your process, but the BsonSerializationException can
be caught and your process can continue executing if you choose.
The default MaxSerializationDepth is 100.
Interpretation of C# null vs BsonNull.Value
When working with the BsonDocument object model a C# null is usually ignored,
specially when creating BsonDocuments using functional construction. However,
when mapping between .NET types and the BsonDocument object model a C# null
will now be mapped to a BsonNull. For example:
var dictionary = new Dictionary<string, object> { { "x", null } };
var document = new BsonDocument(dictionary);
// document["x"] == BsonNull.Value
and when mapping in the reverse direction a BsonNull will map to a C# null:
var document = new BsonDocument { { "x", BsonNull.Value } };
var dictionary = document.ToDictionary();
// dictionary["x"] == null
Usually mapping between .NET types and the BsonDocument object model happens
automatically as needed, but if you want to invoke the mapping yourself you
can access the BsonTypeMapper directly:
var dictionary = new Dictionary<string, object> { { "x", null } };
var document = BsonTypeMapper.MapToBsonValue(dictionary);
// document["x"] == BsonNull.Value
or in the other direction:
var document = new BsonDocument { { "x", BsonNull.Value } };
var dictionary = (IDictionary<string, object>)BsonTypeMapper.MapToDotNetValue(document);
// dictionary["x"] == null
Serializing read-only properties
The class map based serialization support normally serializes only public
read-write properties (or fields). Sometimes it can be useful to serialize
read-only properties as well, specially if you want to query against them.
You can now opt-in your read-only properties so that they appear in the
serialized document. For example:
public class Book
{
public ObjectId Id;
public string Title;
[BsonElement] // opt-in the read-only LowercaseTitle property
public string LowercaseTitle { get { return Title.ToLower(); } }
}
Now when a Book is serialized the document will look like:
{
_id : ObjectId("4f8d771dae879111d289dbc0"),
Title : "For Whom the Bell Tolls",
LowercaseTitle : "for whom the bell tolls"
}
During deserialization any elements in the serialized document that
correspond to read-only properties are ignored.
Driver changes
MongoServer
There is a new method called IsDatabaseNameValid that you can call to test if
a database name is valid.
MongoDatabase
There is a new method called IsCollectionNameValid that you can call to test if a
collection name is valid.
MongoGridFS
You can now disable computing the MD5 at the server when uploading a GridFS
file. You can also disable the client side verification of the MD5 that is
normally done on Upload or Download. The reason you might choose to disable
MD5 verification is that it is computationally expensive to compute the MD5.
LINQ OfType query operator
You can now use the OfType query operator in LINQ queries. For example:
var query = collection.AsQueryable<B>().OfType<C>();
this generates a query against the "_t" discriminator value that is used to
identify the actual type of a serialized document.
Additional expressions supported in LINQ where clauses
The following expressions are now supported in LINQ where clauses:
// d is the document
// p is a property of the document
// c is a character constant
// ca is an array of character constants
// s is a string constant
// i, j, k, n are integer constants
where d.p.Equals(constant)
where string.IsNullOrEmpty(d.p)
where d.p.IndexOf(c) == i
where d.p.IndexOf(c, j) == i
where d.p.IndexOf(c, j, k) == i
where d.p.IndexOf(s) == i
where d.p.IndexOf(s, j) == i
where d.p.IndexOf(s, j, k) == i
where d.p.IndexOfAny(ca) == i
where d.p.IndexOfAny(ca, j) == i
where d.p.IndexOfAny(ca, j, k) == i
where d.p[i] == c
where d.p.Length == n
where d.p.ToLower().Contains("xyz")
where d.p.ToLower().StartsWith("xyz")
where d.p.ToLower().EndsWith("xyz")
where d.p.ToUpper().Contains("xyz")
where d.p.ToUpper().StartsWith("xyz")
where d.p.ToUpper().EndsWith("xyz")
where d.p.Trim().Contains("xyz")
where d.p.Trim().StartsWith("xyz")
where d.p.Trim().EndsWith("xyz")
where d.p.TrimStart().Contains("xyz")
where d.p.TrimStart().StartsWith("xyz")
where d.p.TrimStart().EndsWith("xyz")
where d.p.TrimEnd().Contains("xyz")
where d.p.TrimEnd().StartsWith("xyz")
where d.p.TrimEnd().EndsWith("xyz")
where d.GetType() == typeof(T)
where d is T
// you can use any combination of ToLower/ToUpper/Trim/TrimStart/TrimEnd
// before Contains/StartsWith/EndsWith
In the 1.4 version of the C# driver the constant always had to appear on the
right of a comparison operator. That restriction is lifted in 1.4.1 so now the
following are equivalent:
where d.Height < 60
where 60 > d.Height
Type of in AsQueryable can now be deduced
The type of in the call to AsQueryable can now be deduced from the collection argument:
var collection = database.GetCollection<MyDocument>("mydocuments")
var query = collection.AsQueryable(); // <T> is deduced to be MyDocument