diff --git a/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs b/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs
index 65fcfa39d71..8009af1db03 100644
--- a/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs
+++ b/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs
@@ -47,6 +47,11 @@ public interface IMongoQueryProvider : IQueryProvider
///
internal interface IMongoQueryProviderInternal : IMongoQueryProvider
{
+ ///
+ /// Gets the client that owns the collection or database being queried.
+ ///
+ IMongoClient Client { get; }
+
///
/// Gets the collection namespace.
///
diff --git a/src/MongoDB.Driver/Linq/IQueryableExtensions.cs b/src/MongoDB.Driver/Linq/IQueryableExtensions.cs
index a90a78e9c93..efc6cc4b828 100644
--- a/src/MongoDB.Driver/Linq/IQueryableExtensions.cs
+++ b/src/MongoDB.Driver/Linq/IQueryableExtensions.cs
@@ -26,6 +26,23 @@ namespace MongoDB.Driver.Linq
public static class IQueryableExtensions
{
+ ///
+ /// Gets the client that owns the collection or database the source is querying.
+ ///
+ /// The source.
+ /// The client.
+ public static IMongoClient GetClient(this IQueryable source)
+ {
+ Ensure.IsNotNull(source, nameof(source));
+ var provider = source.Provider as IMongoQueryProviderInternal;
+ if (provider == null)
+ {
+ throw new ArgumentException("The source argument must be a MongoDB IQueryable.", nameof(source));
+ }
+
+ return provider.Client;
+ }
+
///
/// Gets the most recently logged stages.
///
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs
index 91c07d41aea..f5263571507 100644
--- a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs
@@ -42,6 +42,7 @@ protected MongoQueryProvider(
}
// public properties
+ public abstract IMongoClient Client { get; }
public abstract CollectionNamespace CollectionNamespace { get; }
public abstract BsonDocument[] LoggedStages { get; }
public AggregateOptions Options => _options;
@@ -102,6 +103,7 @@ internal MongoQueryProvider(
}
// public properties
+ public override IMongoClient Client => (_database ?? _collection?.Database)?.Client;
public IMongoCollection Collection => _collection;
public override CollectionNamespace CollectionNamespace => _collection == null ? null : _collection.CollectionNamespace;
public IMongoDatabase Database => _database;
diff --git a/tests/MongoDB.Driver.Tests/Linq/IQueryableExtensionsTests.cs b/tests/MongoDB.Driver.Tests/Linq/IQueryableExtensionsTests.cs
new file mode 100644
index 00000000000..a86674a16c6
--- /dev/null
+++ b/tests/MongoDB.Driver.Tests/Linq/IQueryableExtensionsTests.cs
@@ -0,0 +1,129 @@
+/* Copyright 2010-present MongoDB Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Linq;
+using FluentAssertions;
+using MongoDB.Driver.Linq;
+using Moq;
+using Xunit;
+
+namespace MongoDB.Driver.Tests.Linq;
+
+public class IQueryableExtensionsTests
+{
+ [Fact]
+ public void GetClient_should_return_client_for_collection_queryable()
+ {
+ var client = CreateClient();
+ var queryable = CreateCollection(client).AsQueryable();
+
+ var result = queryable.GetClient();
+
+ result.Should().BeSameAs(client);
+ }
+
+ [Fact]
+ public void GetClient_should_return_client_for_projected_queryable()
+ {
+ var client = CreateClient();
+ var queryable = CreateCollection(client).AsQueryable().Select(c => c.X);
+
+ var result = queryable.GetClient();
+
+ result.Should().BeSameAs(client);
+ }
+
+ [Fact]
+ public void GetClient_should_return_client_for_group_joined_queryable()
+ {
+ var client = CreateClient();
+ var queryable = CreateCollection(client).AsQueryable()
+ .GroupJoin(
+ CreateCollection(client).AsQueryable(),
+ c => c.Id,
+ d => d.CId,
+ (c, ds) => new { C = c, Ds = ds });
+ queryable.ElementType.Should().NotBe(typeof(C));
+
+ var result = queryable.GetClient();
+
+ result.Should().BeSameAs(client);
+ }
+
+ [Fact]
+ public void GetClient_should_return_client_for_database_queryable()
+ {
+ var client = CreateClient();
+ var queryable = CreateDatabase(client).AsQueryable();
+
+ var result = queryable.GetClient();
+
+ result.Should().BeSameAs(client);
+ }
+
+ [Fact]
+ public void GetClient_should_throw_when_source_is_not_a_MongoDB_queryable()
+ {
+ var queryable = new[] { 1, 2, 3 }.AsQueryable();
+
+ var exception = Record.Exception(() => queryable.GetClient());
+
+ exception.Should().BeOfType()
+ .Subject.ParamName.Should().Be("source");
+ }
+
+ [Fact]
+ public void GetClient_should_throw_when_source_is_null()
+ {
+ IQueryable source = null;
+
+ var exception = Record.Exception(() => source.GetClient());
+
+ exception.Should().BeOfType()
+ .Subject.ParamName.Should().Be("source");
+ }
+
+ private static IMongoClient CreateClient() => Mock.Of();
+
+ private static IMongoDatabase CreateDatabase(IMongoClient client)
+ {
+ var database = Mock.Of();
+ Mock.Get(database).SetupGet(d => d.Client).Returns(client);
+ Mock.Get(database).SetupGet(d => d.Settings).Returns(new MongoDatabaseSettings());
+ return database;
+ }
+
+ private static IMongoCollection CreateCollection(IMongoClient client)
+ {
+ var collection = Mock.Of>();
+ Mock.Get(collection).SetupGet(c => c.Database).Returns(CreateDatabase(client));
+ Mock.Get(collection).SetupGet(c => c.Settings).Returns(new MongoCollectionSettings());
+ return collection;
+ }
+
+ // nested types
+ public class C
+ {
+ public int Id { get; set; }
+ public int X { get; set; }
+ }
+
+ public class D
+ {
+ public int Id { get; set; }
+ public int CId { get; set; }
+ }
+}