Monday, October 3, 2011

Using functions inside Powershell scripts

I had a task today that required mounting some content databases to my Sharepoint 2010 farm from an old WSS 3.0 environment that we are currently updating. Tasks like this often involve putting together a script file of some sort, a task that I quite frankly always find clunky and  boring to do and has always been performed on a need-to-do basis only in the past. However, I seem to have been bitten by the "what does this button do???" bug today and decided to investigate if I could make this whole experience a wee bit more exciting (and robust, god I do not want to mess up sharepoint content databases & have to roll back). Since I love working with a nix-style shell environment from my open-source days (hint hint: caffiene), I really love working with windows power shell. I feel it is MS's first real attempt at creating a decent shell scripting environment, and quite frankly PS cmdlet's are wicked.

Firstly, a bit of googling for some sort of IDE safe-heaven revealed the existence PowerGUI which can get for free (bit of $$ gets you a Pro version) and offer's a really wicked environment with PS specific intellisense & debugging enabled in a very Visual Studio'esque look & feel. Definitely a must have for nube's like myself.  The tool also offers VS2010 integration (see codeplex), which depends on the PowerGUI env being installed first but I was happy with the basic tool itself.

As for the scripting itself, my task required repeatedly calling the Sharepoint 2010 admin console cmdlet function "Mount-SPContentDatabase" which basically updates migrated content DB's from a WSS 3.0/2007 environment. What I wanted to achieve was basically execute the command for each content DB, if it is a success continue on, otherwise stop. So basically what I wanted to do was to write a function that I can call with my param's that will execute the call, and if unsuccessful exit the script. Oh and I also wanted to call this custom executable we made, called "MyLogTruncator.exe" to truncate sql log files on our db server that hosted the content databases prior to mounting them (otherwise you run the risk of running out of disk space if you dont have a lot to work with like me).

So basically I came up with this

function TruncateLog{param ($dbName, $LogFileName)
 .\MyLogTruncator.exe $dbName $LogFileName
 if(-not($?))
 {
  Write-Host 'Could not truncate log for ' $dbName $LogFileName
  Write-Host 'Exiting Script'
  exit
 }
}
function MountSPContentDB{param ($dbName) 
#this is the SP2010 cmdlet that will mount the content db's 
Mount-SPContentDatabase $dbName  -DatabaseServer 'MySharepointDBServer' -WebApplication 'http://MySPWebApplication'
 if(-not($?))
 {
  Write-Host 'Could not mount SP Content DB for ' $dbName
  Write-Host 'Exiting Script'
  exit
 }
}
#repeated calls to the functions 
TruncateLog WSS_DB1 WSS_DB1_log
MountSPContentDB 'WSS_DB1' 
TruncateLog WSS_DB2 WSS_DB2_log
MountSPContentDB 'WSS_DB2'

TruncateLog WSS_DB3 WSS_DB3_log
MountSPContentDB 'WSS_DB3' 

#other db's ......

As you can see, I have two functions specified with params to each function that I can call it with. The function body is enclosed with curly braces, followed by the first line outlining the list of params that the function can take. My TruncateLog function takes 2 parameters, the name of the db, denoted by the variable $dbName & the the name of the SQL log file to truncate, denoted by the variable $LogFileName. The function MountSPContentDB only needs to know the name of the content db to mount. Simply enough, once I have them declared I can continuously call them, passing in my parameters as shown.

Another tiny pearl I uncovered is catered towards adding a degree of robustness. If you could shift your attention to the PS if statement (which again is not very hard to write), you may notice a variable declared like so: $? . This is basically a boolean PS variable which stores the outcome of the previously executed statement, i.e if successful will store true otherwise false. I simply use this to make sure the previous command ran ok, if not I simply exit the script (call to "exit") and leave it to the user to work out what's gone wrong (the plethora of calls to Write-Host should outline where it failed hopefully). 

Well that was basically all I needed to do. Hope this helps. Happy shell'ing everybody.

Saturday, October 1, 2011

Defining metadata for Entities created using Entity Framework

Ive been using Entity framework for a couple of months now. I had this issue a while back, where I had an entity model designed using the entity framework designer  (note: I am currently using the 4.1 update). My task was to extend the entity model so that I could decorate an entity class with some data annotations.

For the sake of simplicity, let us assume that our entity data model has only the following entity, Product, defined like so:

If this product class was used, say as a model for an MVC3 controller, calling an html helper method to automatically render an editor for the model, (as shown below):


@model MyDomain.Entities.Product
 
@{
    ViewBag.Title = "Edit";
}
 
<h1>Edit @Model.Name</h2>
 
@using (Html.BeginForm())
{
    @Html.EditorForModel()
    <input type="submit" value="save" />
}

However this would result in the view being rendered with an html input element for each data member of the Product entity, including the Id field (of type int) which also happens to be the primary key. This may not be desired, as typically users should not be able to control setting the primary key for an entity. However we cannot simply extend the partial class and overwrite the field either. We could simply edit the code generated by EF for the Product EntityObject class, but any changes to the model would result in your data annotations being wiped, so this is not recommended either. As such the easiest way to solve this issue is to decorate your entity class with the System.ComponentModel.DataAnnotations.MetadataTypeAttribute 






First, we create a new class called ProductMetaData within which we define a single property called Id. Notice that the Id property is defined to be the same type as that of the Id field in the Product entity object, i.e. they are both of type Int. Once this is done, we can add a data annotation to instruct MVC's html helper methods to omit this field when rendering the view (note I am using the HiddenInput data annotation defined in the System.Web.Mvc namespace for this example):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
 
