代码之家  ›  专栏  ›  技术社区  ›  Damovisa

针对实体框架的LINQ查询如何与SQL Server通信日期?

  •  4
  • Damovisa  · 技术社区  · 16 年前

    我使用两个LINQ查询使用实体框架访问我的数据库:

    第一个将令牌存储在数据库中,并为其提供60秒超时:

    string tokenHash = GetTokenHash();
    Token token = new Token()
    {
        Client = client,
        Expiry = DateTime.Now.AddSeconds(60),
        UserId = userId,
        TokenHash = tokenHash,
    };
    context.AddToToken(token);
    context.SaveChanges();
    

    第二个检查是否存在尚未过期的匹配令牌:

    var item = (from t in datasource.SmsToken
                where t.Client.Id == clientId
                && t.UserId == userId
                && t.TokenHash == tokenHash
                && t.Expiry > DateTime.Now
                select t);
    bool success = item.Count() >= 1;
    

    我遇到的问题是,这在测试服务器上运行得非常好。现在,它已移动到另一个环境,不再工作。

    我已经转储了很多调试信息,所有内容似乎都匹配。如果我移除 t.Expiry > DateTime.Now 在这种情况下,它工作得很好。因此,问题在于日期比较。

    新服务器已在Windows中设置了不同的日期格式和全球化设置。我想这就是问题所在,这让我很困惑。

    我本以为可以使用LINQ和EF一致地存储和检索日期。我不应该有任何格式问题,我应该吗?有人能告诉我这里出了什么问题吗?


    更新:

    有趣的是,我将检索代码替换为以下代码,从而获得正确的行为:

    var item = (from t in datasource.SmsToken
                where t.Client.Id == clientId
                && t.UserId == userId
                // && t.TokenHash == tokenHash
                // && t.Expiry > DateTime.Now
                select t).ToList();
    
    var matchingToken = (from t in item
                         where t.TokenHash == tokenHash
                         && t.Expiry > DateTime.Now
                         select t).FirstOrDefault();
    
    bool success = matchingToken != null;
    

    这向我表明,问题在于Linq内部的日期与实体之间的比较。Linq to objects工作正常!

    2 回复  |  直到 16 年前
        1
  •  2
  •   Remus Rusanu    16 年前

    是否在具有相同时区的服务器上插入令牌并检索比较?

    最好不要在数据库中存储本地时间。一方面,它消除了在一个时区排队和在另一个时区排队的问题。第二,代码全年都在运行:正如现在一样,您的代码每年都会在两个晚上“设计”失败:夏令时开始和夏令时结束。

    更好地利用 DateTime.UtcNow

    话虽如此,这不太可能是你面临的问题。但要知道在您的案例中发生的比较类型,我们需要知道数据库中声明的和EF中声明的到期列的类型。它是字符串,是datetime还是datetimeoffset?

        2
  •  1
  •   Damovisa    16 年前

    感谢克里斯蒂安的建议,我已经解决了:

    var item = (from t in datasource.SmsToken
                where t.Client.Id == clientId
                && t.UserId == userId
                && t.TokenHash == tokenHash
                && t.Expiry > DateTime.Now
                select t);
    bool success = item.Count() >= 1;
    

    exec sp_executesql N'SELECT 
    1 AS [C1], 
    [Extent1].[Id] AS [Id], 
    [Extent1].[UserId] AS [UserId], 
    [Extent1].[TokenHash] AS [TokenHash], 
    [Extent1].[Expiry] AS [Expiry], 
    [Extent1].[ClientId] AS [ClientId]
    FROM [dbo].[Token] AS [Extent1]
    WHERE ([Extent1].[ClientId] = @p__linq__16)
    AND ([Extent1].[UserId] = @p__linq__17)
    AND ([Extent1].[TokenHash] = @p__linq__18)
    AND ([Extent1].[Expiry] > (GetDate()))',
    N'@p__linq__16 nvarchar(7),@p__linq__17 nvarchar(9),@p__linq__18 nvarchar(16)',@p__linq__16=N'OTPTest',@p__linq__17=N'Test User',@p__linq__18=N'7?????:??????'
    

    AND ([Extent1].[Expiry] > (GetDate()))',

    这意味着 LINQ到实体使用 DateTime.Now 到 GetDate() 在数据库中。因此,服务器之间的任何时间差都可能导致问题。

    我的解决方案是不在原始查询中进行日期比较,而是在所有内容返回时使用LINQ to对象进行比较。