首页 > 解决方案 > 比较来自客户端的日期时间和保存在数据库中的日期时间

问题描述

我试图在 Date,Hours,Minutes,Seconds 中的两个日期之间进行比较,但是当我调用 x.CreationTime.TimeOfDay 时出现了可为空的异常。

我认为这个链接答案会解决我的问题,但是在我找到解决方案之后,问题仍然存在 这是我的查询:

public async Task<List<MessageDto>> getMessageHistory(long userId, string code, long HCSId, long Role,DateTime latestMessageDateTime , DateTime messageDateBeforeSeeMore)
        {
var result =await _repository.GetAllIncluding(x => x.listOfMessages)
                                       .Where(x => ((x.receiverID == userId|| x.CreatorUserId == userId)
                                       && x.code== code) && (
                                        x.CreationTime != null
                                        
                                       && x.CreationTime.Date > latestMessageDateTime.Date
                                       && x.CreationTime.TimeOfDay !=null 
                                       
                                       && x.CreationTime.TimeOfDay.Hours > latestMessageDateTime.TimeOfDay.Hours /*<===== this cause the problem if I remove it the query working fine*/

                                       )
                                       && x.listOfMessages.Any(x => x.HCSId== HCSId)

                                       ).OrderBy(message => message.CreationTime).ToListAsync();

return result ;
}

更新:

异常详情:

-       $exception  {"Object reference not set to an instance of an object."}   System.NullReferenceException


+       Data    {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}


  at MyProject.ChatAppService.MessageAppService.<getMessageHistory>d__19.MoveNext() in D:\WorkSpace\MyProject\aspnet-core\src\MyProject.Application\ChatAppService\MessageAppService.cs:line 346

+       TargetSite  {Void MoveNext()}   System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}

更新 2: 我遵循@RandRandom 提到的步骤

我发现的低于异常


The LINQ expression 'DbSet<Message>
    .Where(m => m.receiverID == __citizenId_0 || m.CreatorUserId == __userId_0 && m.code == __code_1 && DbSet<HCSMessages>
        .Where(h => EF.Property<Nullable<long>>(m, "Id") != null && EF.Property<Nullable<long>>(m, "Id") == EF.Property<Nullable<long>>(h, "messageId"))
        .Any(h => h.HCsId == __HCSId_2) && m.CreationTime > __messageDateBeforeSeeMore_3)
    .Where(m => m.CreationTime.Date > __latestMessageDateTime_Date_4)
    .Where(m => m.CreationTime.TimeOfDay.Hours > __latestMessageDateTime_TimeOfDay_Hours_5)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

标签: c#datetime.net-coreaspnetboilerplateef-core-3.1

解决方案


问题:

异常没有包含更多有用信息的内部异常?

尝试回答:

一个疯狂的猜测是,唯一实际上可以为 null 的东西是x.listOfMessages因为它是您正在使用的唯一引用类型,其他一切都是结构。

您可以通过将查询拆分为多个部分并一个接一个地分别具体化每个查询来“轻松”缩小错误范围。

所以首先像这样重写你的查询:

public async Task<List<MessageDto>> getMessageHistory(long userId, string code, long HCSId, long Role,DateTime latestMessageDateTime , DateTime messageDateBeforeSeeMore)
{
    IEnumerable<MessageDto> result = await _repository.GetAllIncluding(x => x.listOfMessages);
    
    result = result.Where(x => x.receiverID == userId || x.CreatorUserId == userId);
    result = result.Where(x => x.code == code);

    //dropped the null checks for DateTime and TimeOfDay, since NULL should be impossible
    result = result.Where(x => x.CreationTime.Date > latestMessageDateTime.Date );
    result = result.Where(x => x.CreationTime.TimeOfDay.Hours > latestMessageDateTime.TimeOfDay.Hours);
    
    result = result.Where(x => x.listOfMessages.Any(x => x.HCSId== HCSId));
    
    result = result.OrderBy(message => message.CreationTime);
    
    return (List<MessageDto>)result;
}

在此之后添加.ToListAsync()从顶部开始的每一行,并随着每个成功的步骤将其向下移动一个位置。

因此,在您的第一次测试中,将第一行更改为:

IEnumerable<MessageDto> result = await _repository.GetAllIncluding(x => x.listOfMessages).ToListAsync(); //added .ToListAsync()

对于第二个测试,ToListAsync()从第一行中删除并将其添加到第二行,第一行和第二行应该如下所示:

IEnumerable<MessageDto> result = await _repository.GetAllIncluding(x => x.listOfMessages);  //removed .ToListAsync()
result = result.Where(x => (x.receiverID == userId || x.CreatorUserId == userId)).ToListAsync(); //added .ToListAsync()

使用这种方法,您可以分别具体化每个条件,并且可以找出哪些条件会失败。

编辑:

发生“Update2”中的错误是因为您正在执行无法转换为 SQL 查询的操作。

一般来说,要解决此问题,您有两个选择

  1. 在本地运行不受支持的表达式,以跳过将表达式转换为 SQL 的必要性

    为此,您必须在不支持之前评估所有表达式,当枚举发生时会发生评估IQueryable,简单的方法是例如调用ToList(),ToArray()AsEnumerable(),在此之后您的表达式将针对 anIEnumerable<T>而不是IQueryable<T>

  2. 如果有合适的,请使用 DbFunctions - https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbfunctions - 或编写您自己的函数 - https://khalidabuhakmeh.com/add-custom -database-functions-for-entity-framework-core

但在你的情况下,我会质疑你的整体表达,为什么要单独比较日期和时间

    result = result.Where(x => x.CreationTime.Date > latestMessageDateTime.Date );
    result = result.Where(x => x.CreationTime.TimeOfDay.Hours > latestMessageDateTime.TimeOfDay.Hours);

而不是简单地比较DateTime结构?

    result = result.Where(x => x.CreationTime > latestMessageDateTime );

推荐阅读