Skip to content

Commit 2a050c5

Browse files
craiggwilsonrstam
authored andcommitted
CSHARP-538: fixed bug related to element selection after projections.
1 parent e1e12a3 commit 2a050c5

File tree

4 files changed

+146
-14
lines changed

4 files changed

+146
-14
lines changed

Driver/Linq/Translators/Projector.cs

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,83 @@
2121

2222
namespace MongoDB.Driver.Linq
2323
{
24+
/// <summary>
25+
/// Represents a projection from TSource to TResult;
26+
/// </summary>
27+
internal interface IProjector : IEnumerable
28+
{
29+
/// <summary>
30+
/// Gets the cursor.
31+
/// </summary>
32+
MongoCursor Cursor { get; }
33+
}
34+
35+
/// <summary>
36+
/// Represents a projection from TSource to TResult;
37+
/// </summary>
38+
/// <typeparam name="TSource">The type of the source.</typeparam>
39+
/// <typeparam name="TResult">The type of the result.</typeparam>
40+
internal interface IProjector<TSource, TResult> : IProjector, IEnumerable<TResult>
41+
{ }
42+
43+
/// <summary>
44+
/// Represents a projector that does nothing.
45+
/// </summary>
46+
/// <typeparam name="T"></typeparam>
47+
internal class IdentityProjector<T> : IProjector<T,T>
48+
{
49+
// private fields
50+
private readonly MongoCursor _cursor;
51+
52+
// constructors
53+
/// <summary>
54+
/// Initializes a new instance of the <see cref="IdentityProjector&lt;T&gt;"/> class.
55+
/// </summary>
56+
/// <param name="cursor">The cursor.</param>
57+
public IdentityProjector(MongoCursor cursor)
58+
{
59+
_cursor = cursor;
60+
}
61+
62+
// public properties
63+
/// <summary>
64+
/// Gets the cursor.
65+
/// </summary>
66+
public MongoCursor Cursor
67+
{
68+
get { return _cursor; }
69+
}
70+
71+
// public methods
72+
/// <summary>
73+
/// Returns an enumerator that iterates through the collection.
74+
/// </summary>
75+
/// <returns>
76+
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
77+
/// </returns>
78+
public IEnumerator<T> GetEnumerator()
79+
{
80+
return ((IEnumerable<T>)_cursor).GetEnumerator();
81+
}
82+
83+
/// <summary>
84+
/// Returns an enumerator that iterates through a collection.
85+
/// </summary>
86+
/// <returns>
87+
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
88+
/// </returns>
89+
IEnumerator IEnumerable.GetEnumerator()
90+
{
91+
return GetEnumerator();
92+
}
93+
}
94+
2495
/// <summary>
2596
/// Represents a projection.
2697
/// </summary>
2798
/// <typeparam name="TSource">The type of the source objects.</typeparam>
2899
/// <typeparam name="TResult">The type of the result objects.</typeparam>
29-
public class Projector<TSource, TResult> : IEnumerable<TResult>
100+
internal class Projector<TSource, TResult> : IProjector<TSource, TResult>
30101
{
31102
// private fields
32103
private MongoCursor<TSource> _cursor;
@@ -44,17 +115,23 @@ public Projector(MongoCursor<TSource> cursor, Func<TSource, TResult> projection)
44115
_projection = projection;
45116
}
46117

118+
// public properties
119+
/// <summary>
120+
/// Gets the cursor.
121+
/// </summary>
122+
public MongoCursor Cursor
123+
{
124+
get { return _cursor; }
125+
}
126+
47127
// public methods
48128
/// <summary>
49129
/// Gets an enumerator for the result objects.
50130
/// </summary>
51131
/// <returns>An enumerator for the result objects.</returns>
52132
public IEnumerator<TResult> GetEnumerator()
53133
{
54-
foreach (var document in _cursor)
55-
{
56-
yield return _projection(document);
57-
}
134+
return _cursor.Select(_projection).GetEnumerator();
58135
}
59136

60137
// explicit interface implementation

Driver/Linq/Translators/SelectQuery.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,11 @@ public override object Execute()
188188
}
189189
}
190190

191-
IEnumerable enumerable;
191+
IProjector projector;
192192
if (projection == null)
193193
{
194-
enumerable = cursor;
194+
var projectorType = typeof(IdentityProjector<>).MakeGenericType(DocumentType);
195+
projector = (IProjector)Activator.CreateInstance(projectorType, cursor);
195196
}
196197
else
197198
{
@@ -201,17 +202,16 @@ public override object Execute()
201202
var resultType = delegateType.GetGenericArguments()[1];
202203
var projectorType = typeof(Projector<,>).MakeGenericType(sourceType, resultType);
203204
var compiledProjection = projection.Compile();
204-
var projector = Activator.CreateInstance(projectorType, cursor, compiledProjection);
205-
enumerable = (IEnumerable)projector;
205+
projector = (IProjector)Activator.CreateInstance(projectorType, cursor, compiledProjection);
206206
}
207207

208208
if (_elementSelector != null)
209209
{
210-
return _elementSelector(enumerable);
210+
return _elementSelector(projector);
211211
}
212212
else
213213
{
214-
return enumerable;
214+
return projector;
215215
}
216216
}
217217

@@ -388,7 +388,7 @@ private void TranslateAny(MethodCallExpression methodCallExpression)
388388
_projection = null;
389389

390390
// note: recall that cursor method Size respects Skip and Limit while Count does not
391-
SetElementSelector(methodCallExpression, source => ((int)((MongoCursor)source).Size()) > 0);
391+
SetElementSelector(methodCallExpression, source => ((int)((IProjector)source).Cursor.Size()) > 0);
392392
}
393393

394394
private void TranslateCount(MethodCallExpression methodCallExpression)
@@ -413,10 +413,10 @@ private void TranslateCount(MethodCallExpression methodCallExpression)
413413
switch (methodCallExpression.Method.Name)
414414
{
415415
case "Count":
416-
SetElementSelector(methodCallExpression, source => (int)((MongoCursor)source).Size());
416+
SetElementSelector(methodCallExpression, source => (int)((IProjector)source).Cursor.Size());
417417
break;
418418
case "LongCount":
419-
SetElementSelector(methodCallExpression, source => ((MongoCursor)source).Size());
419+
SetElementSelector(methodCallExpression, source => ((IProjector)source).Cursor.Size());
420420
break;
421421
}
422422
}

DriverUnitTests/DriverUnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
<Compile Include="GridFS\MongoGridFSSettingsTests.cs" />
129129
<Compile Include="GridFS\MongoGridFSStreamTests.cs" />
130130
<Compile Include="GridFS\MongoGridFSTests.cs" />
131+
<Compile Include="Jira\CSharp538Tests.cs" />
131132
<Compile Include="Jira\CSharp532Tests.cs" />
132133
<Compile Include="Jira\CSharp475Tests.cs" />
133134
<Compile Include="Jira\CSharp101Tests.cs" />
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* Copyright 2010-2012 10gen 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.Collections.Generic;
18+
using System.Linq;
19+
using System.Text;
20+
using NUnit.Framework;
21+
22+
using MongoDB.Bson;
23+
using MongoDB.Driver;
24+
using MongoDB.Driver.Linq;
25+
using MongoDB.Bson.Serialization.Attributes;
26+
using MongoDB.Driver.Builders;
27+
28+
namespace MongoDB.DriverUnitTests.Jira.CSharp538
29+
{
30+
[TestFixture]
31+
public class CSharp538Tests
32+
{
33+
[BsonKnownTypes(typeof(B))]
34+
public abstract class A
35+
{
36+
37+
}
38+
39+
public class B : A
40+
{
41+
42+
}
43+
44+
[Test]
45+
public void Test()
46+
{
47+
var db = Configuration.TestDatabase;
48+
var collection = db.GetCollection<A>("csharp_538");
49+
50+
var count = collection.AsQueryable().OfType<B>().Count();
51+
Assert.AreEqual(0, count);
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)