Browse Source

Initial framework of LINQ implementation. Please note that this doesn't really do anything yet because the MongoLinqTranslator class is stubbed out.

pull/70/merge
rstam 14 years ago
parent
commit
4c1b6d2a80
  1. 29
      Bson/Serialization/BsonClassMap.cs
  2. 28
      Bson/Serialization/BsonMemberMap.cs
  3. 7
      Driver/Driver.csproj
  4. 42
      Driver/Linq/MongoLinqExtensionMethods.cs
  5. 59
      Driver/Linq/MongoLinqFindQuery.cs
  6. 66
      Driver/Linq/MongoLinqQuery.cs
  7. 45
      Driver/Linq/MongoLinqTranslator.cs
  8. 233
      Driver/Linq/MongoQueryProvider.cs
  9. 107
      Driver/Linq/MongoQueryable.cs
  10. 64
      Driver/Linq/TypeSystem.cs
  11. 3
      DriverOnlineTests/DriverOnlineTests.csproj
  12. 69
      DriverOnlineTests/Linq/MongoLinqFindQueryTests.cs
  13. 75
      DriverOnlineTests/Linq/MongoQueryProviderTests.cs
  14. 67
      DriverOnlineTests/Linq/MongoQueryableTests.cs

29
Bson/Serialization/BsonClassMap.cs

@ -1031,17 +1031,19 @@ namespace MongoDB.Bson.Serialization {
private Func<object> GetCreator() { private Func<object> GetCreator() {
if (creator == null) { if (creator == null) {
Expression expression;
Expression body;
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var defaultConstructor = classType.GetConstructor(bindingFlags, null, new Type[0], null); var defaultConstructor = classType.GetConstructor(bindingFlags, null, new Type[0], null);
if (defaultConstructor != null) { if (defaultConstructor != null) {
expression = Expression.New(defaultConstructor);
// lambdaExpression = () => (object) new TClass()
body = Expression.New(defaultConstructor);
} else { } else {
var getUnitializedObjectInfo = typeof(FormatterServices).GetMethod("GetUninitializedObject", BindingFlags.Public | BindingFlags.Static);
expression = Expression.Call(getUnitializedObjectInfo, Expression.Constant(classType));
// lambdaExpression = () => FormatterServices.GetUninitializedObject(classType)
var getUnitializedObjectMethodInfo = typeof(FormatterServices).GetMethod("GetUninitializedObject", BindingFlags.Public | BindingFlags.Static);
body = Expression.Call(getUnitializedObjectMethodInfo, Expression.Constant(classType));
} }
var lambda = Expression.Lambda<Func<object>>(expression);
creator = lambda.Compile();
var lambdaExpression = Expression.Lambda<Func<object>>(body);
creator = lambdaExpression.Compile();
} }
return creator; return creator;
} }
@ -1056,14 +1058,15 @@ namespace MongoDB.Bson.Serialization {
shouldSerializeMethodInfo.IsPublic && shouldSerializeMethodInfo.IsPublic &&
shouldSerializeMethodInfo.ReturnType == typeof(bool) shouldSerializeMethodInfo.ReturnType == typeof(bool)
) { ) {
// we need to construct a lambda wich does the following
// (obj) => obj.ShouldSerializeXyz()
var parameter = Expression.Parameter(typeof(object), "obj");
var body = Expression.Call(
Expression.Convert(parameter, classType),
shouldSerializeMethodInfo
// lambdaExpression = (obj) => ((TClass) obj).ShouldSerializeXyz()
var objParameter = Expression.Parameter(typeof(object), "obj");
var lambdaExpression = Expression.Lambda<Func<object, bool>>(
Expression.Call(
Expression.Convert(objParameter, classType),
shouldSerializeMethodInfo
),
objParameter
); );
var lambdaExpression = Expression.Lambda<Func<object, bool>>(body, parameter);
return lambdaExpression.Compile(); return lambdaExpression.Compile();
} else { } else {
return null; return null;

28
Bson/Serialization/BsonMemberMap.cs

@ -420,19 +420,20 @@ namespace MongoDB.Bson.Serialization {
} }
} }
var instance = Expression.Parameter(typeof(object), "obj");
var lambda = Expression.Lambda<Func<object, object>>(
// lambdaExpression = (obj) => (object) ((TClass) obj).Member
var objParameter = Expression.Parameter(typeof(object), "obj");
var lambdaExpression = Expression.Lambda<Func<object, object>>(
Expression.Convert( Expression.Convert(
Expression.MakeMemberAccess( Expression.MakeMemberAccess(
Expression.Convert(instance, memberInfo.DeclaringType),
Expression.Convert(objParameter, memberInfo.DeclaringType),
memberInfo memberInfo
), ),
typeof(object) typeof(object)
), ),
instance
objParameter
); );
return lambda.Compile();
return lambdaExpression.Compile();
} }
private Action<object, object> GetPropertySetter() { private Action<object, object> GetPropertySetter() {
@ -443,19 +444,20 @@ namespace MongoDB.Bson.Serialization {
throw new BsonSerializationException(message); throw new BsonSerializationException(message);
} }
var instance = Expression.Parameter(typeof(object), "obj");
var argument = Expression.Parameter(typeof(object), "a");
var lambda = Expression.Lambda<Action<object, object>>(
// lambdaExpression = (obj, value) => ((TClass) obj).SetMethod((TMember) value)
var objParameter = Expression.Parameter(typeof(object), "obj");
var valueParameter = Expression.Parameter(typeof(object), "value");
var lambdaExpression = Expression.Lambda<Action<object, object>>(
Expression.Call( Expression.Call(
Expression.Convert(instance, memberInfo.DeclaringType),
Expression.Convert(objParameter, memberInfo.DeclaringType),
setMethodInfo, setMethodInfo,
Expression.Convert(argument, memberType)
Expression.Convert(valueParameter, memberType)
), ),
instance,
argument
objParameter,
valueParameter
); );
return lambda.Compile();
return lambdaExpression.Compile();
} }
#endregion #endregion
} }

