当我传入 Func<T, bool> 的谓词时,IMongoCollection<T> 抛出一个
IMongoCollection<T> throws an when I pass in a predicate for Func<T, bool>
我正在尝试对 ASP.NET 核心网络控制器进行单元测试。这是原始数据访问的样子:
var database = configuration.GetSection("MongoDb:Database").Value;
var mongoSettings = new MongoCollectionSettings() { AssignIdOnInsert = true };
var users = client.GetDatabase(database).GetCollection<User>("User", mongoSettings);
var user = users.Find(u => u.EmailAddress == emailAddress).SingleOrDefault();
这很好用。它不容易进行单元测试(Find
不能用 Moq 模拟),而且我更喜欢在我的数据访问上有一个间接层,所以我尝试重构到存储库 class 中,如下所示:
public interface IRepository<T>
{
T SingleOrDefault(Func<T, bool> predicate);
}
public class MongoRepository<T> : IRepository<T>
{
// ...
public T SingleOrDefault(Func<T, bool> predicate)
{
var objects = client.GetDatabase(this.databaseName).GetCollection<T>(this.repositoryName, this.settings);
var toReturn = objects.Find(o => predicate(o) == true).SingleOrDefault();
return toReturn;
}
}
当我更新我的控制器以使用 MongoRepository<User>
时,它会抛出:
var usersRepo = new MongoRepository<User>(this.configuration, this.client);
var user = usersRepo.SingleOrDefault(u => u.EmailAddress == emailAddress);
具体来说,在版本库代码中,objects.Find(o => predicate(o) == true).SingleOrDefault();
抛出异常:An exception of type 'System.InvalidOperationException' occurred in MongoDB.Driver.dll but was not handled in user code: 'Invoke(value(System.Func
2[AutoDungeoners.Web.Models.User,System.Boolean]), {document}) 不被支持。 '`
我不知道如何解决这个问题。我怀疑我的谓词定义为 Func<T, bool>
是不正确的,因为如果我只是调用 objects.Find(o => true)
,它 returns 第一个对象没有任何问题。
下面是完整的异常堆栈。
An exception of type 'System.InvalidOperationException' occurred in MongoDB.Driver.dll but was not handled in user code: 'Invoke(value(System.Func`2[AutoDungeoners.Web.Models.User,System.Boolean]), {document}) is not supported.'
at MongoDB.Driver.Linq.Translators.PredicateTranslator.GetFieldExpression(Expression expression)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(BinaryExpression binaryExpression)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression`1 predicate, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.ExpressionFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.MongoCollectionImpl`1.CreateFindOperation[TProjection](FilterDefinition`1 filter, FindOptions`2 options)
at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](IClientSessionHandle session, FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass41_0`1.<FindSync>b__0(IClientSessionHandle session)
at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSession[TResult](Func`2 func, CancellationToken cancellationToken)
at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
at MongoDB.Driver.FindFluent`2.ToCursor(CancellationToken cancellationToken)
at MongoDB.Driver.IAsyncCursorSourceExtensions.SingleOrDefault[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)
at MongoDB.Driver.IFindFluentExtensions.SingleOrDefault[TDocument,TProjection](IFindFluent`2 find, CancellationToken cancellationToken)
您可以在此处使用的 Find(...)
方法的唯一实现是 (docs):
public static IFindFluent<TDocument, TDocument> Find<TDocument>(this IMongoCollection<TDocument> collection, Expression<Func<TDocument, bool>> filter, FindOptions options = null)
描述了 Func
和 Expression<Func>
之间的区别 here。基本上GetCollection
returns引用了Mongodb集合,这里Find
方法的作用是将表达式树转换成MongoDB查询。您的查询在数据库中执行的那一刻是您调用 SingleOrDefault()
的时候,这就是您将获得异常的地方。
由于 Func<T,bool>
和 Expression<Func<T,bool>>
之间没有隐式转换,您尝试使用 o => predicate(o) == true
创建另一个表达式并使您的代码可编译,但如前所述 - MongoDB 。 NET 驱动程序将无法弄清楚如何将此类表达式转换为 MongoDB 查询。
您需要将实施更改为:
public T SingleOrDefault(Expression<Func<T, bool>> predicate)
{
var objects = client.GetDatabase(this.databaseName).GetCollection<T>(this.repositoryName, this.settings);
var toReturn = objects.Find(predicate).SingleOrDefault();
return toReturn;
}
请记住,这仅适用于那些可以翻译成 MongoDB 查询语言的表达式。 u => u.EmailAddress == emailAddress
看起来不错。
我正在尝试对 ASP.NET 核心网络控制器进行单元测试。这是原始数据访问的样子:
var database = configuration.GetSection("MongoDb:Database").Value;
var mongoSettings = new MongoCollectionSettings() { AssignIdOnInsert = true };
var users = client.GetDatabase(database).GetCollection<User>("User", mongoSettings);
var user = users.Find(u => u.EmailAddress == emailAddress).SingleOrDefault();
这很好用。它不容易进行单元测试(Find
不能用 Moq 模拟),而且我更喜欢在我的数据访问上有一个间接层,所以我尝试重构到存储库 class 中,如下所示:
public interface IRepository<T>
{
T SingleOrDefault(Func<T, bool> predicate);
}
public class MongoRepository<T> : IRepository<T>
{
// ...
public T SingleOrDefault(Func<T, bool> predicate)
{
var objects = client.GetDatabase(this.databaseName).GetCollection<T>(this.repositoryName, this.settings);
var toReturn = objects.Find(o => predicate(o) == true).SingleOrDefault();
return toReturn;
}
}
当我更新我的控制器以使用 MongoRepository<User>
时,它会抛出:
var usersRepo = new MongoRepository<User>(this.configuration, this.client);
var user = usersRepo.SingleOrDefault(u => u.EmailAddress == emailAddress);
具体来说,在版本库代码中,objects.Find(o => predicate(o) == true).SingleOrDefault();
抛出异常:An exception of type 'System.InvalidOperationException' occurred in MongoDB.Driver.dll but was not handled in user code: 'Invoke(value(System.Func
2[AutoDungeoners.Web.Models.User,System.Boolean]), {document}) 不被支持。 '`
我不知道如何解决这个问题。我怀疑我的谓词定义为 Func<T, bool>
是不正确的,因为如果我只是调用 objects.Find(o => true)
,它 returns 第一个对象没有任何问题。
下面是完整的异常堆栈。
An exception of type 'System.InvalidOperationException' occurred in MongoDB.Driver.dll but was not handled in user code: 'Invoke(value(System.Func`2[AutoDungeoners.Web.Models.User,System.Boolean]), {document}) is not supported.'
at MongoDB.Driver.Linq.Translators.PredicateTranslator.GetFieldExpression(Expression expression)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(BinaryExpression binaryExpression)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression`1 predicate, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.ExpressionFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.MongoCollectionImpl`1.CreateFindOperation[TProjection](FilterDefinition`1 filter, FindOptions`2 options)
at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](IClientSessionHandle session, FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass41_0`1.<FindSync>b__0(IClientSessionHandle session)
at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSession[TResult](Func`2 func, CancellationToken cancellationToken)
at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
at MongoDB.Driver.FindFluent`2.ToCursor(CancellationToken cancellationToken)
at MongoDB.Driver.IAsyncCursorSourceExtensions.SingleOrDefault[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)
at MongoDB.Driver.IFindFluentExtensions.SingleOrDefault[TDocument,TProjection](IFindFluent`2 find, CancellationToken cancellationToken)
您可以在此处使用的 Find(...)
方法的唯一实现是 (docs):
public static IFindFluent<TDocument, TDocument> Find<TDocument>(this IMongoCollection<TDocument> collection, Expression<Func<TDocument, bool>> filter, FindOptions options = null)
描述了 Func
和 Expression<Func>
之间的区别 here。基本上GetCollection
returns引用了Mongodb集合,这里Find
方法的作用是将表达式树转换成MongoDB查询。您的查询在数据库中执行的那一刻是您调用 SingleOrDefault()
的时候,这就是您将获得异常的地方。
由于 Func<T,bool>
和 Expression<Func<T,bool>>
之间没有隐式转换,您尝试使用 o => predicate(o) == true
创建另一个表达式并使您的代码可编译,但如前所述 - MongoDB 。 NET 驱动程序将无法弄清楚如何将此类表达式转换为 MongoDB 查询。
您需要将实施更改为:
public T SingleOrDefault(Expression<Func<T, bool>> predicate)
{
var objects = client.GetDatabase(this.databaseName).GetCollection<T>(this.repositoryName, this.settings);
var toReturn = objects.Find(predicate).SingleOrDefault();
return toReturn;
}
请记住,这仅适用于那些可以翻译成 MongoDB 查询语言的表达式。 u => u.EmailAddress == emailAddress
看起来不错。