1

基本的に、ポストバック間でオブジェクトを永続化するために ViewState を使用しようとしています。さらに、これらのオブジェクトをプライベート クラスのプロパティ内にカプセル化しようとしています。

これの目的は、以前は静的フィールドを使用してオブジェクトを永続化していた既存のコードをリファクタリングすることであり、これは明らかに問題を引き起こしていました。

以下は、Web フォームの HTML の非常に単純な例です。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication3.WebForm1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        <br />
        <asp:Button ID="Button1" runat="server" Text="Button" />

    </div>
    </form>
</body>
</html>

そして、これは最初のコード ビハインドの非常に単純な例です (私が書いたものではありません)。

using System;
using System.Web.UI;

namespace WebApplication3
{
    public partial class WebForm1 : Page
    {
        private static string[,] _my2DArray;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                _my2DArray = new string[3,3];

                for (var i = 0; i < 3; i++)
                {
                    for (var j = 0; j < 3; j++)
                    {
                        _my2DArray[i, j] = Guid.NewGuid().ToString();
                    }
                }
            }

            TextBox1.Text = _my2DArray[1, 1];
        }
    }
}

これにより、ポストバック間でオブジェクトを永続化するという目標が達成されますが、他のすべての間でもオブジェクトが永続化されるため、問題が発生します。

私のアイデアは、このオブジェクトを使用する多くの分離コードがあり、実際のアプリケーションにはさらに多くのオブジェクトがあるため、このようなものにリファクタリングすることでした。できるだけコードを変更せずに問題を修正したいと考えています (resharper を使用すると、フィールドを別のクラスに移動して、それらをプロパティに変換することが簡単になります。次に、プロパティ コードを修正して、プライベート バッキングではなく Viewstate と連携するようにしました。分野):

using System;
using System.Web.UI;

namespace WebApplication3
{
    public partial class WebForm1 : Page
    {
        internal class MyClass
        {
            private readonly WebForm1 _webForm1;

            internal MyClass(WebForm1 webForm1)
            {
                _webForm1 = webForm1;
            }

            internal string[,] My2DArray
            {
                set { _webForm1.ViewState["_my2DArray"] = value; }
                get { return (string[,])_webForm1.ViewState["_my2DArray"]; }
            }
        }

        private MyClass _myClass;

        protected void Page_Load(object sender, EventArgs e)
        {
            _myClass = new MyClass(this);

            if (!IsPostBack)
            {
                _myClass.My2DArray = new string[3,3];

                for (var i = 0; i < 3; i++)
                {
                    for (var j = 0; j < 3; j++)
                    {
                        _myClass.My2DArray[i, j] = Guid.NewGuid().ToString();
                    }
                }
            }

            TextBox1.Text = _myClass.My2DArray[1, 1];
        }
    }
}

これはほとんど機能しました。2D 配列だけでなく、このメソッドを使用して期待どおりに機能する文字列の int と boolean があります。しかし、2 次元配列を混在させると、次のエラーが発生します。

このページの状態情報は無効であり、破損している可能性があります。

ソース エラー:

[該当するソース行はありません]

スタックトレース:

[EndOfStreamException: Unable to read beyond the end of the stream.]
   System.IO.BinaryReader.ReadByte() +9616466
   System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +44
   System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +380
   System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +141

[ArgumentException: The serialized data is invalid.]
   System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +205
   System.Web.UI.ObjectStateFormatter.Deserialize(String inputString) +337
   System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Deserialize(String serializedState) +4
   System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState) +37
   System.Web.UI.HiddenFieldPageStatePersister.Load() +147

[ViewStateException: Invalid viewstate. 
    Client IP: 127.0.0.1
    Port: 
    Referer: http://localhost:27314/WebForm1.aspx
    Path: /WebForm1.aspx
    User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36
    ViewState: /wEPDwULLTE0MDM4MzYxMjMPFgIeBkZvb2JhcmRkem7xFcvXyGXVXNDDKHSutQ9j/EhfbWTpihkzXIlH66A=]

[HttpException (0x80004005): The state information is invalid for this page and might be corrupted.]
   System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError) +198
   System.Web.UI.ViewStateException.ThrowViewStateError(Exception inner, String persistedState) +14
   System.Web.UI.HiddenFieldPageStatePersister.Load() +251
   System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +106
   System.Web.UI.Page.LoadAllState() +43
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +8431
   System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +253
   System.Web.UI.Page.ProcessRequest() +78
   System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) +21
   System.Web.UI.Page.ProcessRequest(HttpContext context) +49
   ASP.webform1_aspx.ProcessRequest(HttpContext context) in c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\37286ff3\6dc3cf92\App_Web_t5caigbu.0.cs:0
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +100
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

私は困惑しています。VS で新しい Webforms ソリューションを作成し、上記の HTML + コード ビハインドをそれぞれのファイルにコピーすると、私が持っているものとまったく同じになります。

Visual Studio 2010 を使用し、.Net 4 をターゲットにしています。

このメソッドを使用してポストバック間で 2D 配列のオブジェクトを永続化するときにこのエラーが発生する理由についてのアイデアはありますが、他のタイプのオブジェクトはありませんか?

乾杯

4

1 に答える 1

1

私が覚えている限り、多次元配列は、ViewState で使用されるシリアル化モードである XmlSerializable ではありません。

セッションに関しては、InProc モードを使用している限り、オブジェクトはメモリに保存されるため、この問題は発生しません。データベース ストレージのような xml シリアル化を必要とするセッション メカニズムを使用しようとすると、問題は同じになります。

もし可能なら:

  • 2 次元配列のタイプを変更します。たとえばString[][]、多くのページで配列の配列 ( ) に変更します...
  • ページに共通の階層がある場合は、配列をオーバーライドSaveViewStateしてカスタムのシリアル化を実行することができます。LoadViewState

試していませんが、オーバーライドする最初の試みは次のようになります

protected override void LoadViewState(object savedState)
{
    base.LoadViewState(savedState);

    string[,] my2DArray = myMethodForDeserializing2DArrayFromCustomFormat(ViewState["my2DArray"]);
    ViewState["my2DArray"] = my2DArray;
}

protected override object SaveViewState()
{
    string my2DArraySerialized = myMethodForSerializing2DArrayToCustomFormat(ViewState["my2DArray"]);
    ViewState["my2DArray"] = my2DArraySerialized;
    return base.SaveViewState();
}
于 2013-08-30T07:58:54.853 に答える