【问题标题】:Compare between DateTime from Client and DateTime Saved in Database比较来自客户端的日期时间和保存在数据库中的日期时间
【发布时间】:2020-10-30 14:06:45
【问题描述】:

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

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

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.

【问题讨论】:

  • 我有点怀疑你说的是否正确,因为DateTimeTimOfDay 都是结构,不能为空,所以你不应该看到NullReferenceException。如果其中一个声明为Nullable&lt;DateTime&gt;DateTime?,您可以获得NullReferenceException,但事实并非如此。您能提供异常、消息和堆栈跟踪的完整详细信息吗?
  • @RandRandom 我正在尝试提到的解决方案,我想为您提供额外的详细信息!
  • @RandRandom 您现在可以查看异常详情。
  • 更新了我的答案,抱歉回复晚了,放假了。

标签: c# datetime .net-core aspnetboilerplate ef-core-3.1


【解决方案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()或@987654332 @,在此之后,您的表达式将针对 IEnumerable&lt;T&gt; 而不是 IQueryable&lt;T&gt;

  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 );

【讨论】:

    【解决方案2】:

    在开始时,我将尝试将查询参数(来自客户端的日期)放在查询之外并获取具体值。 例如

    int lastMessageHours = latestMessageDateTime.TimeOfDay.Hours;
    DateTime lastMessageDate = latestMessageDateTime.Date;
    

    然后传递给查询

    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 > lastMessageDate
                                       && x.CreationTime.TimeOfDay !=null 
                                       
                                       && x.CreationTime.TimeOfDay.Hours > latestMessageHours
    
                                       )
                                       && x.listOfMessages.Any(x => x.HCSId== HCSId)
    
                                       ).OrderBy(message => message.CreationTime).ToListAsync();
    

    如果这没有帮助,CreationTime 如果它是可空类型,则它具有值。

    x.CreationTime.HasValue
    

    我还建议将此查询拆分为多个方法。为此查询编写扩展方法:

    public static IQueryable<Message> NewerThan(this IQueryable<Message> query, DateTime value)
    {
    int lastMessageHours = latestMessageDateTime.TimeOfDay.Hours;
    DateTime lastMessageDate = latestMessageDateTime.Date;
    return query.Where(x=> x.CreationTime != null
    
                                   && x.CreationTime.Date > lastMessageDate
                                   && x.CreationTime.TimeOfDay !=null 
    
                                   && x.CreationTime.TimeOfDay.Hours > latestMessageHours );
    }
    

    查询将更具可读性。

    【讨论】:

    • 上述解决方案不起作用,两个值都不能为空!
    • 使用 x.CreationTime.TimeOfDay.Hours 或 x.CreationTime.Hours 比较 DateTime 有什么区别吗?我认为两者都应该返回相同的值。我刚刚分析了 sql server 函数映射,没有 TimeOfDay.Hours 映射到任何函数。这是分析源代码的链接github.com/dotnet/efcore/blob/…
    猜你喜欢
    • 1970-01-01
    • 2015-07-20
    • 2013-03-02
    • 2012-01-20
    • 1970-01-01
    • 2012-04-20
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    相关资源
    最近更新 更多