Customizing PowerShell to be a Read-Eval-Print-Loop for SharePoint

May 12 2010 3 comments

In my previous post I briefly ventured into the world of on-the-fly SharePoint administration using PowerShell. Today, I want to take that idea a step further.

The first thing that popped into my mind when considering using PowerShell was the fact that doing anything interesting with the SharePoint API involves internally creating a SPRequest, which by the looks of things is a scarce resource that must be Dispose()d properly.
In an environment where the using construct doesn’t exist and objects are often accessed and allocated without actually storing them into variables, resource leakage can become a problem.

The team responsible for the SharePoint 2010 commandlets has opted to solve this issue with a two-pronged approach: first, by default, all IDisposables returned by the SharePoint API are automatically disposed at the end of the pipeline – that is, the objects exist only “as long as they’re needed” and then get disposed. Second, if and when the need arises to hold on to such objects for a longer period of time, there is a commandlet that creates a new container. All SharePoint commandlets that produce IDisposables accept such a container, and if you provide one, you can then call another commandlet to dispose things at your discretion.

As usual, since I’m learning the ropes here, I’m taking a different, but similar approach. I’m going to create a bunch of tools that will assist me in managing the disposable heroes of the SharePoint API, but I happen to prefer implicit over explicit, particularly when the explicit way feels verbose.

For starters, I’m not too fond of typing this all the time:

$site = New-Object "Microsoft.SharePoint.SPSite" `
    –ArgumentList "http://site-name"

The long incantation gets old, real fast. Keep in mind that we’re doing this interactively, so we’re likely to repeat this a lot.

I’m going to constrain myself to creating types from the Microsoft.SharePoint namespace for now:

function SPNew([string] $typeName) {
    New-Object "Microsoft.SharePoint.$typeName" –ArgumentList $args
}

This may feel a bit strange if you’re a C# developer – after all, I’m not explicitly returning anything, and I’m not even declaring $args anywhere. The PowerShell philosophy is that it’s a shell language, and the default action in a shell is to output things. Functions don’t really return values so much as they output them. Put two and two together, and you’ll realize that any expressoin that produces a value will also output – or return, if you prefer – that value unless you assign it. As for $args, it’s a variable that exists in PowerShell functions and scripts by default. If you have declared formal parameters for your function (such as $typeName in my example above), $args will contan every argument that did not match a formal parameter. Otherwise, $args will contain all the arguments.

Practically, this means that we can now create SharePoint objects by invoking:

SPNew SPSite "http://site-name"

All arguments passed after the type name will be passed to the constructor of the specified type. Much better!

Nice as that is, it doesn’t solve the dispose issue yet. For that, we need some sort of store for all the objects we create:

function New-GenericList([type] $type) {
    $baseType = [System.Collectinos.Generic.List``1]
    $listType = $baseType.MakeGenericType($type)
    New-Object $listType
}

$__sp_disposable_object_store__ = New-GenericList IDisposable

function SPStoreDisposables() {
    $args | foreach {
        if( $_ –is [IDisposable]) {
            $__sp_disposable_object_store__.Add($_)
        }
    }
    $args
}

Now we have a list to contain our disposables (I chose the difficult-looking name to make it unlikely to collide with other variable names). We still need to make sure the objects we create get stored there, so let’s modify SPNew a bit:

function SPNew([string] $typeName) {
    SPStoreDisposables (New-Object "Microsoft.SharePoint.$typeName" `
        –ArgumentList $args)
}

Since StoreDisposables returns the items it stores, we don’t need to explicitly return anything.

Finally, we also need a way to dispose the contents of our list:

function SPDisposeAll() {
    $__sp_disposable_object_store__ | foreach { $_.Dispose() }
    $__sp_disposable_object_store__.Clear()
}

Since I tend to create SPSite objects more often than anything else, let’s define one more convenience function:

function SPSite() {
    SPNew SPSite $args
}

Now I can open a site with the following incantation:

SPSite "http://site-name"

and get rid of all the IDisposables I’ve allocated:

SPDisposeAll

This is pretty neat, but there’s one more facet to this problem we haven’t solved: if we get, say, SPWeb objects from calling SPSite.OpenWeb, we still need to track them manually. I have an idea for that, though — so stay tuned for the next part, wherein I venture into the PowerShell Type Extension mechanism. :-)

Have fun!

Popularity: 1% [?]

3 comments to “Customizing PowerShell to be a Read-Eval-Print-Loop for SharePoint”

  1. Sami Poimala says:

    Nice helpers! You might want to have a look at Start-SPAssignment and Stop-SPAssignement Cmdlets also. http://cglessner.blogspot.com/2009/11/sharepoint-2010-powershell-and.html

  2. Lauri Kotilainen says:

    Those would be the commandlets I was referring to. I was going to refer to them by name, but I posted the entry in a hurry and forgot to add the references. :-)

  3. [...] This post was mentioned on Twitter by SharePoint Dev Wiki. SharePoint Dev Wiki said: #SharePoint #Link Customizing PowerShell to be a Read-Eval-Print-Loop for SharePoint http://ow.ly/17oMFB [...]

Leave a Reply