PowerShell 3.0–Now with Property Unrolling!

by oising 16. March 2012 22:37

There are many new improvements to the language and parser in v3 (some of which I hope to cover over the next few posts) but one of my favourites is what Microsoft are calling singleton/array enumeration (or something equally obtuse.) I am hereby christening it “Property Unrolling” as it works similarly to how PowerShell does automatic collection unrolling when piping an enumerable (list, collection, array.)

This powershell 3.0 technique is where you can take a variable that contains an array (or collection, list or anything else that is enumerable) like $myarray and if you want to access a property on each element in that array, you no longer need to use foreach-object with $_.propertyName to access it. Instead, you can simply type $myarray.propertyName and powershell will return that property from each element in the array, but only if the array itself does not have that property. For example if you had an array of strings, asking for $arr.length would return the length of the array, and not the length of each string. The best way to show this is with some examples:

Array of files

# the older way (still works)
dir | foreach-object { $_.lastwritetime } | sort

# now, here's the shortcut way for v3
(dir).lastwritetime | sort

XML

Here's an example on how working with XML just got ten times easier. Here's some XML:

    
                     
             42
        
                     
             43
        
                     
             44
        
    

Here's a script that dumps the prop value in each element:

# the older way
$xml = [xml]" ... "
$xml.root.element | foreach-object { $_.prop }

# the v3 way ;)
$xml.root.element.prop

This is such a time saver. Thank you Microsoft!

Tags:

PowerShell | PowerShell 3.0 | XML | Monad | Beta

Why VSTO 3.0, Visual Studio 2008, SharePoint+Windows Workflow And InfoPath Might Give You A Hernia

by oising 30. September 2008 18:19

This is just a post containing all the issues I’ve run into over the last six months while working with MOSS Workflows, InfoPath and Visual Studio 2008/VSTO 3.0. I’ve been using these tools in this combination for a while now, but it’s only the last six months where I decided to take a note every time I ran into something. Don’t get me wrong though. This is more of a post aimed at helping others in their quest for abdominal hull-integrity while working with these tools, not attacking the toolset. Ultimately, the Office Suite and the corresponding tools are working together better than ever before, but unfortunately with large teams working relatively independently you’re just going to get these kind of problems. To be brutally honest, I think VSTO 3.0 is a saddle-sore on the otherwise supple and leathery hide of Visual Studio 2008 and the disparity in their respective product quality is way too pronounced to be ignored.

Anyway, I’m not going to make much attempt to explain each case for non-SharePoint people, but if you’re involved in this area, you’ll know where I’m coming from. The following list contains Glitches and Gotchas.

Glitches

First though, the Glitches, in no particular order. These are the lovingly hand-crafted defects and “product quality issues” that will first cause your stomach muscles to contract somewhat, then violently spasm so hard that that first loop of intestine will pop out to nestle amongst the fat cells and veins in the fleshy meadow of your lower-epidermis. Don’t say I didn’t warn you.

Publish an Installable Form Template (requires Visual Studio)

vs2008-vs-not-installed-properly

If you try to publish a form as "an installable template (requires visual studio)" from within Visual Studio 2008 (even with sp1), VSTO, in its infinite wisdom tries to use visual studio 2005 devenv.exe executable to do it! And it fails spectacularly 'cos it's not installed, or if it is installed it's part of some vs-shell install for some other office app like office macros IDE, or Internet Explorer’s “script debugger” shell for example.

vs2008-infopath-vs2005

Publishing a Form to an explicitly-included Managed Path

Trying to publish a form to a SiteCollection sitting on an explicitly-included managed path fails with "getting the site content type columns failed." Workaround? Don’t publish forms to Managed Paths. It’s b0rked.

“An exception occurred during loading of business logic” - Publishing XSNs with CodeBehind to SharePoint

If you perform a cursory search for the title, you’ll find lots of herniated VSTO users having issues with this. InfoPath Forms Services on MOSS Enterprise is a fickle beast; a beast that will have you tucking those intestinal loops back into your torso with a pencil before you can say “FileNotFoundException, whuh?”

XSN files are actually CAB files. When Forms Services loads them up to be rendered into Browser Forms, it has to load the embedded DLL into memory. It has an extremely hard time doing this sometimes. Particularly when you store your XSN files in a subdirectory under your workflow feature, say like one called Forms. It appears that the form loads ok, but Forms Services is looking for the DLL in the root of your feature. Even sometimes if you have the XSN in the root of your feature, it still won’t find and extract/load the embedded DLL. The only way to avoid this it appears is to manually extract the DLL and place it in the root of the feature yourself after you have deployed the workflow solution.

