Как запросы LINQ к Entity Framework передают даты на SQL Server? - PullRequest
4 голосов
/ 24 февраля 2010

Я использую два запроса LINQ для доступа к моей базе данных с использованием Entity Framework:

Первый хранит токен в базе данных и дает ему 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-to-entity. Linq-to-objects отлично работает!

Ответы [ 2 ]

2 голосов
/ 24 февраля 2010

Вы вставляете токен и получаете сравнение на серверах с одинаковым часовым поясом?

Лучше, если вы никогда не будете хранить локальное время в базе данных. С одной стороны, это устраняет проблему постановки в очередь в одном часовом поясе и смены очереди в другом. А во-вторых, код работает круглый год: как сейчас, ваш код будет «отказывать» «по замыслу» две ночи в году: когда начинается летнее время и когда заканчивается летнее время.

Лучше использовать DateTime.UtcNow как для операций постановки в очередь, так и для операций удаления из очереди.

Теперь, как говорится, вряд ли это проблема, с которой вы сталкиваетесь. Но чтобы узнать тип сравнения, который происходит в вашем случае, нам нужно знать тип столбца Expiry, как объявлено в базе данных и как объявлено в EF. Это строка, дата-время или дата-смещение?

1 голос
/ 25 февраля 2010

Благодаря предложению Кристиана я решил:

Этот код:

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;

отображает этот SQL:

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-to-Entities преобразует сравнение, используя DateTime.Now в GetDate() в базе данных. Поэтому любые различия во времени между серверами могут вызвать проблемы.

Мое решение состояло в том, чтобы исключить сравнение дат из исходного запроса и выполнить его с LINQ-to-Objects, когда все вернется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...