7
Driver/Driver.csproj

@ -179,6 +179,13 @@
<Compile Include="Internal\MongoReplyMessage.cs" /> <Compile Include="Internal\MongoReplyMessage.cs" />
<Compile Include="Internal\MongoUpdateMessage.cs" /> <Compile Include="Internal\MongoUpdateMessage.cs" />
<Compile Include="Internal\ReplicaSetConnector.cs" /> <Compile Include="Internal\ReplicaSetConnector.cs" />
<Compile Include="Linq\MongoLinqFindQuery.cs" />
<Compile Include="Linq\MongoLinqExtensionMethods.cs" />
<Compile Include="Linq\MongoQueryable.cs" />
<Compile Include="Linq\MongoQueryProvider.cs" />
<Compile Include="Linq\MongoLinqTranslator.cs" />
<Compile Include="Linq\MongoLinqQuery.cs" />
<Compile Include="Linq\TypeSystem.cs" />
<Compile Include="MongoUtils.cs" /> <Compile Include="MongoUtils.cs" />
<Compile Include="MongoDefaults.cs" /> <Compile Include="MongoDefaults.cs" />
<Compile Include="GridFS\MongoGridFS.cs" /> <Compile Include="GridFS\MongoGridFS.cs" />

42
Driver/Linq/MongoLinqExtensionMethods.cs

