Vilx-
Vilx-

Reputation: 106970

Why does ASP.NET viewstate lose identity of serialized objects?

Short question: When I put one and the same instance of an object inside viewstate twice, upon deserialization there are two instances. I want there to be just one instance. Can this be done and how?

Wordy explanation:

Consider the following code:

public partial class MyControl : System.Web.UI.UserControl
{
    [Serializable]
    class MyClass
    {
        public string x;
    }
    public void Page_Load(object sender, EventArgs e)
    {
        if (this.IsPostBack)
        {
            MyClass a = (MyClass)this.ViewState["a"];
            MyClass b = (MyClass)this.ViewState["b"];
            MessageManager.Show((a == b).ToString(), MessageSeverity.Debug);
        }
        else
        {
            var x = new MyClass() { x = "stackoverflow" };
            this.ViewState["a"] = x;
            this.ViewState["b"] = x;
            MessageManager.Show("Init", MessageSeverity.Debug);
        }
    }
}

When it is run, and a postback is initiated, I get the message "false". That is, although I put a single object inside the viewstate, it got serialized twice. This can be verified by inspecting viewstate contents.

If I try to put cross-references objects in viewstate, then each item gets serialized as separate graph. To illustrate:

public partial class MyControl : System.Web.UI.UserControl
{
    [Serializable]
    class MyClass
    {
        public string x;
        public MyClass other;
    }
    public void Page_Load(object sender, EventArgs e)
    {
        if (this.IsPostBack)
        {
            MyClass a = (MyClass)this.ViewState["a"];
            MyClass b = (MyClass)this.ViewState["b"];
            MessageManager.Show((a.other == b).ToString(), MessageSeverity.Debug);
            MessageManager.Show((a.other.other == a).ToString(), MessageSeverity.Debug);
        }
        else
        {
            var a = new MyClass() { x = "stack" };
            var b = new MyClass() { x = "overflow" };
            a.other = b;
            b.other = a;
            this.ViewState["a"] = a;
            this.ViewState["b"] = b;
            MessageManager.Show("Init", MessageSeverity.Debug);
        }
    }
}

Now I get the messages "False" and "True" (in that order). Again, inspecting Viewstate shows, that each object got serialized twice. What gives? I checked the source of System.Web.UI.StateBag with ILSpy, but it just pushes all the values into an ArrayList, and that has no special serializing code either. So whoever is serializing the viewstate (System.Web.UI.ObjectStateFormatter?) is somehow taking each object and serializing as a separate graph... why??? And can I work around it?

Update: The reason I need this is that the same object will be persisted by two separate components, and upon deserialization I would like to check if they have the same object. (Or rather, they both store collections of objects themselves, and I need to synchronize those collections).

I could implement custom comparison in a dozen different ways, but since I want to do this for arbitrary objects, it kinda gets tricky. ;)

Upvotes: 1

Views: 693

Answers (2)

Michael Christensen
Michael Christensen

Reputation: 1786

Viewstate is a dictionary, that is how it's intended to operate. If you want to store the same value and only have it once you should write to the same "key" which will overwrite the previous value.

Also did you try your equality comparison by doing a.Equals(b)

Upvotes: 2

Sam Holder
Sam Holder

Reputation: 32946

you could override the == operator for your class, or override Equals and do the comparison via a.Equals(b) to see if they are the same, rather than relying on the default equality based on address.

Upvotes: 2

Related Questions