Skip to content

Commit 2803f87

Browse files
authored
CSHARP-4976: MongoDB C# Driver no longer compatible with DevExpress ASP.NET Core Middleware (#1291)
1 parent 536fd85 commit 2803f87

File tree

2 files changed

+179
-2
lines changed
  • src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators
  • tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators

2 files changed

+179
-2
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExecutableQuery.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
1617
using System.Linq;
1718
using System.Threading;
1819
using System.Threading.Tasks;
1920
using MongoDB.Bson;
2021
using MongoDB.Bson.Serialization;
22+
using MongoDB.Bson.Serialization.Serializers;
2123
using MongoDB.Driver.Core.Misc;
2224
using MongoDB.Driver.Linq.Linq3Implementation.Ast;
2325
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers;
26+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
2427

2528
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators
2629
{
@@ -141,17 +144,42 @@ public override string ToString()
141144
// private methods
142145
private BsonDocumentStagePipelineDefinition<TDocument, TOutput> CreateCollectionPipelineDefinition(BsonDocument[] stages)
143146
{
144-
return new BsonDocumentStagePipelineDefinition<TDocument, TOutput>(stages, (IBsonSerializer<TOutput>)_pipeline.OutputSerializer);
147+
var outputSerializer = GetOutputSerializer();
148+
return new BsonDocumentStagePipelineDefinition<TDocument, TOutput>(stages, outputSerializer);
145149
}
146150

147151
private BsonDocumentStagePipelineDefinition<NoPipelineInput, TOutput> CreateDatabasePipelineDefinition(BsonDocument[] stages)
148152
{
149-
return new BsonDocumentStagePipelineDefinition<NoPipelineInput, TOutput>(stages, (IBsonSerializer<TOutput>)_pipeline.OutputSerializer);
153+
var outputSerializer = GetOutputSerializer();
154+
return new BsonDocumentStagePipelineDefinition<NoPipelineInput, TOutput>(stages, outputSerializer);
150155
}
151156

152157
private BsonDocument[] RenderPipeline()
153158
{
154159
return _pipeline.Render().AsBsonArray.Cast<BsonDocument>().ToArray();
155160
}
161+
162+
private IBsonSerializer<TOutput> GetOutputSerializer()
163+
{
164+
var outputSerializer = _pipeline.OutputSerializer;
165+
var outputType = outputSerializer.ValueType;
166+
167+
if (outputType == typeof(TOutput))
168+
{
169+
return (IBsonSerializer<TOutput>)outputSerializer;
170+
}
171+
172+
if (!typeof(TOutput).IsAssignableFrom(outputType))
173+
{
174+
throw new NotSupportedException($"The type of the pipeline output is {outputType} which is not assignable to {typeof(TOutput)}.");
175+
}
176+
177+
if (typeof(TOutput).IsNullableOf(outputType))
178+
{
179+
return (IBsonSerializer<TOutput>)NullableSerializer.Create(outputSerializer);
180+
}
181+
182+
return (IBsonSerializer<TOutput>)DowncastingSerializer.Create(typeof(TOutput), outputType, outputSerializer);
183+
}
156184
}
157185
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Linq;
18+
using FluentAssertions;
19+
using MongoDB.Driver.Linq;
20+
using Xunit;
21+
22+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators
23+
{
24+
public class ExecutableQueryTests : Linq3IntegrationTest
25+
{
26+
[Fact]
27+
public void Cast_to_object_should_work()
28+
{
29+
var collection = GetCollection();
30+
var queryable1 = collection.AsQueryable();
31+
var queryable2 = queryable1.Provider.CreateQuery<object>(queryable1.Expression);
32+
33+
var results = queryable2.ToList();
34+
35+
results.Should().HaveCount(5);
36+
}
37+
38+
[Fact]
39+
public void Cast_aggregation_to_object_should_work()
40+
{
41+
var collection = GetCollection();
42+
var queryable1 = collection.AsQueryable().GroupBy(
43+
p => p.Type,
44+
(k, p) => new ProductAggregation {Type = k, MaxPrice = p.Select(i => i.Price).Max()});
45+
var queryable2 = queryable1.Provider.CreateQuery<object>(queryable1.Expression);
46+
47+
var results = queryable2.ToList();
48+
49+
results.Should().HaveCount(2);
50+
}
51+
52+
[Fact]
53+
public void Cast_int_to_object_should_work()
54+
{
55+
var collection = GetCollection();
56+
var queryable1 = collection.AsQueryable().Select(p => p.Id);
57+
var queryable2 = queryable1.Provider.CreateQuery<object>(queryable1.Expression);
58+
59+
var results = queryable2.ToList();
60+
61+
results.Should().HaveCount(5);
62+
}
63+
64+
[Fact]
65+
public void Cast_to_nullable_should_work()
66+
{
67+
var collection = GetCollection();
68+
var queryable1 = collection.AsQueryable().Select(p => p.Id);
69+
var queryable2 = queryable1.Provider.CreateQuery<int?>(queryable1.Expression);
70+
71+
var results = queryable2.ToList();
72+
73+
results.Should().HaveCount(5);
74+
}
75+
76+
[Fact]
77+
public void Cast_to_incompatible_type_should_throw()
78+
{
79+
var collection = GetCollection();
80+
var queryable1 = collection.AsQueryable();
81+
var queryable2 = queryable1.Provider.CreateQuery<ProductAggregation>(queryable1.Expression);
82+
83+
var exception = Record.Exception(() => queryable2.ToList());
84+
85+
exception.Should().BeOfType<NotSupportedException>();
86+
exception.Message.Should().Contain($"The type of the pipeline output is {typeof(DerivedProduct)} which is not assignable to {typeof(ProductAggregation)}");
87+
}
88+
89+
[Fact]
90+
public void Cast_to_interface_should_work()
91+
{
92+
var collection = GetCollection();
93+
var queryable1 = collection.AsQueryable();
94+
var queryable2 = queryable1.Provider.CreateQuery<IProduct>(queryable1.Expression);
95+
96+
var results = queryable2.ToList();
97+
98+
results.Should().HaveCount(5);
99+
}
100+
101+
[Fact]
102+
public void Cast_to_base_class_should_work()
103+
{
104+
var collection = GetCollection();
105+
var queryable1 = collection.AsQueryable();
106+
var queryable2 = queryable1.Provider.CreateQuery<ProductBase>(queryable1.Expression);
107+
108+
var results = queryable2.ToList();
109+
110+
results.Should().HaveCount(5);
111+
}
112+
113+
private IMongoCollection<DerivedProduct> GetCollection()
114+
{
115+
var collection = GetCollection<DerivedProduct>("test");
116+
CreateCollection(
117+
collection,
118+
new DerivedProduct { Id = 1, Type = "a", Price = 1 },
119+
new DerivedProduct { Id = 2, Type = "a", Price = 5 },
120+
new DerivedProduct { Id = 3, Type = "a", Price = 12 },
121+
new DerivedProduct { Id = 4, Type = "b", Price = 2 },
122+
new DerivedProduct { Id = 5, Type = "b", Price = 7 });
123+
return collection;
124+
}
125+
126+
private interface IProduct
127+
{
128+
string Type { get; set; }
129+
decimal Price { get; set; }
130+
}
131+
132+
private class ProductBase : IProduct
133+
{
134+
public int Id { get; set; }
135+
public string Type { get; set; }
136+
public decimal Price { get; set; }
137+
}
138+
139+
private class DerivedProduct : ProductBase
140+
{
141+
}
142+
143+
private class ProductAggregation
144+
{
145+
public string Type { get; set; }
146+
public decimal MaxPrice { get; set; }
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)