Variations.Current.UserAccessibleLabels can spawn lots of SPWebs

Be careful when using the OOTB SharePoint Publishing property Variations.Current.UserAccessibleLabels as it spawns SPWebs for every call.

To demo this I’ve created a web part and added 2 different code blocks to it with monitored scopes. The first one creates 3 objects with a direct reference to the UserAccessibleLabels property. The second one uses a local field which is only assigned once.

namespace TestVariations.TestAccessibleLabels
{
    [ToolboxItemAttribute(false)]
    public partial class TestAccessibleLabels : WebPart
    {
        private ReadOnlyCollection<VariationLabel> userAccessibleLables = null;

        public ReadOnlyCollection<VariationLabel> UserAccessibleLables
        {
            get
            {
                if (userAccessibleLables == null)
                    userAccessibleLables = Variations.Current.UserAccessibleLabels;

                return userAccessibleLables;
            }
        }

        public TestAccessibleLabels()
        {
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                var resultBuilder = new StringBuilder();

                using (new SPMonitoredScope("Variations.Current.UserAccessibleLabels"))
                {
                    var variationLabels = Variations.Current.UserAccessibleLabels;
                    var variationLabels2 = Variations.Current.UserAccessibleLabels;
                    var variationLabels3 = Variations.Current.UserAccessibleLabels;

                    foreach (var label in variationLabels)
                    {
                        resultBuilder.Append(label.Title);
                    }

                    foreach (var label2 in variationLabels2)
                    {
                        resultBuilder.Append(label2.Title);
                    }

                    foreach (var label3 in variationLabels3)
                    {
                        resultBuilder.Append(label3.Title);
                    }
                }

                using (new SPMonitoredScope("this.UserAccessibleLabels"))
                {
                    var variationLabels4 = this.UserAccessibleLables;
                    var variationLabels5 = this.UserAccessibleLables;
                    var variationLabels6 = this.UserAccessibleLables;

                    foreach (var label4 in variationLabels4)
                    {
                        resultBuilder.Append(label4.Title);
                    }

                    foreach (var label5 in variationLabels5)
                    {
                        resultBuilder.Append(label5.Title);
                    }

                    foreach (var label6 in variationLabels6)
                    {
                        resultBuilder.Append(label6.Title);
                    }
                }

                resultLabel.Text = resultBuilder.ToString();
            }
            catch (Exception ex)
            {
                resultLabel.Text = ex.Message;
            }
        }
    }
}

If we take a look at the developer dashboard, we can see SPWebs getting generated for each variation:

spweb spawns

Using ILSpy we can see that SharePoint opens web objects to check the current user’s access.

ilspy variations

When taking a look at the loading time, we see that the impact on the total loading time is quite high when using lots of direct references.

spweb monitored scopes

For the conclusion we can say that using the OOTB SharePoint property is the way to go when you only have to use it once. However when you’re using this property all over the place in your code it’s best to keep the “result” of the UserAccessibleLabels in a private field.

Create list views across webs using PowerShell or code

Sometimes you’ll want to have one basic list, which is accessible on all your subsites. One way to do this is using the content query web part. This however, blocks the functionalities a standard list view offers. So instead of the content query web part, we’ll use the basic XsltListViewWebPart. An important note: the standard ListViewWebPart class cannot work across your different webs inside your site collection. You have to use the Xslt one.

First, get an instance of the list you want to reference in your subsite:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]

Then, get the web part manager of the page of your subsite where you want to insert the list view:

$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);

Now that we’ve got both required objects, it’s time to create the XsltListViewWebPart:

$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id

It’s very important the WebId is set or otherwise you’ll get a nasty error like this:

“List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user.”

Last but not least, add the web part to your page:

$wpm.AddWebPart($ListViewWebPart, "Left", 0)

In one flow the script looks like this:

$webWithList = Get-SPWeb http://weburl
$list = $webWithList.Lists["ListName"]
$targetWeb = Get-SPWeb http://weburl/subweb
$wpm = $targetWeb.GetLimitedWebPartManager("default.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared);
$ListViewWebPart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$ListViewWebPart.ListId = $list.id
$ListViewWebpart.Title = $list.title
$ListViewWebPart.WebId = $list.parentweb.id
$wpm.AddWebPart($ListViewWebPart, "Left", 0)

If you would like for instance feature stapling, you can use the following code block to your web feature activation:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var web = properties.Feature.Parent as SPWeb;
    using (var rootWeb = web.Site.OpenWeb())
    {
        var list = rootWeb.Lists[Constants.ListName];
        string file = web.RootFolder.WelcomePage;
        var webPartManager = web.GetLimitedWebPartManager(file, PersonalizationScope.Shared);
        var webPart = new XsltListViewWebPart
        {
            ListId = list.ID,
            Title = list.Title,
            WebId = list.ParentWeb.ID
        };
        webPartManager.AddWebPart(webPart, "Left", 0);
    }
}

SPGridView: Paging does not work

The SPGridView control supports the cascading style sheets that are built into Microsoft SharePoint Foundation 2010. You can present data in a grid view that closely resembles the view that SharePoint Foundation 2010 uses to display data from lists. You can also sort and group rows.

This class does not support auto-generated columns. This means that you must explicitly bind each column using SPBoundField.

There are a lot of tutorials on this amazing UX control, so I won’t go into detail on how to implement it. A nice reference is the post on msdn blog by Paul Robinson.

However, I came across an issue by enabling the paging functionality. Whilst playing around with the SPGridViewPager, I had to bind it to my SPGridView control:

SPGridViewPager pager = new SPGridViewPager();
pager.GridViewId = oGrid.ID;
this.Controls.Add(pager);

So far so good. All I had to do now was assign an ID to my SPGridView:

oGrid.ID = Guid.NewGuid().ToString();

Seems legit, no? Apparently, your control’s ID is overwritten every time your web part is loaded, aswell on postbacks:

Postback:

As a solution you can use ViewState to track your ID or use an oldschool static one. When the webpart has it’s correct ID, the paging will trigger the correct page of the DataSet. More info on the lifecycle of a SharePoint webpart can be found here.