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; } + } +}