Some strange things I’ve observed about Forms Services on MOSS Enterprise:

  • Uploading a codebehind-XSN  file via central admin automatically extracts dll/pdb into feature directory but...
  • Deploying a codebehind-XSN with  workflow feature does not extract dll/pdb.
  • Upgrading a codebehind-XSN with central admin creates new subdir in feature folder, modifies feature.xml to point to it but does not extract the form's codebehind dll/pdb into new subdir either.

These seem to be some rather serious looking issues to me.

VSTO Designer ToolPane Content Shrinks

Occasionally the Design ToolPane in VS2008/VSTO inexplicably changes size, with the contents of it being set at about 1cm wide.b0rked-vsto-toolpane

A restart of Visual Studio or close/reopen of the project usually fixes this.
Workflow Designer Screws Up Layout of Nested States

The workflow designer in VS 2008 (even sp1) will screw up the formatting of nested states in a state machine workflow - they get randomly offset to the left or right so they are half occluded by the containing state. Yay.

Views ToolPane Is Blank For a Form that has Views

Views created in infopath sometimes do not appear in vs2008 project built around that imported XSN. Weirdly, the toolpane is completely white/blank. A restart of Visual Studio or close/reopen of the project usually fixes this.

Manipulating Files In “InfoPath Form Template” Project Folder Is a Bad Idea

Deleting/modifying/renaming a file in the "infopath form template" meta-folder in Visual Studio 2008 does not fix the reference in the manifest.xsf file. The form is then impossible to open until you fix the references within the manifest by closing the form designer and opening the XSF with the XML handler through a right-click. Why does VSTO even let you modify embedded resources if it won’t update the manifest for you?

The magical secondary DataSource ItemMetadata.xml

The secondary DataSource (for receive data) ItemMetadata.xml has _two_ ways of being added as a resource - the second way (prompted at end of import). You can either add it as a resource when you initially point VSTO/InfoPath at it, or if you don’t do it then you get a second chance at the end of import to add it as a resource. This is confusing.

Converting an embedded DataSource to site-relative format and placing into a  Data Connection Library can leave the required field "Title" empty

This enforces the UDCX files to be permanently checked out - trying to update the title field by hand does not work - it remains blank at every attempt to change it when done directly or via bulk edit DataGrid view. It sometimes exclaims that the files are checked out to another user even though they are plainly checked out to me. Worst of all, only *I* can use the InfoPath form that uses this DataSource so it’s starts out a looking like “it works for me” issue.  Anyone else cannot use the form - DataSource loading fails - ULS log says "root element is missing" - after much debugging, it appears that anyone (apart from me the author) attempting to download the UDCX file gets a 0 length file - you can test this by getting the poor user to try to download the UDCX from the connection library. This is because only an author of a checked-out file can read it. 

Hacky Fix: Remove ReadOnly attribute from the list-level UDCX content type and mark the Title column as optional, then check-in the connections. Title be damned!

This all started when I enabled Versioning for the connection library before converting my data sources. Not sure if that’s relevant.

BDC randomly losing ServerContext while inside Event Receiver triggered from Workflow context

The TLS slot for the resource provider SqlSessionProvider sometimes ends up being null on the Event Receiver's thread. If you’re not following me, you probably don’t need to understand this any further.

Gotchas

These are not so much glitches are they are things that can trip you up and/or be considered “non-obvious” behavioural quirks. While they may give you an upset tummy, you are not going to be trussed up in a Spigelian torso-bangle any time soon.

A Workflow Continually Spawns New Instances of Itself

When you have a workflow added to a document library and the workflow updates the list item when it is running, a new workflow will spawn once the .Update() method is executed on the list item. To update the item without having a new worklfow started, use .SystemUpdate(). Duh.

"The database returns an error. Failed to enable constraints." When InfoPath DataBinds

Changing the query directly in a UDCX file inside a data connection library may cause "The database returns an error. Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints" errors from live InfoPath forms because the data source schema in InfoPath no longer matches (especially if you increase the length of a returned field) - after you change the UDCX, you must go through the "manage data connections" and choose "modify" for that connection and go through the wizard without changing anything. This will cause InfoPath to re-sync the schema.

How The Hell Can I Render The Form Version On The Form Itself?

Tip: insert form version with the expression: substring-before( substring-after( /processing-instruction()[local-name(.) = "mso-infoPathSolution"], 'solutionVersion="'), '"')

I can’t remember where I got this, I didn’t write it myself. Thanks to the original author.

