Получить отношения с условиями в Entity Framework Core - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над приложением с .net core 2 и Entity Framework Core 2.

У меня есть следующие модели:

Topic {
   int TopicID,
   List<Message> Messages,
   …
}

Message {
   int MessageID,
   int TopicID,
   Topic Topic,
   int UserID,
   User User,
   …
}

User {
   int UserID,
   …
}

Я хочу получить все данные для темы и ее отношений, так что если у меня есть тема с 3 сообщениями, каждое из которых написано другим пользователем, я получу следующий объект:

var topic = new Topic() {
   TopicID: 1,
   Messages: new Message[] {
      new Message() { MessageID: 1, TopicID: 1, UserID: 1, User: new User() { UserID: 1 } },
      new Message() { MessageID: 2, TopicID: 1, UserID: 2, User: new User() { UserID: 2 } },
      new Message() { MessageID: 3, TopicID: 1, UserID: 1, User: new User() { UserID: 3 } }
   }
}

Это было просто, и я сделал это со следующей реализацией:

return context.Topics
   .Include(t => t.Messages)
   .ThenInclude(m => m.User)
   .AsNoTracking()
   .SingleOrDefault(t => t.TopicID == topicId);

но теперь я хочу добавить некоторые условия к сообщениям:

  1. принимать только 2 сообщения одновременно
  2. передать параметр, указывающий, что я хочу принимать только сообщения, в которых messageID меньше заданного параметра

Я проверил и обнаружил, что Include() не имеет возможности использовать Take() и Where(), поэтому я попытался реализовать это с помощью Select():

return context.Topics
   .Where(t => t.TopicID == topicId)
   .Select(t => new {
      TopicId = t.TopicID,
      Messages = t.Messages
         .Where(m => m.MessageID < messageId)
         .Take(2)
   })
   .AsNoTracking()
   .SingleOrDefault();

Когда я удаляю Where() и Take() изнутри Select(), он работает (но без условий, которые я хочу реализовать), но если я их оставлю, он возвращает ошибки:

{System.ArgumentException: выражение типа 'System.Collections.Generic.IEnumerable 1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable 1 [Dal.Models.Message]' метода 'System.Collections.Generic.IEnumerable 1[Dal.Models.Message] _ToEnumerable[Message](System.Collections.Generic.IEnumerable 1 [Dal.Models. Сообщение]) 'Имя параметра: arg0 в System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument (метод MethodBase, ExpressionType nodeKind, аргументы выражения, ParameterInfo pi, String methodParamName, String аргументParamName, индекс Int32) в System.Linq.Expressions.Expression.Call. (Метод MethodInfo, Выражение arg0) в Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.ProjectionExpressionVisitor.VisitSubQuery (выражение SubQueryExpression) в Remotion.Linq.Clauses.Expressions.SubQueryExpression.Accept (выражение). Выражение .Visitor. Узел выражения) в Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit (выражение выражения) в System.Linq.Expressions.ExpressionVisitor.VisitAndConvert [T] (Rea Регистратор dOnlyCollection 1 nodes, String callerName) at Remotion.Linq.Parsing.RelinqExpressionVisitor.VisitNew(NewExpression expression) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.VisitNew(NewExpression newExpression) at System.Linq.Expressions.NewExpression.Accept(ExpressionVisitor visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit(Expression expression) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel) at Remotion.Linq.Clauses.SelectClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel) at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateQueryExecutor[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](QueryModel queryModel) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](Expression query, INodeTypeProvider nodeTypeProvider, IDatabase database, IDiagnosticsLogger 1, введите contextType) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler. <> c__DisplayClass15_0 1.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func 1 компилятор) в Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCueryCueryCuCareCache 1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression) at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable 1 источник) в Features.Topic.TopicMessagesQueries.getTopic (контекст RbbContext, Int32 entityFetchLimit, Int32 topicId) в TopicQueries.cs: строка 20 в Features.Topic.TopicService.getTopic (контекст RbbContext, topicopuQuery в32) TopicService.cs: строка 43 в Features.Topic.TopicController.Get (идентификатор Int32) в TopicController.cs: строка 34}

Я также хочу получить отношение пользователей из сообщений, я попытался добавить еще Select() внутрь t.Messages, но это также не сработало.

Я не могу найти похожие ссылки в google / stackoverflow запросов с двухуровневыми отношениями и условиями отношений.

Может кто-нибудь здесь может направить меня, чтобы получить этот запрос правильно?

1 Ответ

0 голосов
/ 03 мая 2018

Вы возвращаете анонимный тип. Вы должны спроецировать окончательный результат на ваш Topic объект. Это может показаться немного уродливым, но оно должно попасть в базу данных только один раз и получить только необходимые записи. Запустите профилировщик, чтобы увидеть полученный запрос.

   return context.Topics
   .Where(t => t.TopicID == topicId)
   .Select(t => new {
                   TopicId = t.TopicID,
                   Messages = t.Messages
                               .Where(m => m.MessageID < messageId)
                               .Take(2)
           }).AsNoTracking()
             .AsEnumerable().Select(x => new Topic
                                  {
                                   TopicId = x.TopicId ,
                                   Bookings = x.Messages.ToList()
                                   }).SingleOrDefault();
...