Why You Need Reflector: SPList.DefaultView

I love Reflector. It has been invaluable in my various continuous learning efforts, poking around interesting code to improve my own. However, the need for Reflector spans beyond curiosity: when working with the SharePoint object model, it is an essential tool to understand what’s going on behind the scenes. It would be great if the API always did what you expect, or at least documented some of its less-than-obvious behavior, but it doesn’t and probably never will. It’s simply too big and “what you expect” is often a matter of opinion.

An excellent example is the behavior of SPList.DefaultView. As recently discovered by Andy Burns and earlier documented by Daniel Pavelescu, DefaultView returns a new SPView instance on every call. So instead of this:

list.DefaultView.ViewFields.Add("SomeField");
list.DefaultView.Update();

You need to do this:

SPView defView = list.DefaultView;
defView.ViewFields.Add("SomeField");
defView.Update();

Daniel was able to verify this behavior with the VS debugger, but it’s not clear why. Enter Reflector:

  1. SPList.DefaultView:
    public SPView DefaultView
    {
        get
        {
            return this.Views.DefaultView;
        }
    }
  2. SPViewCollection.DefaultView:
    internal SPView DefaultView
    {
        get
        {
            if (this.m_iDefaultViewIndex != -1)
            {
                return this[this.m_iDefaultViewIndex];
            }
            return null;
        }
    }
  3. SPViewCollection.Item[Int32]
    public SPView this[int iIndex]
    {
        get
        {
            if ((iIndex < 0) || (iIndex >= this.Count))
            {
                throw new ArgumentOutOfRangeException();
            }
            return new SPView(this, this.m_arrViewSchema, iIndex);
        }
    }

In three clicks we see exactly what the problem was and why. Since I usually have Reflector open, this is almost always faster than asking Google or MSDN. Of course some methods are obfuscated (code can’t be disassembled), but fortunately these are exceptions rather than the rule.

Exploring SPView.ViewFields

As another example of code not behaving as expected, consider the implementation of SPView.ViewFields:

public SPViewFieldCollection get_ViewFields()
{
    if (this.m_xdView != null)
    {
        while (this.m_bFullBlownXmlDoc)
        {
            return new SPViewFieldCollection(this, this.Node);
        }
    }
    return new SPViewFieldCollection(this, this.GetInnerXmlForNode("ViewFields"));
}

Given that a new collection is created each time, one might expect the following to add only the second field:

SPView defView = list.DefaultView;
defView.ViewFields.Add("FirstField");
defView.ViewFields.Add("SecondField");
defView.Update();

But, much to my surprise, both fields are added! So what’s going on here? Well, let’s explore a bit with PowerShell:

PS 1> $w = spw http://localhost
PS 2> $l = $w.Lists['Test']
PS 3> $dv = $l.DefaultView
PS 4> $vf1 = $dv.ViewFields
PS 5> $vf2 = $dv.ViewFields
PS 6> $vf1
Attachments
LinkTitle
PS 7> $vf1.Add('Editor')
PS 8> $vf2
Attachments
LinkTitle
Editor

So we store two different SPViewFieldCollection objects, update one, and the other is updated as well. In my mind, this begs two questions:

  1. Why doesn’t SPView just store a reference to a shared collection?
  2. How are the collections kept in sync?

It’s tough to guess why, but Reflector can probably help us figure out how. Let’s start with one of the constructors called by ViewFields:

internal SPViewFieldCollection(SPView view, string innerXml)
{
    this.m_View = view;
    this.m_ViewStyle = null;
    this.m_strInnerXml = innerXml;
}

Not much to see here, other than the captured reference to the parent SPView. Using Reflector’s Analyze function (Ctrl-R), we see that m_strInnerXml is also referenced in the SchemaXml property:

Analyzing SPViewFieldCollection.m_strInnerXml

Which disassembles as…

public string get_SchemaXml()
{
    if (this.m_node == null)
    {
        return this.m_strInnerXml;
    }
    return this.m_node.InnerXml;
}

So the value passed in is only used if m_node has not been set. Ctrl-R again:

Analyzing SPViewFieldCollection.m_node

Now we’re getting somewhere. The members rely on EnsureViewFields() to keep up-to-date:

private void EnsureViewFields()
{
    if (this.m_node != null)
    {
        this.m_iCount = this.m_node.ChildNodes.Count;
    }
    else
    {
        this.m_View.EnsureFullBlownXmlDocument();
        this.InitViewFields(this.m_View.Node);
    }
}

And the node connection is set up in InitViewFields using its view’s Node. So SPViewFieldCollection doesn’t even have an internal data store! Instead, everything operates against m_node, which is attached to the parent view. Thus changes to one collection are immediately reflected in other collections created from the same SPView.

I still prefer to capture and reuse the SPViewFieldCollection, but at least now we know that it’s safe not to and, more interestingly, why that’s the case.

Time to share! When has Reflector saved you from SharePoint headaches?

Advertisements

3 Responses to “Why You Need Reflector: SPList.DefaultView”

  1. Links (2/22/2008) « Steve Pietrek - Everything SharePoint Says:

    […] Why You Need Reflector: SPList.DefaultView […]

  2. novolocus.com » WSS Practice: Create a list, columns and view programmatically Says:

    […] – Keith Dahlby has also investigated, and looked at why this is happening. Although he let’s the code talk for itself, the essence of it all is that each call to […]

  3. Tim Nugiel Says:

    You saved my sanity, i spent countless hours trying to update the default view of a list. I should have known to fire up reflector (saved me many times before) thanks!!


Comments are closed.

%d bloggers like this: