Веб-сайт ASP.NET + сеанс с поддержкой SQL

Мы используем проект веб-сайта ASP.NET с динамической компиляцией и недавно перешли в состояние сеанса, поддерживаемого SQL Server, и начали получать странную ошибку. Я понял, что вызывает это, но я не знаю, как лучше всего решить эту проблему.

Шаги для воспроизведения на локальном хосте (с включенным сеансом sql):

  • Поместите объект, определенный в AppCode, допустим, это DanObject в сеанс.

    Session["x"] = новый DanObject();

  • (Сеанс сериализуется в базу данных)

  • Измените что-то в коде приложения, чтобы сайт перекомпилировался при следующем запросе.
  • Сделайте запрос на любую страницу, которая обращается к сеансу

Ошибка: «Не удалось найти сборку App_SubCode_CS.rmdbqb81, версия = 0.0.0.0, культура = нейтральная, PublicKeyToken = null».

Происходит то, что каждый раз, когда AppCode компилируется, он входит в сборку со случайным именем. Когда моя сессия сериализуется в первый раз, AppCode называется AppCode_123. Когда я изменил свое приложение, AppCode теперь AppCode_456. Однако сеанс, хранящийся в моей базе данных, имеет объект, определенный в AppCode_123. Когда Session пытается бинарно десериализовать DanObject, он взрывается, потому что не может найти AppCode_123.

Каков самый простой способ исправить это?
*Пожалуйста, не говорите о переходе на веб-приложение — наша кодовая база огромна, и на данный момент это невозможно :)


person dan    schedule 04.03.2011    source источник


Ответы (3)


Используйте явное пространство имен вокруг DanObject

person dove    schedule 04.03.2011
comment
+1: самое простое решение для огромного приложения. Конечно, я бы все равно рекомендовал не хранить полные объекты в сеансе, но похоже, что этот проект уже стал кошмаром производительности, поэтому, вероятно, на данный момент это не имеет значения. - person NotMe; 04.03.2011
comment
@ Крис Лайвли, точно, я начал писать о переходе в отдельную библиотеку и о том, что вы сериализуете, но думаю, что пока буду ПОЦЕЛУЙ, отвечая. - person dove; 04.03.2011
comment
Это не сработало. Я думаю, что BinarySerializer использует AssemblyQualifiedName, которое включает случайное имя в AppCode. - person dan; 04.03.2011

Хорошо, второе самое простое решение:
переместите код для этого класса вместо отдельной библиотеки сборки. Таким образом, когда сериализатор увидит класс, он будет иметь известное хорошее имя для работы. Конечно, это лишает вас возможности вносить изменения непосредственно в каталог app_code... чего вам в любом случае делать не следует.

Третье самое простое (и лучшее) решение:
не помещать объекты в сеанс. Если вам нужна пара свойств, это одно, сохраните эти свойства как обычные строки.

<slight_rant>

Серьезно, размещение полных объектов в сеансе — это НЕ то, для чего он был создан.

См. мой ответ здесь ( Сеансы и хранение объектов ) для получения дополнительной информации о причины, по которым вы не хотите этого делать.

И последнее примечание: эта проблема кажется проблемой только потому, что вы, ребята, сохраняете сеанс И вносите изменения непосредственно в код на веб-сервере. Перестань. Внесите свои изменения (и протестируйте их) локально, затем предварительно скомпилируйте и разверните свой сайт, наконец, сделайте сеанс недействительным после его развертывания. Делайте это по стандартному графику выпуска, чтобы все знали, когда наступает время простоя.

</slight_rant>
person NotMe    schedule 04.03.2011
comment
мы не редактируем код приложения непосредственно на сервере. У нас есть несколько серверов на балансировщике нагрузки, и мы хотим иметь возможность отправлять код в середине дня, не отключая весь сайт... Итак, мы выводим сервер из ротации, загружаем на него новый код, а затем возвращаем его обратно. в ротации. Проблема в том, что объекты, сериализованные из AppCode, взрываются. - person dan; 04.03.2011

Проблема была в том, что у нас были отдельные папки в App_Code для разных языков кода. Вот почему в сообщении об ошибке было сказано "App_SubCode_CS..." вместо "App_Code...", потому что конкретный класс был взят из папки кода CS (для кода С#). Каждая из этих папок с кодом (определенная в web.config) компилируется в собственную сборку.

Обычно, когда у вас нет нескольких папок с кодом AppCode, ASP.NET может сериализовать и десериализовать объекты, скомпилированные в разное время или на разных серверах, поскольку имя сборки («AppCode.randomstring») хранится вместе с именем класса в сериализованном вывод. При десериализации платформа вызывает System.Type.GetType() для имени сборки + класса, и эта функция имеет особый случай для обработки имен сборок, начинающихся с App_Code, где, если она не находит сборку с точно таким же именем, но находит ту, которая начинается с App_Code он использует эту сборку для загрузки класса.

Когда у вас есть языковые папки в App_Code, сгенерированные сборки называются так: App_SubCode_FOLDERNAME.randomstring, и фреймворк, похоже, не обрабатывает этот случай. Итак, если у вас есть 2 веб-сервера, совместно использующих сеанс с поддержкой Sql, класс Foo компилируется как "App_SubCode_FN.random1, Foo" на сервере A и "App_SubCode_FN.random2, Foo" на сервере B. Если пользователь получает Foo в своем сеансе с сервера A, а затем его следующий запрос отправляется на сервер B , сервер B не сможет десериализовать Foo, так как не сможет найти сборку с именем "App_SubCode_FN.random1".

Проблема может быть решена путем избавления от вашего старого кода VB (что позволит скомпилировать код приложения в единую сборку, с которой .NET лучше работает) или путем написания пользовательского SerializationBinder + пользовательской реализации сеанса, поддерживаемого сервером Sql.

person dan    schedule 16.06.2011