IMongoCollection <T>выдает, когда я передаю предикат для Fun c - PullRequest
1 голос
/ 22 апреля 2020

Я пытаюсь провести модульное тестирование 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 не является mockable с Moq), и я предпочел бы слой косвенности по сравнению с моим доступом к данным, поэтому я попытался выполнить рефакторинг в класс репозитория, например:

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), он возвращает первый объект без каких-либо проблем.

Полный стек исключений ниже.

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)

1 Ответ

1 голос
/ 23 апреля 2020

Единственная реализация метода Find(...), которую вы можете использовать здесь, это (документы) :

public static IFindFluent<TDocument, TDocument> Find<TDocument>(this IMongoCollection<TDocument> collection, Expression<Func<TDocument, bool>> filter, FindOptions options = null)

Описана разница между Func и Expression<Func> здесь . В основном GetCollection возвращает ссылку на коллекцию 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 выглядит хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...