Add public API to get the owning IMongoClient from a MongoDB LINQ queryable

XMLWordPrintableJSON

    • Type: Improvement
    • Resolution: Done
    • Priority: Unknown
    • None
    • Affects Version/s: None
    • Component/s: None
    • None
    • None
    • Dotnet Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      Description

      There is currently no supported way to get from a MongoDB IQueryable (or the public IMongoQueryProvider interface) back to the IMongoClient that owns it. The internal MongoQueryProvider<TDocument> holds the source collection/database (and therefore the client), but none of it is reachable through public API — the public IMongoQueryProvider only exposes LoggedStages/ExecuteAsync, and the source metadata (CollectionNamespace, etc.) lives on the internal IMongoQueryProviderInternal.

      Frameworks and middleware that operate on a Mongo IQueryable they did not construct (query-layer integrations such as OData, diagnostic/telemetry code, session/transaction enlistment) need to identify the queryable's client. Today the only option is reflecting into driver internals.

      Motivation / concrete consumer

      mongo-aspnetcore-odata (ODATA-38) needs the owning IMongoClient to call IMongoClient.AppendMetadata(LibraryInfo) and tag the driver handshake with the OData library name/version. Its current workaround reflects MongoQueryProvider<T>.Collection -> IMongoCollection<T>.Database -> Client. This is fragile in two ways:

      1. It couples to internal member names.
      2. It builds IMongoCollection<ElementType> from queryable.ElementType, which is wrong whenever the queryable has been projected/joined so that ElementType != TDocument (e.g. a GroupJoin). Because IMongoCollection<T> is invariant, invoking the getter throws TargetException, and the telemetry is silently lost for those queries.

      Proposed API

      An additive extension method (non-breaking), in MongoDB.Driver.Linq, alongside the existing IQueryableExtensions.GetMongoQueryProvider and IQueryableExtensions.GetLoggedStages (which it directly mirrors):

      namespace MongoDB.Driver.Linq;
      
      public static partial class IQueryableExtensions
      {
          // Returns the IMongoClient that owns the source queryable's collection/database.
          public static IMongoClient GetClient(this IQueryable source);
      }
      

      Usage from a consumer:

      var client = queryable.GetClient();
      

      Requirements

      • Implemented via direct access to the internal provider state (no reflection), maintained alongside MongoQueryProvider<T>. Unlike GetLoggedStages, it cannot read from the public IMongoQueryProvider interface (we are deliberately not adding an interface member), so it casts to the internal provider and reads the internal field.
      • Returns the owning client for both provider construction paths — collection-based queryables and database-level aggregation queryables.
      • Correct when the queryable has been transformed (Select, GroupJoin, GroupBy, ...) such that the element type differs from the collection's document type.
      • Returns the same IMongoClient instance that created the collection/database.
      • Non-null: an IMongoQueryProvider always has a resolvable client, so no Try/null semantics are needed. Consistent with GetMongoQueryProvider/GetLoggedStages, it throws ArgumentException for a non-Mongo IQueryable.

      Versioning / backport

      Purely additive (extension method, no interface change), so it is not a breaking change. Implement on main (4.x) and backport to the next 3.x minor. A public Client member on the IMongoQueryProvider interface was considered but rejected: it would be a breaking change (DIMs are not viable across the driver's net472/netstandard2.1 targets), and it benefits only external implementers of the interface, of which there are effectively none. It was also considered whether to place the method on a new IMongoQueryProviderExtensions; the existing IQueryableExtensions was chosen for consistency with GetMongoQueryProvider/GetLoggedStages and because callers hold an IQueryable, not a provider.

      Acceptance criteria

      • queryable.GetClient() returns the owning client for a plain collection.AsQueryable().
      • Same for a projected/joined queryable where ElementType != TDocument (regression case from ODATA-38 CountriesController, which uses GroupJoin).
      • Same for a database-level aggregation queryable.
      • Returned instance is reference-equal to the client used to create the collection/database.
      • Throws ArgumentException for a non-Mongo IQueryable (consistent with existing IQueryableExtensions).

      References

      ODATA-38; mongo-aspnetcore-odata PR #31 (interim reflection-based ClientMetadata.TryGetClient, to be replaced by this API once the 3.x backport ships).

            Assignee:
            Adelin Mbida Owona
            Reporter:
            Adelin Mbida Owona
            None
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: