'Yeah, I'm gonna need you to go ahead and work this weekend', Bill Lumbergh

Recently I was working on a Sitecore Powershell report generator that allows the end user to select the columns that they need to be returned in a CSV file. After the user selects the columns, the report generates the CSV according to the parameters. Below is an example of the classes and PSE script that supports this.

The report class used to populate the columns looks like this…

namespace Example
{
    public class MyReport
    {
        public string OrderId { get; set; }
        public string Firstname { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public DateTime OrderDate { get; set; }
        public bool IsShipped { get; set; }
        public decimal OrderAmount { get; set; }
    }
}

We’re using the CsvHelper nuget package to generate the CSV, which allows us to ignore properties that we don’t want. I won’t post the code here, but it creates a map and uses .Ignore() on the columns that we don’t want.

Now to the powershell script in Sitecore…

# These are the columns that the user can choose from.
# The key is the POCO property name, and the value is the pretty name to display to the user.
$columns = @{
    "OrderId"="Order ID"
    "FirstName"="First name"
    "LastName"="Last name"
    "Email"="Email"
    "OrderDate"="Date ordered"
    "IsShipped"="Is shipped"
    "OrderAmount"="Total"
}

# Set up the form where the user chooses parameters.
$props @{
    Parameters = @(
        @{ Name="startDate"; Title="Start date"; Editor="date" }
        @{ Name="endDate"; Title="End date"; Editor="date" }
        @{ Name="selectedColumns"; Title="Select columns"; Editor="check"; Options=$columns }
    )
    Title = "My report"
    Description = "Choose the time range and columns for your report"
    Width = 300
    Height = 400
    Showhints = $true
}

# Pass the user data to Sitecore/other systems and get CSV in code.
# ...
# ...

This works well enough for the few columns in this demo, but you will have noticed that we have to repeat ourselves with the column names in both the POCO and the PSE script. During the lifetime of this solution, undoubtedly there will be more columns added or removed and that raises a couple of problems:

  1. We need to update the property names in several places (property name in the POCO, property name and pretty name in the PSE script), and if the next developer working on it is not familiar with the code, it will take them longer to make changes.
  2. We need to update and commit the serialized item containing the PSE report every time there is a change. (Of course, your CI/CD pipeline automatically runs unicorn, so it’s no problem…. right? ;-p )

The solution

Ideally we would be better to enumerate the property names in the POCO from Powershell, so that we don’t need to repeat ourselves. But is it possible to:

  1. Read the DLL within Sitecore Powershell.
  2. Get the class.
  3. Read the class properties.
  4. Put them into a hashtable.

It turns out that the answer to all four is YES!

As shown below, we can keep the property name and pretty field name together using a custom attribute on each property.

namespace Example
{
    public class FieldNameAttribute : Attribute
    {
        public FieldNameAttribute(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }

    public class MyReport
    {
        [FieldName("Order ID")]
        public string OrderId { get; set; }
        [FieldName("First name")]
        public string Firstname { get; set; }
        [FieldName("Last name")]
        public string LastName { get; set; }
        [FieldName("Email")]
        public string Email { get; set; }
        [FieldName("Date ordered")]
        public DateTime OrderDate { get; set; }
        [FieldName("Is shipped")]
        public bool IsShipped { get; set; }
        [FieldName("Total")]
        public decimal OrderAmount { get; set; }
    }
}

And now we don’t have to explicitly list the columns in the PSE script…

# get an object of the type we need
$orderReportItem = New-Object Example.MyReport
# get the properties of that type
$properties = $orderReportItem | Get-Member -MemberType Properties
$columns = @{}

# put the property name and pretty name into a hashtable like before, but automagically
Foreach($prop in $properties)
{
    $propName = $prop.Name
    $propAttribute = [Example.MyReport].GetProperty($propName).GetCustomAttributes("FieldName").Name
    $columns[$propName] = $propAttribute
}

# ...
# ...

Conclusion

By using Powershell’s DotNet integration, we can read information dynamically from code, improving code maintainability and reliability, and reducing the number of changes that need to be deployed.

I hope this has been helpful. Thanks for reading. :-)