@ -0,0 +1,42 @@
/* Copyright 2010-2011 10gen 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.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
namespace MongoDB.Driver.Linq {
/// <summary>
/// Static class that contains the Mongo Linq extension methods.
/// </summary>
public static class MongoLinqExtensionMethods {
/// <summary>
/// Returns an instance of IQueryable{{T}} for a MongoCollection.
/// </summary>
/// <typeparam name="T">The type of the returned documents.</typeparam>
/// <param name="collection">The name of the collection.</param>
/// <returns>An instance of IQueryable{{T}} for a MongoCollection.</returns>
public static IQueryable<T> AsQueryable<T>(
this MongoCollection collection
) {
var provider = new MongoQueryProvider(collection);
return new MongoQueryable<T>(provider);
}
}
}

59
Driver/Linq/MongoLinqFindQuery.cs

@ -0,0 +1,59 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
namespace MongoDB.Driver.Linq {
/// <summary>
/// Represents a LINQ query that has been translated to an equivalent MongoDB Find query.
/// </summary>
public class MongoLinqFindQuery : MongoLinqQuery {
#region private fields
private IMongoQuery query;
#endregion
#region constructor
/// <summary>
/// Initializes a new instance of the MongoLinqFindQuery class.
/// </summary>
/// <param name="collection">The collection being queried.</param>
/// <param name="query">The query.</param>
public MongoLinqFindQuery(
MongoCollection collection,
IMongoQuery query
)
: base(collection) {
this.query = query;
}
#endregion
#region public methods
/// <summary>
/// Executes the translated Find query.
/// </summary>
/// <returns>The result of executing the translated Find query.</returns>
public override IEnumerator<T> GetEnumerator<T>() {
var cursor = collection.FindAs<T>(query);
// TODO: modify the cursor with things like sort order, skip and limit
return cursor.GetEnumerator();
}
#endregion
}
}

66
Driver/Linq/MongoLinqQuery.cs

@ -0,0 +1,66 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
namespace MongoDB.Driver.Linq {
/// <summary>
/// Represents a LINQ query that has been translated to a MongoDB query.
/// </summary>
public abstract class MongoLinqQuery {
#region protected fields
/// <summary>
/// The collection being queried.
/// </summary>
protected MongoCollection collection;
#endregion
#region constructors
/// <summary>
/// Initializes a new instance of the MongoLinqQuery class.
/// </summary>
/// <param name="collection">The collection being queried.</param>
protected MongoLinqQuery(
MongoCollection collection
) {
this.collection = collection;
}
#endregion
#region public methods
/// <summary>
/// Executes a query that returns a single result (overridden by subclasses).
/// </summary>
/// <returns>The result of executing the query.</returns>
public virtual object Execute() {
throw new NotSupportedException();
}
/// <summary>
/// Gets an enumerator for the results of a query that returns multiple results (overridden by subclasses).
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual IEnumerator<T> GetEnumerator<T>() {
throw new NotSupportedException();
}
#endregion
}
}

45
Driver/Linq/MongoLinqTranslator.cs

@ -0,0 +1,45 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
namespace MongoDB.Driver.Linq {
/// <summary>
/// A translator from LINQ expression queries to Mongo queries.
/// </summary>
public static class MongoLinqTranslator {
#region public static methods
/// <summary>
/// A translator from LINQ queries to MongoDB queries.
/// </summary>
/// <param name="collection">The collection being queried.</param>
/// <param name="expression">The LINQ query.</param>
/// <returns>An instance of MongoLinqQuery.</returns>
public static MongoLinqQuery Translate(
MongoCollection collection,
Expression expression
) {
// total hack just to test the initial LINQ framework
var query = MongoDB.Driver.Builders.Query.EQ("X", 1);
return new MongoLinqFindQuery(collection, query);
}
#endregion
}
}

233
Driver/Linq/MongoQueryProvider.cs

@ -0,0 +1,233 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Reflection;
namespace MongoDB.Driver.Linq {
/// <summary>
/// An implementation of IQueryProvider for querying a MongoDB collection.
/// </summary>
public class MongoQueryProvider : IQueryProvider {
#region private static fields
private static Dictionary<Type, Func<MongoQueryProvider, Expression, IQueryable>> createQueryDelegates = new Dictionary<Type, Func<MongoQueryProvider, Expression, IQueryable>>();
private static MethodInfo createQueryGenericMethodDefinition;
private static Dictionary<Type, Func<MongoQueryProvider, Expression, object>> executeDelegates = new Dictionary<Type, Func<MongoQueryProvider, Expression, object>>();
private static MethodInfo executeGenericMethodDefinition;
private static object staticLock = new object();
#endregion
#region private fields
private MongoCollection collection;
#endregion
#region static constructor
static MongoQueryProvider() {
foreach (var methodInfo in typeof(MongoQueryProvider).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
if (methodInfo.Name == "CreateQuery" && methodInfo.IsGenericMethodDefinition) {
createQueryGenericMethodDefinition = methodInfo;
}
if (methodInfo.Name == "Execute" && methodInfo.IsGenericMethodDefinition) {
executeGenericMethodDefinition = methodInfo;
}
}
}
#endregion
#region constructors
/// <summary>
/// Initializes a new instance of the MongoQueryProvider class.
/// </summary>
public MongoQueryProvider(
MongoCollection collection
) {
if (collection == null) {
throw new ArgumentNullException("collection");
}
this.collection = collection;
}
#endregion
#region private static methods
private static Func<MongoQueryProvider, Expression, IQueryable> GetCreateQueryDelegate(
Type type
) {
lock (staticLock) {
Func<MongoQueryProvider, Expression, IQueryable> createQueryDelegate;
if (!createQueryDelegates.TryGetValue(type, out createQueryDelegate)) {
var createQueryMethodInfo = createQueryGenericMethodDefinition.MakeGenericMethod(type);
// lambdaExpression = (provider, expression) => (IQueryable) provider.CreateQuery<T>(expression)
var providerParameter = Expression.Parameter(typeof(MongoQueryProvider), "provider");
var expressionParameter = Expression.Parameter(typeof(Expression), "expression");
var lambdaExpression = Expression.Lambda<Func<MongoQueryProvider, Expression, IQueryable>>(
Expression.Convert(
Expression.Call(providerParameter, createQueryMethodInfo, expressionParameter),
typeof(IQueryable)
),
providerParameter,
expressionParameter
);
createQueryDelegate = lambdaExpression.Compile();
createQueryDelegates.Add(type, createQueryDelegate);
}
return createQueryDelegate;
}
}
private static Func<MongoQueryProvider, Expression, object> GetExecuteDelegate(
Type type
) {
lock (staticLock) {
Func<MongoQueryProvider, Expression, object> executeDelegate;
if (!executeDelegates.TryGetValue(type, out executeDelegate)) {
var executeMethodInfo = executeGenericMethodDefinition.MakeGenericMethod(type);
// lambdaExpression = (provider, expression) => (object) provider.Execute<T>(expression)
var providerParameter = Expression.Parameter(typeof(MongoQueryProvider), "provider");
var expressionParameter = Expression.Parameter(typeof(Expression), "expression");
var lambdaExpression = Expression.Lambda<Func<MongoQueryProvider, Expression, object>>(
Expression.Convert(
Expression.Call(providerParameter, executeMethodInfo, expressionParameter),
typeof(object)
),
providerParameter,
expressionParameter
);
executeDelegate = lambdaExpression.Compile();
executeDelegates.Add(type, executeDelegate);
}
return executeDelegate;
}
}
#endregion
#region public methods
/// <summary>
/// Creates a new instance of MongoQueryable{{T}} for this provider.
/// </summary>
/// <typeparam name="T">The type of the returned elements.</typeparam>
/// <param name="expression">The query expression.</param>
/// <returns>A new instance of MongoQueryable{{T}}.</returns>
public IQueryable<T> CreateQuery<T>(
Expression expression
) {
if (expression == null) {
throw new ArgumentNullException("expression");
}
if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type)) {
throw new ArgumentOutOfRangeException("expression");
}
return new MongoQueryable<T>(this, expression);
}
/// <summary>
/// Creates a new instance MongoQueryable{{T}} for this provider. Calls the generic CreateQuery{{T}}
/// to actually create the new MongoQueryable{{T}} instance.
/// </summary>
/// <param name="expression">The query expression.</param>
/// <returns>A new instance of MongoQueryable{{T}}.</returns>
public IQueryable CreateQuery(
Expression expression
) {
if (expression == null) {
throw new ArgumentNullException("expression");
}
try {
var elementType = TypeSystem.GetElementType(expression.Type);
var createQueryDelegate = GetCreateQueryDelegate(elementType);
return createQueryDelegate(this, expression);
} catch (TargetInvocationException ex) {
throw ex.InnerException;
}
}
/// <summary>
/// Executes a query.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="expression">The query expression.</param>
/// <returns>The result of the query.</returns>
public TResult Execute<TResult>(
Expression expression
) {
if (expression == null) {
throw new ArgumentNullException("expression");
}
if (!typeof(TResult).IsAssignableFrom(expression.Type)) {
throw new ArgumentException("Argument expression is not valid.");
}
var translatedQuery = MongoLinqTranslator.Translate(collection, expression);
return (TResult) translatedQuery.Execute();
}
/// <summary>
/// Executes a query. Calls the generic method Execute{{T}} to actually execute the query.
/// </summary>
/// <param name="expression">The query expression.</param>
/// <returns>The result of the query.</returns>
public object Execute(
Expression expression
) {
if (expression == null) {
throw new ArgumentNullException("expression");
}
try {
var resultType = expression.Type;
var executeDelegate = GetExecuteDelegate(resultType);
return executeDelegate(this, expression);
} catch (TargetInvocationException ex) {
throw ex.InnerException;
}
}
/// <summary>
/// Gets an enumerator by executing the query.
/// </summary>
/// <typeparam name="T">Type element type.</typeparam>
/// <param name="expression">The LINQ expression.</param>
/// <returns>An enumerator for the results of the query.</returns>
public IEnumerator<T> GetEnumerator<T>(
Expression expression
) {
if (expression == null) {
throw new ArgumentNullException("expression");
}
if (!typeof(IEnumerable<T>).IsAssignableFrom(expression.Type)) {
throw new ArgumentException("Argument expression is not valid.");
}
var translatedQuery = MongoLinqTranslator.Translate(collection, expression);
return translatedQuery.GetEnumerator<T>();
}
/// <summary>
/// Gets a string representation of the LINQ expression translated to a MongoDB query.
/// </summary>
/// <param name="expression">The LINQ expression.</param>
/// <returns>A string.</returns>
public string GetQueryText(
Expression expression
) {
var translatedQuery = MongoLinqTranslator.Translate(collection, expression);
return translatedQuery.ToString();
}
#endregion
}
}

107
Driver/Linq/MongoQueryable.cs

@ -0,0 +1,107 @@
/* Copyright 2010-2011 10gen 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.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
namespace MongoDB.Driver.Linq {
/// <summary>
/// An implementation of IQueryable{{T}} for querying a MongoDB collection.
/// </summary>
public class MongoQueryable<T> : IOrderedQueryable<T> {
#region private fields
private MongoQueryProvider provider;
private Expression expression;
#endregion
#region constructors
/// <summary>
/// Initializes a new instance of the MongoQueryable class.
/// </summary>
public MongoQueryable(
MongoQueryProvider provider
) {
if (provider == null) {
throw new ArgumentNullException("provider");
}
this.provider = provider;
this.expression = Expression.Constant(this);
}
/// <summary>
/// Initializes a new instance of the MongoQueryable class.
/// </summary>
public MongoQueryable(
MongoQueryProvider provider,
Expression expression
) {
if (provider == null) {
throw new ArgumentNullException("provider");
}
if (expression == null) {
throw new ArgumentNullException("expression");
}
if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type)) {
throw new ArgumentOutOfRangeException("expression");
}
this.provider = provider;
this.expression = expression;
}
#endregion
#region public methods
/// <summary>
/// Gets an enumerator for the results of a MongoDB LINQ query.
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator() {
return provider.GetEnumerator<T>(expression);
}
/// <summary>
/// Gets a string representation of the MongoDB query obtained by translating the LINQ query.
/// </summary>
/// <returns></returns>
public override string ToString() {
return provider.GetQueryText(expression);
}
#endregion
#region explicit implementation of IEnumerable
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region explicit implementation of IQueryable
Type IQueryable.ElementType {
get { return typeof(T); }
}
Expression IQueryable.Expression {
get { return expression; }
}
IQueryProvider IQueryable.Provider {
get { return provider; }
}
#endregion
}
}

64
Driver/Linq/TypeSystem.cs

@ -0,0 +1,64 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace MongoDB.Driver.Linq {
// taken from http://msdn.microsoft.com/en-us/library/bb546158.aspx
internal static class TypeSystem {
internal static Type GetElementType(Type seqType) {
Type ienum = FindIEnumerable(seqType);
if (ienum == null) { return seqType; }
return ienum.GetGenericArguments()[0];
}
private static Type FindIEnumerable(Type seqType) {
if (seqType == null || seqType == typeof(string)) {
return null;
}
if (seqType.IsArray) {
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
}
if (seqType.IsGenericType) {
foreach (Type arg in seqType.GetGenericArguments()) {
Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
if (ienum.IsAssignableFrom(seqType)) {
return ienum;
}
}
}
Type[] ifaces = seqType.GetInterfaces();
if (ifaces != null && ifaces.Length > 0) {
foreach (Type iface in ifaces) {
Type ienum = FindIEnumerable(iface);
if (ienum != null) { return ienum; }
}
}
if (seqType.BaseType != null && seqType.BaseType != typeof(object)) {
return FindIEnumerable(seqType.BaseType);
}
return null;
}
}
}

3
DriverOnlineTests/DriverOnlineTests.csproj

@ -119,6 +119,9 @@
<Compile Include="Jira\CSharp93Tests.cs" /> <Compile Include="Jira\CSharp93Tests.cs" />
<Compile Include="Jira\CSharp98Tests.cs" /> <Compile Include="Jira\CSharp98Tests.cs" />
<Compile Include="Jira\CSharp100Tests.cs" /> <Compile Include="Jira\CSharp100Tests.cs" />
<Compile Include="Linq\MongoLinqFindQueryTests.cs" />
<Compile Include="Linq\MongoQueryableTests.cs" />
<Compile Include="Linq\MongoQueryProviderTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Samples\MagicDiscriminatorTests.cs" /> <Compile Include="Samples\MagicDiscriminatorTests.cs" />
</ItemGroup> </ItemGroup>

69
DriverOnlineTests/Linq/MongoLinqFindQueryTests.cs

@ -0,0 +1,69 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
namespace MongoDB.DriverOnlineTests.Linq {
[TestFixture]
public class MongoLinqFindQueryTests {
private class C {
public ObjectId Id { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
private MongoServer server;
private MongoDatabase database;
private MongoCollection<C> collection;
[TestFixtureSetUp]
public void Setup() {
server = MongoServer.Create("mongodb://localhost/?safe=true");
server.Connect();
database = server["onlinetests"];
collection = database.GetCollection<C>("linqtests");
collection.Drop();
collection.Insert(new C { X = 1, Y = 11 });
collection.Insert(new C { X = 2, Y = 12 });
collection.Insert(new C { X = 3, Y = 13 });
collection.Insert(new C { X = 4, Y = 14 });
collection.Insert(new C { X = 5, Y = 15 });
}
[Test]
public void TestQueryXEquals1() {
var query = from c in collection.AsQueryable<C>()
where c.X == 1
select c;
var count = 0;
foreach (var c in query) {
Assert.AreEqual(1, c.X);
count++;
}
Assert.AreEqual(1, count);
}
}
}

75
DriverOnlineTests/Linq/MongoQueryProviderTests.cs

@ -0,0 +1,75 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NUnit.Framework;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
namespace MongoDB.DriverOnlineTests.Linq {
[TestFixture]
public class MongoQueryProviderTests {
private class C {
public ObjectId Id { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
private MongoServer server;
private MongoDatabase database;
private MongoCollection collection;
[TestFixtureSetUp]
public void Setup() {
server = MongoServer.Create("mongodb://localhost/?safe=true");
server.Connect();
database = server["onlinetests"];
collection = database["linqtests"];
}
[Test]
public void TestConstructor() {
var provider = new MongoQueryProvider(collection);
}
[Test]
public void TestCreateQuery() {
var expression = collection.AsQueryable<C>().Expression;
var provider = new MongoQueryProvider(collection);
var query = provider.CreateQuery<C>(expression);
Assert.AreSame(typeof(C), query.ElementType);
Assert.AreSame(provider, query.Provider);
Assert.AreSame(expression, query.Expression);
}
[Test]
public void TestCreateQueryNonGeneric() {
var expression = collection.AsQueryable<C>().Expression;
var provider = new MongoQueryProvider(collection);
var query = provider.CreateQuery(expression);
Assert.AreSame(typeof(C), query.ElementType);
Assert.AreSame(provider, query.Provider);
Assert.AreSame(expression, query.Expression);
}
}
}

67
DriverOnlineTests/Linq/MongoQueryableTests.cs

@ -0,0 +1,67 @@
/* Copyright 2010-2011 10gen 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.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NUnit.Framework;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
namespace MongoDB.DriverOnlineTests.Linq {
[TestFixture]
public class MongoQueryableTests {
private class C {
public ObjectId Id { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
private MongoServer server;
private MongoDatabase database;
private MongoCollection collection;
[TestFixtureSetUp]
public void Setup() {
server = MongoServer.Create("mongodb://localhost/?safe=true");
server.Connect();
database = server["onlinetests"];
collection = database["linqtests"];
}
[Test]
public void TestConstructorWithOneArgument() {
var provider = new MongoQueryProvider(collection);
var iqueryable = (IQueryable) new MongoQueryable<C>(provider);
Assert.AreSame(typeof(C), iqueryable.ElementType);
Assert.AreSame(provider, iqueryable.Provider);
}
[Test]
public void TestConstructorWithTwoArguments() {
var queryable = collection.AsQueryable<C>();
var iqueryable = (IQueryable) new MongoQueryable<C>((MongoQueryProvider) queryable.Provider, queryable.Expression);
Assert.AreSame(typeof(C), iqueryable.ElementType);
Assert.AreSame(queryable.Provider, iqueryable.Provider);
Assert.AreSame(queryable.Expression, iqueryable.Expression);
}
}
}
Loading…
Cancel
Save