与实体框架核心

时间:2018-05-02 20:28:19

标签: c# sql-server linq .net-core 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小于给定参数的消息
  3. 我检查过并发现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的表达式'[Dal.Models.Message]'方法'System.Collections.Generic.IEnumerable 1[Dal.Models.Message] _ToEnumerable[Message](System.Collections.Generic.IEnumerable 1 [ Dal.Models.Message])'System.Linq.Expressions中的参数名:arg0 at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase方法,ExpressionType nodeKind,Expression arguments,ParameterInfo pi,String methodParamName,String argumentParamName,Int32 index) System.Linq.Expressions上的Remotion.Linq.Clauses.Expressions.SubQueryExpression.Accept(ExpressionVisitor visitor)中的Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.ProjectionExpressionVisitor.VisitSubQuery(SubQueryExpression表达式)中的.Expression.Call(MethodInfo方法,Expression arg0)。 System.Linq.Expressions.ExpressionVisitor.VisitAndConvert [T]上的Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit(Expression expression)中的ExpressionVisitor.Visit(Expression node)(ReadOnly Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery中的Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler。&lt;&gt; c__DisplayClass15_0 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编译器的集合1.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func 1记录器,类型contextType)[TResult在TopicQueries.cs中的Features.Topic.TopicMessagesQueries.getTopic(RbbContext context,Int32 entitiesFetchLimit,Int32 topicId)中的(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](IQueryable 1 source):Features.Topic.TopicService.getTopic(RbbContext context)中的第20行,TopicService.cs中的ITopicQueries topicQueries,Int32 topicId):TopicController.cs中的Features.Topic.TopicController.Get(Int32 id)第43行:第34行

    我还想从消息中获取用户关系,我尝试在Select()内添加另一个t.Messages,但这也无效。

    我无法在google / stackoverflow中找到类似的关于具有2级关系和关系条件的查询的参考资料。

    这里的任何人都可以指导我这个查询吗?

1 个答案:

答案 0 :(得分:1)

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