1

我们正在使用具有动态编译的 ASP.NET 网站项目,并且最近移动到 SQL Server 支持的会话状态并开始出现奇怪的错误。我已经弄清楚是什么原因造成的,但我不知道解决它的最佳方法。

在 localhost 上重现的步骤(启用 sql 会话):

  • 放置一个在 AppCode 中定义的对象,假设它是一个DanObject进入会话。

    会话["x"] = new DanObject();

  • (Session被序列化到数据库)

  • 修改应用代码中的某些内容,导致站点在下一次请求时重新编译
  • 向访问会话的任何页面发出请求

错误是“无法找到程序集 'App_SubCode_CS.rmdbqb81, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'。”

发生的情况是,每次 AppCode 被编译时,它都会进入一个随机命名的程序集。当我的 Session 第一次被序列化时,AppCode 恰好被命名为 AppCode_123。当我修改我的应用程序时,AppCode 现在是 AppCode_456。但是,存储在我的数据库中的 Session 在 AppCode_123 中定义了一个对象。当 Session 尝试对 DanObject 进行二进制反序列化时,它会因为找不到 AppCode_123 而崩溃。

我可以解决这个问题的最简单方法是什么?
*请不要说切换到 Web 应用程序——我们的代码库很大,目前不可行 :)

4

3 回答 3

1

在 DanObject 周围使用显式命名空间

于 2011-03-04T17:32:53.047 回答
0

问题是我们App_Code为不同的代码语言有单独的文件夹。这就是为什么在错误消息中,它说“App_SubCode_CS...”而不是“App_Code...”,因为特定类来自 CS 代码文件夹(用于 c# 代码)。这些代码文件夹(在 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 上编译。如果用户在其会话中从服务器 A 获取 Foo,然后他的下一个请求转到服务器 B,则服务器B 将无法反序列化 Foo 因为它找不到名为"App_SubCode_FN.random1".

该问题可以通过摆脱旧的 VB 代码(允许将应用程序代码编译成一个 .NET 更适合使用的程序集)或编写自定义 SerializationBinder + Sql Server 支持会话的自定义实现来解决。

于 2011-06-16T05:33:40.143 回答
0

好的,第二个最简单的解决方案:
移动这个类的代码,而不是一个单独的程序集库。这样,当序列化程序看到该类时,它将有一个已知的好名称可以使用。当然,这消除了您直接对 app_code 目录进行编辑的能力......无论如何您都不应该这样做。

第三个最简单(也是最好的)解决方案:
不要将对象放入会话中。如果您需要几个属性,那是一回事,将这些属性存储为常规字符串。

<slight_rant>

说真的,将完整的对象放入会话中并不是它的目的。

有关您不想这样做的原因的更多信息,请参阅我的答案(会话和存储对象)。

最后一点:这个问题似乎只是一个问题,因为你们正在坚持会话并直接对 Web 服务器上的代码进行更改。别那样做。在本地进行更改(并测试它们),然后预编译和部署您的站点,最后在部署后使会话无效。按照标准化的发布时间表执行此操作,以便每个人都知道停机时间。

</slight_rant>
于 2011-03-04T18:38:13.777 回答