Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-109

Improve type safety of parameters to methods in MongoCollection

    • Type: Icon: Improvement Improvement
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • 0.9
    • Affects Version/s: 0.9
    • Component/s: None
    • None
    • Major Change

      I am probably going to make the following substantial change to the 10gen C# Driver and thought it would be helpful to describe it first and hope to get some feedback.

      If you look at the MongoCollection class you will find lots of generic methods with type parameters (like TQuery, TIndexKeys, TIndexOptions, TSortBy, etc...). Most of them have no constraints on them so they can be bound to any type at all with no compile time type safety provided.

      For example:

      void EnsureIndex<TIndexKeys, TIndexOptions>(TIndexKeys keys, TIndexOptions options)

      provides no more compile time type safety than:

      void EnsureIndex(object keys, object options)

      One consequence of the lack of compile time type safety is that you could accidentally provide the arguments to EnsureIndex in the wrong order and it would compile just fine.

      The proposed change is to use marker interfaces instead of generic types (I know marker interfaces are sometimes frowned upon but they appear to be justified here, I think). So for example EnsureIndex would be declared:

      // using marker interfaces: IIndexKeys and IIndexOptions
      void EnsureIndex(IIndexKeys keys, IIndexOptions options)

      Now we have compile time type safety on both parameters.

      How will this affect you? If you are using the existing builders to create your argument values not at all, because they will be modified to implement the new marker interfaces. If you are creating a BsonDocument manually you will have to use one of several new subclasses of BsonDocument instead (see the FindAs example below) and in some cases modify your method call to remove the extra type parameters.

      Another generic method is the FindAs method:

      TDocument FindAs<TQuery, TDocument>(TQuery query)

      In this case we aren't concerned about passing arguments in the wrong order, but we can still easily pass the wrong type of argument. For example, the following compiles but is wrong:

      // note also the awkwardness of having to specify the TQuery type, it can't be inferred
      var cursor = collection.FindAs<SortByBuilder, MyDocumentClass>(SortBy.Ascending("x"));

      We can improve type safety and at the same time simplify the call (by only having to provide the type argument for the result type) if we declare FindAs like:

      // using the marker interface: IQuery
      TDocument FindAs<TDocument>(IQuery query)

      So now the previous FindAs example with a SortBy argument would not compile, and a correct example would look like:

      var cursor = collection.FindAs<MyDocumentClass>(Query.EQ("x", 1));

      Since we sometime prefer to hand build a query using BsonDocument, we would have to do it just a little bit differently:

      // assume: public class QueryDocument : BsonDocument, IQuery { }
      var query = new QueryDocument {

      { "x", 1 }

      };
      var cursor = collection.FindAs<MyDocumentClass>(query);

      QueryDocument is just a subclass of BsonDocument that implements the IQuery marker interface. In words: "a QueryDocument is a BsonDocument that we intend to use as a query".

      Hopefully this wouldn't break too much existing code and would have a considerable benefit by achieving better compile time type safety.

      Feedback?

      Thanks,

      Robert

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            robert@mongodb.com Robert Stam
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: