Я использую веб-приложение и службу Windows на том же компьютере, что и Appfabric. Оба приложения повторно используют один и тот же код DAL (dll), основанный на EF (Entity Framework) Code-First и обращающийся к одному и тому же кешу в Appfabric. Код в службе Windows реализован как задание как часть Quartz.Net
Веб-приложение должно поддерживать несколько запросов, а Windows обслуживает несколько потоков (планировщик и события). В обоих случаях общая dll-библиотека DAL создает объект DbContext для каждого сеанса http и ContextID потока или просто ContextID потока для более поздних версий. DAL использует EFCachingProviders из здесь. Кроме того, в моем решении EF используется оптимистический параллелизм со столбцами временных меток и IsRowVersion в сопоставлении.
Как указано здесь, преимущество наличия кеша 2-го уровня состоит в том, чтобы иметь доступ к представлению исходного состояния между процессами! Но это, похоже, не работает для меня, я получаю «OptimisticConcurrencyException» в моем случае использования следующим образом:
- перезапустите кластер кеша, перезапустите службу Windows, перезапустите iis -> с чистого листа :)
- Используя веб-приложение (firefox), я вставляю новый объект A со ссылкой на существующий объект B. Я вижу новую строку в базе данных. Все ок.
- Используя webapp в другом браузере (chrome) = новый сеанс, я вижу новый объект.
- Затем служба Windows пытается выполнить некоторую фоновую обработку и пытается обновить объект B. Это приводит к исключению OptimisticConcurrencyException. По-видимому, процесс в службе Windows содержит версию объекта B с устаревшей версией строки.
- Если я перезапущу службу Windows, она снова попробует ту же логику и будет работать без исключения ....
Таким образом, оба приложения являются многопоточными, используют один и тот же код DAL, подключаются к одной базе данных, к одному и тому же кластеру кеша и к одному и тому же кешу. Я ожидал, что обновление и вставка будут в кеше appfabric. Я ожидал, что контекст EF службы Windows будет использовать новейшую информацию. Почему-то кажется, что это кеш 1-го уровня, хранящий старую информацию ... или что-то еще идет не так.
Пожалуйста посоветуй...
Обновить
Хорошо, покопавшись, я исправил проблему с обновлением моей службы Windows. Каждый объект Manager с запросами к DAL использует DbContext, привязанный к его идентификатору процесса + идентификатору потока. Итак, в функции Execute моего Quartz Job все менеджеры (с разными типами объектов) должны использовать один и тот же DbContext, который создается первым менеджером.
Проблема заключалась в том, что после завершения функции DbContext не был удален (что происходит автоматически в диспетчере DbContext на основе сеанса HTTP). Таким образом, в следующий раз, когда задание было выполнено, был найден и использован тот же DbContext, который к тому времени уже был датирован (старый кеш первого уровня ???). Кэш 2-го уровня не должен быть проблемой, потому что он является общим и ДОЛЖЕН содержать новейшие объекты ... если они есть.
Итак, эта часть исправлена.
Новая проблема
Таким образом, веб-приложение создает новый объект A, обновляет существующий объект B, служба Windows теперь работает и может без проблем обновлять существующий (измененный) объект B.
Проблема: когда я обновляю веб-приложение, оно не видит изменений (выполненных службой Windows) объекта B ....
Итак, если веб-приложение изменило счет на 5, через 10 минут служба Windows изменит счет на 6, и я открываю веб-приложение в том же или новом окне / браузере, я все равно вижу 5, а не 6!
Перезапуск веб-приложения (iis) не помогает, также iisreset не помогает. Когда я делаю Restart-CacheCluster .... он работает и показывает 6 ....
Похоже, что элемент находится в кеше. Служба Windows обновляет его, но не делает недействительным элемент, который является старым и используется веб-приложением ....
Или ... хотя это тот же объект, веб-приложение имеет свою собственную запись в кеше, а приложение win имеет свою собственную запись (которая становится недействительной) ....
Который из?
Решение
Я решил это сам. Похоже, что оболочка EF использует строку запроса в качестве ключа для хранения элементов в кеше. Таким образом, два разных запроса (неважно, исходят ли они из двух разных приложений, использующих один и тот же распределенный кеш или одно и то же приложение), ссылающиеся на одни и те же данные в базе данных, будут иметь разные ключи (разные строки запроса) и поэтому разные места в кеше. Может, дело не в этом черно-белом, а в чем-то вроде этого ...
Я не думаю, что внутри используется какой-то алгоритм, чтобы проверить, касается ли запрос существующих кешированных объектов.
Это вызывает мою проблему, когда моя служба Windows выполняет обновление, а веб-приложение по-прежнему видит старую версию из кеша, которую можно решить только с помощью команды Restart-CacheCluster.
Итак, как я это исправил: Моя служба Windows - это пакетное задание, запускаемое планировщиком Quartz. После этого очищаю весь кеш:
private void InvalidateCache()
{
try
{
DataCache myCache = ...
foreach (String region in myCache.GetSystemRegions())
{
myCache.ClearRegion(region);
}
}
catch (Exception ex)
{
eventLog.WriteEntry("InvalidateCache exception : " + ex.Message);
}
}