【问题标题】:RavenDB workaround for nested LINQ expressionRavenDB 嵌套 LINQ 表达式的解决方法
【发布时间】:2012-11-18 23:25:39
【问题描述】:

我在 RavenDB 中存储了一组简单的对象:

public class Question
{
    public string Id { get; set; }
    public DateTime CreatedOn { get; set; }
    public ICollection<User> Supporters { get; set; }
    public ICollection<Answer> Answers { get; set; }
}

public class Answer 
{
    public string Id { get; set; }
    public bool IsOfficial { get; set; }
}

现在我想查询 RavenDB 给我一组问题,首先按支持者数量排序,然后按条件排序 - 如果问题有任何官方答案,然后按问题创建日期排序。所以我写了一个查询:

var questions = DocumentSession.Query<Question>().AsQueryable(); 
questions = questions
                .OrderByDescending(x => x.Supporters.Count)
                .ThenByDescending(x => x.Answers.Any(a => a.IsOfficial)) //EDIT: source of exception
                .ThenByDescending(x => x.CreatedOn)
                .Take(15);                
var result = questions.ToList();

抛出异常:

System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'

当我使用 linq-to-objects 并简单地将 .ToList() 添加到第一行时,查询在逻辑上是正确的并且有效:

var questions = DocumentSession.Query<Question>().Tolist().AsQueryable();
// next lines stay unchanged

由于性能问题,我不想这样做(此更改强制在过滤之前将所有问题从数据库加载到内存中)。

如何在不影响性能的情况下使其工作?也许 shell 我定义了一个索引?那么它应该是什么样子呢?

【问题讨论】:

  • 您是否尝试过不使用ToList().AsQueryable() 的查询,例如:var questions = DocumentSession.Query&lt;Question&gt;().OrderByDescending();
  • 在哪一行抛出异常?是否会在调用 ToList() 时发生,或者当您尝试访问 Count of Supporters,或调用 Any() 或其他地方时会发生这种情况吗?我同意 MGA 的观点,即不需要 .AsQueryable()。我也认为 Count 和 Any() 的索引是个好主意。
  • @Bear Alexander:我已经编辑了问题并指出了错误的来源。 AsQueryable() 确实不需要。索引线索似乎很有趣。您可以根据我的情况发布这样的索引示例,以及保留我的逻辑并返回所需结果的修改后的查询吗?

标签: c# linq ravendb


【解决方案1】:

用于您的目的的自定义索引基本上将是您的类的重新创建,其中包含额外的字段(以及一些支持它的逻辑)。您似乎不想为当前类添加更多字段,是否可以向您的项目添加更多类?

这是一个例子:

public class Question_WithAnyOfficial: AbstractIndexCreationTask<Question>
{
    public class Question_WithAnyOfficial()
    {
        Map = questions => from question in questions
                           // New Anonymous Type
                           select new
                           {
                                Id = question.Id,
                                CreatedOn = question.CreatedOn,
                                Supporters = question.Supporters,
                                Answers = question.Answers,
                                AnyOfficial = question.Answers.Where(a => a.IsOfficial).Any()
                           };
    }
}

那么就可以这样查询了:

var questions = DocumentSession.Query<Question_WithAnyOfficial>()
                .OrderByDescending(x => x.Supporters.Count)
                .ThenByDescending(x => x.AnyOfficial)
                .ThenByDescending(x => x.CreatedOn)
                .Take(15)
                .ToList();         

不要忘记,您必须在应用启动时注册索引。

【讨论】:

  • 感谢您的努力。根据您的回复,我创建了索引。你的代码对我不起作用,但给了我一个线索。实际上,我对我的项目中的更多课程感到满意。由于某些原因,我不想将冗余字段添加到我的域对象中,并且同样会非规范化我的数据。
【解决方案2】:

Raven 不支持在 LINQ 查询中进行这样的计算,所以这应该可以工作(删除了问题子句):

var questions = DocumentSession.Query<Question>()
                .OrderByDescending(x => x.Supporters.Count)
                //.ThenByDescending(x => x.Answers.Any(a => a.IsOfficial))
                .ThenByDescending(x => x.CreatedOn)
                .Take(15);                
var result = questions.ToList();

如果您想包含该逻辑,您需要在您的类中添加一个名为AreAllAnswersOfficial(或类似名称)的字段。然后你可以把它放在子句中:

var questions = DocumentSession.Query<Question>()
                .OrderByDescending(x => x.Supporters.Count)
                .ThenByDescending(x => x.AreAllAnswersOfficial)
                .ThenByDescending(x => x.CreatedOn)
                .Take(15);                
var result = questions.ToList();

【讨论】:

  • 您好,再次感谢您。我知道哪条线路是问题的根源。我一直在想我必须做什么样的改变才能保持这种逻辑。那个额外的字段似乎没问题,但是还有其他选择吗,一些自定义索引呢?
  • Jarek,是的 - 如果您不想在文档中包含该属性“AreAllAnswersOfficial”,您当然可以使用简单的地图索引来进行计算。事实上很容易,你应该有问题。
【解决方案3】:

根据 Bear Alexander 的回复,我是这样做的:

public class QuestionByAnyOfficial : AbstractIndexCreationTask<Question, QuestionByAnyOfficial.Result>
{
    public class Result
    {
        public string Id;
        public bool AnyOfficial;
        public int SupportersCount;
        public DateTime CreatedOn;
    }

    public QuestionByAnyOfficial()
    {
        Map = questions => from question in questions                               
                           select new
                              {
                                  Id = question.Id,                                              
                                  AnyOfficial = question.Answers.Any(a => a.IsOfficial),
                                  SupportersCount = question.Supporters.Count,
                                  CreatedOn = question.CreatedOn
                              };            
    }
}

var questionIds = DocumentSession.Query<QuestionByAnyOfficial.Result, QuestionByAnyOfficial>()
                       .OrderByDescending(x => x.SupportersCount)
                       .ThenByDescending(x => x.AnyOfficial)
                       .ThenByDescending(x => x.CreatedOn)
                       .Take(NumberOfQuestions)
                       .Select(x => x.Id);
var questions = DocumentSession.Load<Question>(questionIds);
var result = questions.ToList();

它有效,我相信它比我的原始版本更有效。如果它可以以任何更优雅的方式完成,我会很感激任何想法。问候。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-22
    相关资源
    最近更新 更多