Why Can’t SharePoint Find My Forms for my Workflow?

The Form URN will change if you don't name your form exactly as you did (e.g. accept the default) in "file > form properties" when publishing it with the publishing wizard.

Why Can’t My Workflow Forms Find My Data Connections?

Site relative data connections stored in a data connection library work fine for InfoPath published forms bound to a content type in a site; also form security will stay at domain. But they can’t be found if these same forms are used in a workflow. Use “centrally managed” data connections instead.

Why Does Visual Studio Put Referenced DLLs Bound For the GAC Into My InfoPath XSN?

If you reference other feature projects in the same solution that deploy into GAC, be sure that they exist in the GAC before building the InfoPath project's XSN - if they are not in the GAC, they will be copied into the XSN by default. Duh.

How Do I Uninstall My Workflow I was Debugging In Visual Studio?

Workflow features deployed via vs2008 F5/start debugging have no obvious means of being "undeployed." you must use VSTO 1.0 PowerTools "Feature Sweeper" to remove it; this is a dependency for workflow forms registered in central admin.

Why Are The TaskProperties Hashtables So Weird?

ExtendedProperties Hashtables leaves root XML fields as keys, and nested xml as my:schema formatted XML for Hashtable values. Strangely, the Workflow API has a public XmlToHashtable method on a utility class, but not the inverse to pull it back out of ExtendedProperties. Duh.

Where The Hell Did The Form Publish Menu Disappear to?

You cannot publish the InfoPath form unless you have the designer open, even though you can only have one form per project and you might just have a single project in the solution. This is annoying when your forms are large and sluggish to open.

Why do some VS SharePoint Activities have a User (string) property and others a UserID (int) property?

I wish I knew. This is just another silly design (in)decision. All SPUsers in SharePoint have both a Login (string) and a PrincipalID (int). If you want to set the Logging Activity’s UserId (int) to the id of a person who performed a Workflow Modification User (string), you have to convert, persist and expose these properties yourselves before you can bind to an activity using the designer. Duh.

I use a method like this:

id = Contact.FromName(executor, this.workflowProperties.Web).PrincipalID;
Why is Visual Studio 2008  SP1’s Workflow Designer Host still such a dog?

