Skip to content

Commit ab8eaaf

Browse files
author
rstam
committed
Some refactoring of LINQ code. Removed some premature optimizations (might come back but only if proven worthwhile).
1 parent 0652c61 commit ab8eaaf

10 files changed

+106
-198
lines changed

Driver/Driver.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@
180180
<Compile Include="Internal\MongoReplyMessage.cs" />
181181
<Compile Include="Internal\MongoUpdateMessage.cs" />
182182
<Compile Include="Internal\ReplicaSetConnector.cs" />
183-
<Compile Include="Linq\Evaluator.cs" />
183+
<Compile Include="Linq\Nominator.cs" />
184+
<Compile Include="Linq\PartialEvaluator.cs" />
184185
<Compile Include="Linq\ExpressionPrettyPrinter.cs" />
185186
<Compile Include="Linq\ExpressionVisitor.cs" />
186187
<Compile Include="Linq\TranslatedFindQuery.cs" />

Driver/Linq/ExpressionVisitor.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
using System.Text;
2222
using System.Threading;
2323

24-
// taken from Part 2 of Matt Warren's blogs on building a LINQ provider
25-
// see: http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
26-
2724
namespace MongoDB.Driver.Linq
2825
{
2926
/// <summary>

Driver/Linq/MongoQueryProvider.cs

Lines changed: 8 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,19 @@
2121
using System.Threading;
2222
using System.Reflection;
2323

24+
// for a good blog post on implementing LINQ query providers see Matt Warren's blog posts
25+
// see: http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx
26+
2427
namespace MongoDB.Driver.Linq
2528
{
2629
/// <summary>
2730
/// An implementation of IQueryProvider for querying a MongoDB collection.
2831
/// </summary>
2932
public class MongoQueryProvider : IQueryProvider
3033
{
31-
// private static fields
32-
private static Dictionary<Type, Func<MongoQueryProvider, Expression, IQueryable>> __createQueryDelegates = new Dictionary<Type, Func<MongoQueryProvider, Expression, IQueryable>>();
33-
private static MethodInfo __createQueryGenericMethodDefinition;
34-
private static Dictionary<Type, Func<MongoQueryProvider, Expression, object>> __executeDelegates = new Dictionary<Type, Func<MongoQueryProvider, Expression, object>>();
35-
private static MethodInfo __executeGenericMethodDefinition;
36-
private static object __staticLock = new object();
37-
3834
// private fields
3935
private MongoCollection _collection;
4036

41-
// static constructor
42-
static MongoQueryProvider()
43-
{
44-
foreach (var methodInfo in typeof(MongoQueryProvider).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
45-
{
46-
if (methodInfo.Name == "CreateQuery" && methodInfo.IsGenericMethodDefinition)
47-
{
48-
__createQueryGenericMethodDefinition = methodInfo;
49-
}
50-
if (methodInfo.Name == "Execute" && methodInfo.IsGenericMethodDefinition)
51-
{
52-
__executeGenericMethodDefinition = methodInfo;
53-
}
54-
}
55-
}
56-
5737
// constructors
5838
/// <summary>
5939
/// Initializes a new instance of the MongoQueryProvider class.
@@ -67,61 +47,6 @@ public MongoQueryProvider(MongoCollection collection)
6747
_collection = collection;
6848
}
6949

70-
// private static methods
71-
private static Func<MongoQueryProvider, Expression, IQueryable> GetCreateQueryDelegate(Type type)
72-
{
73-
lock (__staticLock)
74-
{
75-
Func<MongoQueryProvider, Expression, IQueryable> createQueryDelegate;
76-
if (!__createQueryDelegates.TryGetValue(type, out createQueryDelegate))
77-
{
78-
var createQueryMethodInfo = __createQueryGenericMethodDefinition.MakeGenericMethod(type);
79-
80-
// lambdaExpression = (provider, expression) => (IQueryable) provider.CreateQuery<T>(expression)
81-
var providerParameter = Expression.Parameter(typeof(MongoQueryProvider), "provider");
82-
var expressionParameter = Expression.Parameter(typeof(Expression), "expression");
83-
var lambdaExpression = Expression.Lambda<Func<MongoQueryProvider, Expression, IQueryable>>(
84-
Expression.Convert(
85-
Expression.Call(providerParameter, createQueryMethodInfo, expressionParameter),
86-
typeof(IQueryable)
87-
),
88-
providerParameter,
89-
expressionParameter
90-
);
91-
createQueryDelegate = lambdaExpression.Compile();
92-
__createQueryDelegates.Add(type, createQueryDelegate);
93-
}
94-
return createQueryDelegate;
95-
}
96-
}
97-
98-
private static Func<MongoQueryProvider, Expression, object> GetExecuteDelegate(Type type)
99-
{
100-
lock (__staticLock)
101-
{
102-
Func<MongoQueryProvider, Expression, object> executeDelegate;
103-
if (!__executeDelegates.TryGetValue(type, out executeDelegate))
104-
{
105-
var executeMethodInfo = __executeGenericMethodDefinition.MakeGenericMethod(type);
106-
107-
// lambdaExpression = (provider, expression) => (object) provider.Execute<T>(expression)
108-
var providerParameter = Expression.Parameter(typeof(MongoQueryProvider), "provider");
109-
var expressionParameter = Expression.Parameter(typeof(Expression), "expression");
110-
var lambdaExpression = Expression.Lambda<Func<MongoQueryProvider, Expression, object>>(
111-
Expression.Convert(
112-
Expression.Call(providerParameter, executeMethodInfo, expressionParameter),
113-
typeof(object)
114-
),
115-
providerParameter,
116-
expressionParameter
117-
);
118-
executeDelegate = lambdaExpression.Compile();
119-
__executeDelegates.Add(type, executeDelegate);
120-
}
121-
return executeDelegate;
122-
}
123-
}
124-
12550
// public methods
12651
/// <summary>
12752
/// Creates a new instance of MongoQueryable{{T}} for this provider.
@@ -154,11 +79,11 @@ public IQueryable CreateQuery(Expression expression)
15479
{
15580
throw new ArgumentNullException("expression");
15681
}
82+
var elementType = TypeSystem.GetElementType(expression.Type);
15783
try
15884
{
159-
var elementType = TypeSystem.GetElementType(expression.Type);
160-
var createQueryDelegate = GetCreateQueryDelegate(elementType);
161-
return createQueryDelegate(this, expression);
85+
var queryableType = typeof(MongoQueryable<>).MakeGenericType(elementType);
86+
return (IQueryable)Activator.CreateInstance(queryableType, new object[] { this, expression });
16287
}
16388
catch (TargetInvocationException ex)
16489
{
@@ -182,8 +107,7 @@ public TResult Execute<TResult>(Expression expression)
182107
{
183108
throw new ArgumentException("Argument expression is not valid.");
184109
}
185-
var translatedQuery = MongoQueryTranslator.Translate(_collection, expression);
186-
return (TResult)translatedQuery.Execute();
110+
return (TResult)Execute(expression);
187111
}
188112

189113
/// <summary>
@@ -197,36 +121,8 @@ public object Execute(Expression expression)
197121
{
198122
throw new ArgumentNullException("expression");
199123
}
200-
try
201-
{
202-
var resultType = expression.Type;
203-
var executeDelegate = GetExecuteDelegate(resultType);
204-
return executeDelegate(this, expression);
205-
}
206-
catch (TargetInvocationException ex)
207-
{
208-
throw ex.InnerException;
209-
}
210-
}
211-
212-
/// <summary>
213-
/// Gets an enumerator by executing the query.
214-
/// </summary>
215-
/// <typeparam name="T">Type element type.</typeparam>
216-
/// <param name="expression">The LINQ expression.</param>
217-
/// <returns>An enumerator for the results of the query.</returns>
218-
public IEnumerator<T> GetEnumerator<T>(Expression expression)
219-
{
220-
if (expression == null)
221-
{
222-
throw new ArgumentNullException("expression");
223-
}
224-
if (!typeof(IEnumerable<T>).IsAssignableFrom(expression.Type))
225-
{
226-
throw new ArgumentException("Argument expression is not valid.");
227-
}
228124
var translatedQuery = MongoQueryTranslator.Translate(_collection, expression);
229-
return translatedQuery.GetEnumerator<T>();
125+
return translatedQuery.Execute();
230126
}
231127

232128
/// <summary>

Driver/Linq/MongoQueryTranslator.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ public static class MongoQueryTranslator
3636
/// <returns>An instance of MongoLinqQuery.</returns>
3737
public static TranslatedQuery Translate(MongoCollection collection, Expression expression)
3838
{
39-
expression = Evaluator.PartialEval(expression);
39+
expression = PartialEvaluator.Evaluate(expression);
4040

4141
// total hack just to test the initial LINQ framework
4242
var query = MongoDB.Driver.Builders.Query.EQ("X", 1);
43-
return new TranslatedFindQuery(collection, query);
43+
var documentType = TypeSystem.GetElementType(expression.Type);
44+
return new TranslatedFindQuery(collection, query, documentType);
4445
}
4546
}
4647
}

