首页 > 解决方案 > 在 Entity Framework Core 中获取与条件的关系

问题描述

我正在开发一个带有 .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()and 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()and时,它可以工作(但没有我想要实现的条件),但如果我保留它们,它会返回错误:Take()Select()

{System.ArgumentException:1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable方法“System.Collections.Generic.IEnumerable”的“System.Collections.Generic.IEnumerable 1[Dal.Models.Message]”类型的表达式1[Dal.Models.Message] _ToEnumerable[Message](System.Collections.Generic.IEnumerable1[Dal.Models.Message])'参数名称:在 System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument 的 arg0(MethodBase 方法,ExpressionType nodeKind,表达式参数,ParameterInfo pi,String methodParamName,String argumentParamName,Int32 索引)在 System.Linq .Expressions.Expression.Call(MethodInfo method, Expression arg0) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitor.ProjectionExpressionVisitor.VisitSubQuery(SubQueryExpression expression) at Remotion.Linq.Clauses.Expressions.SubQueryExpression.Accept(ExpressionVisitor visitor) at System.Linq。 Expressions.ExpressionVisitor.Visit(表达式节点)在 Microsoft.EntityFrameworkCore.Query.ExpressionVisitor.RelationalProjectionExpressionVisitor.Visit(表达式表达式)在 System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](ReadOnlyCollection1 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, IDiagnosticsLogger1 个记录器,在 Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler 中键入 contextType。<>c__DisplayClass15_0 1.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func1 个编译器在 Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func 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](IQueryable1 source) 在 Features.Topic .TopicMessagesQueries.getTopic(RbbContext context, Int32 entitiesFetchLimit, Int32 topicId) 在 TopicQueries.cs: 第 20 行在 Features.Topic.TopicService.getTopic(RbbContext context, ITopicQueries topicQueries, Int32 topicId) 在 TopicService.cs: 第 43 行在 Features.Topic .TopicController.Get(Int32 id) 在 TopicController.cs:line 34}

我也想从消息中获取用户关系,我尝试Select()在里面添加另一个t.Messages,但这也没有用。

我在 google/stackoverflow 中找不到具有 2 级关系和关系条件的查询的类似引用。

这里的任何人都可以指导我正确地进行此查询吗?

标签: c#sql-serverlinq.net-coreentity-framework-core

解决方案


您正在返回一个匿名类型。您必须将最终输出投影到您的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();

推荐阅读