namespace MyDomain.Entities.Metadata
{

    public class ProductMetadata
    {
        [HiddenInput(DisplayValue=false)]
        public int Id { getset; }
    }
}


Following this, we can then simply create a partial class for the entity that we wish to annotate, in this case the Product entity. Once this is done, all that is left is to annotate the class with a MetadataTypeAttribute, passing in the type of the ProductMetaData class in the constructor and recompile.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MyDomain.Entities
{
    [MetadataType(typeof(MyDomain.Entities.Metadata.ProductMetadata))]
    public partial class Product
    {
 
    }
}


And that's basically it. During runtime, the html helper methods will now omit generating an editor for the Id field every time the view is rendered like so:




Algorithm for incrementing alphabetical version numbers


Scenario

Forgive me if this post seems a little strange & horribly formatted (haven't quite worked out how to get everything to look right yet). This basically constitutes my first ever blog post .... ever! so I thought I might start off with a slightly light topic. I had an odd request a while back to implement a versioning algorithm that incrementally helped maintain a version number every time a form was submitted/re-submitted using Ms infopath 2010. Long story short, the product is a small enterprise system that integrates with a third party workflow engine, allowing users to conduct and maintain an internal quality and review process. However the client asked for the versioning to be alphabetically indexed to follow a sequence like so:

a, b, c, d,...., z, aa, ab, ac, ...., az, ba, bb, bc, ...........,zy, zz, aaa, aab, .......
 After much googling, I must admit I could not find any code on the web that I could easily reuse. I did not (and still don't) know if this form of sequencing has an official name. In the end, I simply gave in and came up with my own solution. I must admit it was not very hard to come up with one, but thought I might share it all the same, should someone else face a similar request.

Solution & Code

Before we get under way, I imported the following .Net C# namespaces:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;


I was told to hard code every new form to have a default start version value of 'a'. Regardless, in an effort to implement error checking and ensure that my code was always dealing with strings containing alphabetical characters. This is nothing special, just using the .net reg-ex library to check for unwanted chars in the version string. I wrote the following method:

        /// <summary>
        /// Ensure that all versioning strings have an alphabetical version
        /// </summary>
        private static bool IsCharacterString(string currentVersionNumber)
        {
            if (string.IsNullOrEmpty(currentVersionNumber))
                throw new ArgumentNullException("currentId");
            //match to see if there are any non alphabetical chars in the string
            var pattern = @"[^a-z]";
            var regx = new Regex(pattern);
            //if there are no such chars, then this is an accepted character versioned string
            return !regx.IsMatch(currentVersionNumber.ToLower());
        }
Next I proceeded to define the following constants (at class level as they are used in more than one function):

        const string DEFAULT_FIRST_CHAR_VERSION = "a";
        //ascii representations of characters a,z
        const int ASCII_A = 97;
        const int ASCII_Z = 122;

This was followed by the actual algorithm that produces the next version:


       /// <summary>
        /// algorithm to generate char Id's in the format: 'a,b,c...z,aa,ab,ac,ad.... az, ba.....zz, aaa, aab, ....'
        /// </summary>
        private static string NextCharVersion(string currentVersion)
        {
            if (string.IsNullOrEmpty(currentVersion))
                throw new ArgumentNullException("currentVersion");
            //ensure that the string is in lower case
            currentVersion = currentVersion.ToLower();
            //get a vector of chars
            var charList = Encoding.ASCII.GetBytes(currentVersion.ToCharArray());
            //check the last character in the list
            byte lastChar = charList.Last();
            //if the last char is [a-y] &  not Z
            if (lastChar >= ASCII_A
                && lastChar < ASCII_Z)
            {
                // simply increment the last character
                charList[charList.Length - 1]++;
            }
            else
            {
                //check if the version number string needs incrementing (only happens if all chars in string are now 'z')
                if (charList.All(c => c == ASCII_Z))
                {
                    //make a new list
                    var tmpList = charList.Select(c => (byte)ASCII_A).ToList();
                    //add the next version increment
                    tmpList.Add(ASCII_A);
                    charList = tmpList.ToArray();
                }
                else
                {
                    //reset the last char to 'a'
                    charList[charList.Count() - 1] = ASCII_A;
                    //iterate backwasrds & keep incrementing the previous chars (except the last) untill you hit a char that is in the range [a-y]
                    for (int i = charList.Count() - 2; i >= 0; i--)
                    {
                        if (charList[i] >= ASCII_A && charList[i] < ASCII_Z)
                        {
                            charList[i]++;
                            break;
                        }
                        else
                        {
                            charList[i] = ASCII_A;
                        }
                    }
                }
            }
            return new string(Encoding.ASCII.GetChars(charList));
        }

All this is then followed up with a public facing function which accepts a string representing the current version (part of the submitted form), checks if it is a valid string and progresses to determine the next version that should be set (note: actual code that sets the value in the submitted form has been omitted):


public static string UpdateNextCharVersion(string currentVersion)
        {
            //if string is null then return first char
            if (string.IsNullOrEmpty(currentVersion))
                return DEFAULT_FIRST_CHAR_VERSION;
            //if is characters then increment as per character version
            else if (IsCharacterString(currentVersion))
            {
                return NextCharVersion(currentVersion);
            }
            else
            {
                //return the version as is if the string cannot be versioned
                return currentVersion;
            }
        }

Hopefully this should save a lot of people an hour or two if they ever have to implement something like this.