Driver/Linq/MongoQueryable.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
using System.Text;
2222
using System.Threading;
2323

24-
// adapted from Part 1 of Matt Warren's blogs on building a LINQ provider
25-
// see: http://blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx
26-
2724
namespace MongoDB.Driver.Linq
2825
{
2926
/// <summary>
@@ -78,7 +75,7 @@ public MongoQueryable(MongoQueryProvider provider, Expression expression)
7875
/// <returns></returns>
7976
public IEnumerator<T> GetEnumerator()
8077
{
81-
return _provider.GetEnumerator<T>(_expression);
78+
return ((IEnumerable<T>)_provider.Execute(_expression)).GetEnumerator();
8279
}
8380

8481
/// <summary>
@@ -93,7 +90,7 @@ public override string ToString()
9390
// explicit implementation of IEnumerable
9491
IEnumerator IEnumerable.GetEnumerator()
9592
{
96-
return GetEnumerator();
93+
return ((IEnumerable)_provider.Execute(_expression)).GetEnumerator();
9794
}
9895

9996
// explicit implementation of IQueryable

Driver/Linq/Nominator.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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.Linq.Expressions;
20+
using System.Text;
21+
using System.Threading;
22+
23+
using MongoDB.Driver.Linq;
24+
25+
namespace MongoDB.Driver.Linq
26+
{
27+
/// <summary>
28+
/// Performs bottom-up analysis to find maximal subtrees that satisfy a predicate.
29+
/// </summary>
30+
class Nominator : ExpressionVisitor
31+
{
32+
Func<Expression, bool> _predicate;
33+
HashSet<Expression> _candidates;
34+
bool _isBlocked;
35+
36+
internal Nominator(Func<Expression, bool> predicate)
37+
{
38+
if (predicate == null)
39+
{
40+
throw new ArgumentNullException("predicate");
41+
}
42+
_predicate = predicate;
43+
}
44+
45+
internal HashSet<Expression> Nominate(Expression expression)
46+
{
47+
_candidates = new HashSet<Expression>();
48+
this.Visit(expression);
49+
return _candidates;
50+
}
51+
52+
protected override Expression Visit(Expression expression)
53+
{
54+
if (expression != null)
55+
{
56+
bool wasBlocked = _isBlocked;
57+
_isBlocked = false;
58+
base.Visit(expression);
59+
if (!_isBlocked)
60+
{
61+
if (_predicate(expression))
62+
{
63+
_candidates.Add(expression);
64+
}
65+
else
66+
{
67+
_isBlocked = true;
68+
}
69+
}
70+
_isBlocked |= wasBlocked;
71+
}
72+
return expression;
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)