You and thousands of other people would love to know the answer. IMHO, it’s too complex to have been written in Managed Code (e.g. C#). It should have been native C++ from day one. All that synchronizing intellisense, XOML and the visual designer is just too much for it. God help you if you throw in any real-time refactoring tools on top of that.

Summary

Despite all this moaning, SharePoint in conjunction with Visual Studio 2008 is a powerful combination. Nothing to be sniffed at all; I actually love it (most of the time.)

Have fun.

Tags:

.NET | InfoPath | SharePoint | Visual Studio | VSTO 3.0 | XML

Create your own custom "Type Accelerators" in PowerShell

by oising 27. March 2008 13:19

PowerShell has several "type accelerators" which are used exactly like a casting operation. Examples of these special operators are [xml] and [wmi]. The former is used for quickly converting a string of xml into a fully-fledged System.Xml.XmlDocument object.

Often I find myself converting things to and from hexadecimal using the -f operator, but this always seemed like just a little too much typing for me. Enter the [hex] accelerator type:

image

As you can see from the source below, there's no magic here. This is just a straight cast, but I have no namespace. If I had a namespace, say, like "Nivot.PowerShell", we'd have to cast using [nivot.powershell.hex] instead of just [hex]. All of the trickery is done using operator overloads in C#. These tells .NET (and in turn, powershell) how to behave should someone try to add, subtract, remove or divide our instances.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. public class Hex  
  6. {  
  7.     private readonly int _value = 0;  
  8.  
  9.     private Hex(int value)  
  10.     {  
  11.         _value = value;  
  12.     }  
  13.  
  14.     private Hex(string value)  
  15.     {  
  16.         _value = Convert.ToInt32(value, 16);  
  17.     }  
  18.  
  19.     public static implicit operator Hex(int value)  
  20.     {  
  21.         return new Hex(value);  
  22.     }  
  23.  
  24.     public static implicit operator int(Hex value)  
  25.     {  
  26.         return value._value;  
  27.     }  
  28.  
  29.     public static explicit operator Hex(string value)  
  30.     {  
  31.         return new Hex(value);  
  32.     }  
  33.  
  34.     public static Hex operator +(Hex op1, Hex op2)  
  35.     {  
  36.         return new Hex(op1._value + op2._value);  
  37.     }  
  38.  
  39.     public static Hex operator -(Hex op1, Hex op2)  
  40.     {  
  41.         return new Hex(op1._value - op2._value);  
  42.     }  
  43.  
  44.     public static Hex operator *(Hex op1, Hex op2)  
  45.     {  
  46.         return new Hex(op1._value * op2._value);  
  47.     }  
  48.  
  49.     public static Hex operator /(Hex op1, Hex op2)  
  50.     {  
  51.         return new Hex(op1._value / op2._value);  
  52.     }  
  53.  
  54.     public override string ToString()  
  55.     {  
  56.         return "0x" + _value.ToString("X");
  57.     }  

The next step is to tell PowerShell's formatter what to do with the new type. Here's a simple format definition that tells the formatter to call ToString() on the Hex instance. This is the method that does the conversion by calling ToString("X") on the integer field. "X" means format the integer as hexadecimal using upper case. A lower-case "x" would output the value using lower-case (if you couldn't guess ;-)).

  1. <Configuration> 
  2.   <ViewDefinitions> 
  3.     <View> 
  4.       <Name>Hex</Name> 
  5.       <ViewSelectedBy> 
  6.         <TypeName>Hex</TypeName> 
  7.       </ViewSelectedBy> 
  8.       <CustomControl> 
  9.         <CustomEntries> 
  10.           <CustomEntry> 
  11.             <CustomItem> 
  12.               <ExpressionBinding> 
  13.                 <ScriptBlock>$_.ToString()</ScriptBlock> 
  14.               </ExpressionBinding> 
  15.             </CustomItem> 
  16.           </CustomEntry> 
  17.         </CustomEntries> 
  18.       </CustomControl> 
  19.     </View> 
  20.   </ViewDefinitions> 
  21. </Configuration> 

If we don't load this format file, PowerShell just emits a couple of blank lines when you try to use it.  You'll notice from the screenshot above that the blank lines still appear. I'm not sure how to remove these - it looks pretty ugly compared to the output of the [int] object. If you want to play with this, you can download the zip file below and unzip the contents into a single folder and run "hex.ps1". I didn't bother with a full Snap-In, it just loads the DLL using reflection. It also loads the format ps1xml too. Have fun.

HexAccelerator1.zip (2.4 KB)

Tags:

.NET | Monad | PowerShell | XML

Excluding getters and setters from get-member output using a filter

by oising 6. March 2008 16:35

Just a minor thing, but this is a filter I've been using for a while now as a replacement for Get-Member. It's always annoyed me that the Get_ and Set_ methods are returned along with the actual properties. The only place that's actual useful is when you're using an object wrapped in the [xml] adapter since those objects do not expose the XmlDocument's properties in the adapted member set.

update: if you use (of course you do!) MoW's PowerTab, you can disable display of the accessor methods with $PowerTabConfig.ShowAccessorMethods = $false.

  1. filter get-memberex {   
  2.     $_ | gm | ? { -not($_.name -match "^[gs]et_.+") }   
  3. }   
  4. new-alias gmx get-memberex  

Tags:

.NET | Monad | PowerShell | XML

Manipulating remote SharePoint Lists with PowerShell

by oising 29. February 2008 15:11

Someone on the PowerShell usenet group asked if it was possible to interact with SharePoint lists through our favourite little shell. Marco Shaw responded and put the pressure on by saying this was my bag of tricks. Who am I to say otherwise? so lets take a look at the recipe:

  1. One Get-WebService script
  2. One SharePoint List (any flavour but document library based)
  3. One Invoke-ListOperation script
Note: this script can be invoked from any workstation running PowerShell. You don't need access to SharePoint DLLs or other such nonsense.

Step One: Creating the Web Service proxy

Download my get-webservice2.ps1 (save and rename to .ps1) script and build a proxy to any remote Lists webservice. I say any, because you only need build the proxy once. You can switch sites at any time by changing the Url property of the $service object. You will be prompted by PowerShell with a graphical prompt for credentials for the SharePoint site, unless you specify -Anonymous as a switch (of course, if your SharePoint site is not anonymous enabled, this is a bit of a silly move.)

$service = .\get-webservice2.ps1 http://sharepoint/_vti_bin/lists.asmx

later, if you want to work with a different site, change the Url property like this:

$service.Url = "http://sharepoint/sites/root/subsite/_vti_bin/lists.asmx"

Ok, now we're ready to try the different operations: New, Update and Delete.

Step Two: Insert a new item

I chose the [hashtable] object to represent a new item as there's a nice syntax baked into powershell creating an initialising such a structure. Lets work with the Announcements list type. The two main fields I'm going to work with are the Title and Body fields. Note that even in a localised version of SharePoint - let's say French - where the fields are displayed as "Titre" (title) and "Corps" (body), we still use Title and Body for specifying the fields.
PS C:\> $item = @{Title = "Oisin"; Body="Hello, Word."}
PS C:\> $result = .\invoke-listoperation.ps1 $service new announcements $item
Success.
PS C:\> $result.row.ows_ID
2
If you get a success result back, the $result variable can be interrogated for the row that was just inserted. Above, I am retreiving the new ID for use in the next step.

Step Three: Modify an existing item

You probably noticed that the body of the announcemenet I just posted says "Word" instead of "World." Ooops! All we need do now is assign the $item variable the ID of the newly inserted row, modify the Body and use the Update command for our script:
PS C:\> $item.ID = 2
PS C:\> $item.Body = "Hello, World."
PS C:\> $result = .\invoke-listoperation.ps1 $service update announcements $item
Success.
Yee-ha. Fixed. Now it's time to remove such a pointless announcement.

Step Four: Delete an existing item

All we need to do now is pass the Delete command and a hashtable with an ID key set to the ID of the item we wish to delete. We can reuse the $item object at this point since it has the ID set now from the last update operation (alternatively, you could just pass @{ID=2} as the item argument - same effect).
PS C:\> $result = .\invoke-listoperation.ps1 $service delete announcements $item
Success.
or alternatively:
PS C:\> $result = .\invoke-listoperation.ps1 $service delete announcements @{ID=2}
Success.
And there you have it - announcement deleted. It's not that hard once you have the web service proxy bit done -- believe me, that was the hard part ;-)

Step Five: Profit!

Sorry, this part is up to you! Seriously though, if you are not on the SharePoint bandwagon by now, what's wrong with you?! It is an utterly incredible product. I've been working with SharePoint in all its various guises since the days of the Digital Dashboard (remember that?) and WSS 3.0 / MOSS 2007 is the most amazing iteration yet. Btw I have't tested this script on WSS 2.0, but it should work without any modification.

Here's the invoke-listoperation.ps1 script. Just copy and paste it into notepad, save it and away you go!

  1. param (   
  2.     $Service = $(throw "need service reference!"),   
  3.     $Operation = $(throw "need operation: Update, Delete or New"),   
  4.     $ListName = $(throw "need name of list."),     
  5.     [hashtable]$Item = $(throw "need list item in hashtable format.")   
  6. )    
  7.   
  8. # check if valid service reference provided   
  9. [void][system.Reflection.Assembly]::LoadWithPartialName("system.web.services")   
  10. if ($service -isnot [Web.Services.Protocols.SoapHttpClientProtocol]) {   
  11.     Write-Warning "`$Service is not a webservice instance; exiting."  
  12.     return  
  13. }   
  14.   
  15. # check if valid operation (and fix casing)   
  16. $Operation = [string]("Update","Delete","New" -like $Operation)   
  17. if (-not $Operation) {   
  18.     Write-Warning "`$Operation should be Update, Delete or New."  
  19.     return  
  20. }   
  21.   
  22. $xml = @"  
  23. <Batch OnError='Continue' ListVersion='1' ViewName='{0}'>  
  24.     <Method ID='1' Cmd='{1}'>{2}</Method>  
  25. </Batch>  
  26. "@   
  27.   
  28. if ($service) {   
  29.     trap [Exception] {   
  30.         Write-Warning "Error: $_"  
  31.         return;        
  32.     }   
  33.        
  34.     $listInfo = $service.GetListAndView($ListName"")   
  35.        
  36.     $listItem = ""  
  37.     foreach ($key in $item.Keys) {   
  38.         $listItem += ("<Field Name='{0}'>{1}</Field>" -f $key,$item[$key])   
  39.     }   
  40.        
  41.     $batch = [xml]($xml -f $listInfo.View.Name,$operation,$listItem)   
  42. }   
  43.   
  44. $response = $service.UpdateListItems($listInfo.List.Name, $batch)   
  45.   
  46. $code = [int]$response.result.errorcode   
  47.   
  48. if ($code -ne 0) {   
  49.     Write-Warning "Error $code - $($response.result.errormessage)"     
  50. else {   
  51.     Write-Host "Success."  
  52.     $response.Result   
  53. }  

Tags:

.NET | PowerShell | SharePoint | SOA | XML

About the author

Oisin Grehan lives in Montreal, Canada and builds all sorts of crap for all sorts of people.

Month List

Page List