1

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. ;)

Vilx-
  • 104,512
  • 87
  • 279
  • 422

2 Answers2

2

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.

Sam Holder
  • 32,535
  • 13
  • 101
  • 181
  • Yes, I'm aware of this, but then I'd need to implement a custom comparison for every object that I intend to put there. I'd like something more elegant, if that's possible. – Vilx- May 19 '11 at 09:33
  • yeah, I figured that would be the case, just pointing out the obvious in case a better solution doesn't come along, and someone else passes by in the future. – Sam Holder May 19 '11 at 10:02
  • I'll accept your answer, although instead of overriding Equals I created a custom IEqualityComparer. Less invasive. – Vilx- May 19 '11 at 13:31
2

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)

Michael Christensen
  • 1,768
  • 1
  • 13
  • 11
  • See update. And I don't see why it's intended to operate like that. In fact, I'd say it's pretty counter-intuitive. If I put the same thing in twice, I expect to get the same thing out twice. Is it documented anywhere? – Vilx- May 19 '11 at 09:33