Glass.Mapper performance – Sitecore ORM benchmark test

I recently gave a talk at the Manchester Sitecore User Group and part of this involved presenting information regarding the performance of Glass.Mapper. To ensure that reliable information exists in the public domain regarding the speed of some popular Sitecore ORM’s a GitHub repository now exists to perform a benchmark test and I thought I’d expand on the talk and write up some results.

I executed the tests from the repository on Sitecore 9.0.1 with a machine spec of Intel Core i7 7th Gen @ 2.40GHz with 16GB RAM, and the following are covered in the benchmark test:

Native Sitecore API
Glass.Mapper v5.1.0
Fortis v4.2.2
Synthesis v9.0.1

Each test was executed three times and each test itself runs the code 1000 times, so if you’d expect faster speeds – this is why. The results are listed below with an average taken of the three test executions. There is also an explanation that focuses on Glass performance as this is a topic I’d most like to address in this blog post.

1. ‘New’ test:

This is a basic test for a new item, the results are as follows…

Sitecore: 0, 0, 0 = 0ms
Glass: 8, 9, 8 = 8.33ms
Fortis: 1, 1, 1 = 3ms
Synthesis: 0, 0, 0  = 0ms

Glass creates proxy items which maps our data to objects that are CMS agnostic, and with the mapping running through a config pipeline it’s probably expected that the additional features result in a slower speed. Other object mapping frameworks wrap the Sitecore API calls, so we see a similar result to the native API.

2. ‘RenderFields’ test:

This is as the test above plus some field mappings…

Sitecore: 497, 495, 505 = 499ms
Glass: 300, 256, 306 = 287.33ms
Fortis: 526, 470, 480 = 492ms
Synthesis: 492, 512, 474492.66ms

It’s interesting to note that the native API on this occasion is slower than Glass. This is because Glass does not use the Render Field pipeline for fields other than Rich Text. Rendering fields is a very common action as a Sitecore developer, so the performance gain here is one of the more important ones in the test. Obviously, the templates used here are basic, for templates with more fields I’d expect Glass to offer an even bigger performance enhancement.

3. ‘Children’ test:

This test simply maps children of an item but doesn’t contain any field mappings for the child items…

Sitecore: 81, 42, 59 = 60.66ms
Glass: 119, 111, 123 = 117.66ms
Fortis: 80, 70, 72 = 74ms
Synthesis: 49, 42, 47 = 46ms

Again, Glass offers additional features meaning in this instance each child runs through the relevant configuration pipelines where Glass checks the likes of template enforcement etc. A more complex type match offers more features but a slower map speed, once again we’re in the ‘Feature vs Speed’ situation. The other frameworks only contain basic type matching which enables a faster result.

4. ‘ChildrenWithFields’ test:

This test is the same as the previous one, but on this occasion, we map children of an item including field mappings…

Sitecore: 5511, 5561, 5580 = 5550.66ms
Glass: 893, 661, 651 = 735ms
Fortis: 1122, 973, 968 = 1021ms
Synthesis: 1054, 959, 974 = 995.66ms

A practical example of this would be a Menu item for example and here we see an example of where mapping frameworks excel when compared to the native API. The reasons for this again relates to the Render Field pipeline. When it comes to rendering fields, once again it’s Glass that returns the fastest result.

Combined total for all tests:

Sitecore: 0 + 499 + 60.66 + 5550.66 = 6060.32ms
Glass: 8.33 + 287.33 + 117.66 + 735 = 1148.32ms
Fortis: 3 + 492 + 74 + 1021 = 1590ms
Synthesis: 0 + 492.66 + 46 + 995.66 = 1534.32ms

Summary

The test doesn’t factor in how much time it takes to configure the ORM or work with it (or without it) on a daily basis. It also doesn’t necessarily account for the trade-off between the amount of features the ORM supplies vs the time taken to render a page. Other factors to consider are the likes of any time savings afforded to us when using an ORM and writing unit tests for example, or assistance when writing SOLID class design and maintainable/reusable code. As ever, nothing boils down to speed alone, we must weigh up the overall efficiency of the framework which means including all contributing factors, so there’s scope for another post here, potentially a part 2 coming in future.

While Glass is slower than the Sitecore API for specific calls, in the combined totals we can see that for general use there is a significant performance gain. It would seem impossible that an ORM could offer a performance gain over a native API, but we can see here this isn’t the case.

Why not learn more about Glass and study the training course, something I’d highly recommend.

Advertisements

Sitecore placeholders as a Droplist

When it comes to selecting a placeholder in Sitecore we are required to enter text into a Single-Line Text field:

placeholder select

Obviously this introduces scope for a typo and potentially isn’t an ideal user experience. So I thought we’d look at changing this to a Droplist. The good news is the first step is a very straightforward task and we only need to update the Standard Rendering Parameters template found at /sitecore/templates/System/Layout/Rendering Parameters/Standard Rendering Parameters

The Placeholder field can be updated to a Droplist with a source query that filters anything that’s not a ‘Placeholder’ template (/sitecore/templates/System/Layout/Placeholder). We can use a query such as:

query:/sitecore/layout/Placeholder Settings//*[@@templateid='{5C547D4E-7111-4995-95B0-6B561751BF2E}’]

Our Standard Rendering Parameters template now appears as follows:

sitecore rendering parameters

And we can now select our placeholders from a Droplist:

sitecore_rendering_params_dropdown

Bear in mind that we have directly updated a Sitecore template, so any platform update will replace this change. We could serialize the Standard Rendering Parameters template but adding a vanilla template to source control is never a good idea. Food for thought.

Sadly our work isn’t quite finished, while this works for the Control Properties Dialog we also need to think about the Select Rendering Dialog incase a user wants to add a rendering from the Device Editor:

sitecore rendering selection

This is a sheer UI control found at:

MYSITE/sitecore/shell/default.aspx?xmlcontrol=Sitecore.Shell.Applications.Dialogs.SelectRendering&hdl=0ECAC17C88F941F08B14CE06796BF366&ro=sitecore%3A%2F%2Fmaster%2F%7BEB2E4FFD-2761-4653-B052-26A64D385227%7D%3Flang%3Den%26ver%3D1&ic=SoftwareV2%2F16x16%2Fcomponent_blue.png&txt=Select%20the%20rendering%20that%20you%20want%20to%20use.%20Click%20Select%20to%20continue.&ti=Select%20a%20Rendering&bt=Select&rt=Id&sph=1&sop=1&str=1

So we need to find the XML Control that uses the class Sitecore.Shell.Applications.Dialogs.SelectRendering

This is found in \sitecore\shell\Applications\Dialogs\SelectRendering\SelectRendering.xml

Rather than manipulate the vanilla XML control, we can copy this file to the ‘override’ folder to ensure that our new control is used:

\sitecore\shell\override\Applications\Dialogs\SelectRendering\SelectRendering.xml

Edit the line:

<CodeBeside Type=”Sitecore.Shell.Applications.Dialogs.SelectRendering.SelectRenderingForm,Sitecore.Client”/>

And change this to the class in your project:

<CodeBeside Type=”Helixbase.Feature.Fun.SelectRenderingForm,Helixbase.Feature.Fun”/>

And for our code behind file we can copy the class ‘SelectRenderingForm’ which is found in Sitecore.Shell.Applications.Dialogs.SelectRendering of the Sitecore.Client.dll (or Sitecore.Shell.dll depending upon your version of Sitecore).

First we need to change the PlaceholderName property from an ‘Edit’ to a ‘Listbox’ type:

protected Listbox PlaceholderName { get; set; }

We then loop through our placeholder items and populate the Listbox (view below or on Gist)

 var database = Sitecore.Configuration.Factory.GetDatabase("master");

            var placeHolderItems= database.SelectItems("/sitecore/layout/Placeholder Settings//*[@@templateid='{5C547D4E-7111-4995-95B0-6B561751BF2E}']");

            foreach (var placeholder in placeHolderItems)
            {
                this.PlaceholderName.Controls.Add(new Sitecore.Web.UI.HtmlControls.ListItem
                {
                    Value = placeholder.Name,
                    Header = placeholder.Name
                });
            }

We can now see placeholders as a Droplist:

sitecore_placehodler_dropdown

The Little Book of Sitecore® Tips volume 2 now available

littlebookofsitecore_newsFollowing volume 1 I’m pleased to announce the launch of volume 2 of The Little Book of Sitecore® Tips.

Just like the first book, these tips were created from my daily workings with the Sitecore experience platform and we now have another instalment in the ongoing series.

The main purpose of The Little Book of Sitecore Tips series is a light-hearted look at some useful Sitecore tips. These tips are targeted at all levels of user but whether you’re recapping or learning, I hope you find the material either useful, or of some entertainment value.

Your can order the next volume from the likes of Amazon or Barnes & Noble – The booklittlebookofsitecore_unicorn is also available as an eBook on the iBooks Store.

Thanks is due to the technical reviewers Michael West and Matthew Dresser – and the usual thanks to Tamas Varga who was as helpful as ever. Thanks is also due to Mr Helix Thomas Eldblom, and Dominic Hurst who supplied some nice reviews:

littlebookofsitecore_man

“Einstein once said: ‘The definition of genius is taking the complex and making it simple.’ – and this book does just that. It makes the complex simple and it is simply genius!”

“A must read from an author that doesn’t stop in his quest for learning, sharing and giving back.”

amazon-appsstore-us-black-v2Get_it_on_iBooks_Badge_US_1114google-play-badge

Using EnclosingTag, DisableWebEdit and ShowTitleWhenBlank when rendering fields in Glass

If we’re doing views right we’ll be doing views dumb (or replacing the layout service with JSS). In an ideal world our views would be free from helpers and business logic, they should simply render data from a model. Anybody who has been working with Glass for a while will know that the best way to achieve this is to create an editable field with GlassHtml, we simply pass a HtmlString to our view:

var myField = new HtmlString(_glassHtml.Editable(someitem, f => f.SomeField));

However, when rendering fields using the Sitecore API there are properties available from RenderFieldArgs (such as ‘EnclosingTag’) that Glass doesn’t use when rendering a field.

Fortunately, Glass calls the renderField pipeline when creating an editable field so I created a pull request to ensure we can use the following properties:

EnclosingTag – this is available when using @Html.Sitecore().Field as it is present in the render field pipeline as a field arg (renderFieldArgs.EnclosingTag). This can now be used in Glass to wrap our field in the chosen markup if the field value is not empty. Usage: new { EnclosingTag = “h2”}

DisableWebEdit – previously Glass forced renderFieldArgs.DisableWebEdit to false, this is now made optional with a ‘false’ default. There are instances where we may still want to pass in our field values as HtmlString but not allow it to be an editable field (when using EnclosingTag for example). Usage: new { DisableWebEdit = true }

ShowTitleWhenBlank – while it is possible to force this option across the Sitecore instance, we may still want to achieve this on a per field basis. This option will show a field title in the experience editor if the field is empty.  Usage: new { ShowTitleWhenBlank= “true” }

You can see an example implementation of the Glass features below (or using Gist):

This is available from the following pull request so be sure to update your version of Glass and begin (or continue) the good practice of simple view logic 🙂

Resolving workflow permission problems when using Habitat

If like the rest of us you’re a fan of Habitat you may find yourself experimenting with the solution and I wanted to draw attention to potential problems with workflow permissions when modifying the predicates for role serialization. Having disabled the serialization of feature roles:

e.g.
<include domain=”modules” pattern=”^Feature Accounts .*$” />

I found that users cannot edit any data when workflow is enabled. A quick chat with Mr Eldblom revealed the following:

“This is an example of how to cleanly separate security into the modules. Each feature module has a feature role defined which sets rights on the feature fields. Thereby if a feature is added then users do not automatically get access to the fields in the feature.”

So if you remove serialization of the feature role, you lose the ability to edit content. This is because the permissions are assigned on the template fields:

habitat_permissions

In order for your editors to modify content you will need to edit these permissions and grant access to the template fields, in my case I removed the restrictions above. Once you update these permissions, the fields that were previously locked will now be editable again.

Hopefully this saves a few headaches.

Automating item unlocks when deleting Sitecore users

Working with a large team of content editors can present us with difficulties, and one of these problems presents itself when we delete a user. If a user has locked items, deleting the user still leaves their items locked.

Sitecore contains a rule /sitecore/system/Settings/Validation Rules/Item Rules/Security/Locked By Non Existing User and we can use this item rule to give us a visual indicator of which items are locked by none existing users. This can be applied globally by adding it to our Global Rules item found at /sitecore/system/Settings/Validation Rules/Global Rules item.

So we can see which items are locked, and as an individual a content editor can unlock their own files by selecting the ribbon option ‘My Items’ and then using ‘Unlock All Items’ which calls the a method in the Sitecore.Shell.Applications.WebEdit.Commands.UnlockAll class. However, this method executes in the context of the current user rather than our non existing user who has locked the items. So while this method isn’t suitable for what we’re looking for, we can use something similar.

Upon deleting users Sitecore calls the user:deleted event so we need to create a handler for this event. You can see this handler being patched in Helix Base, and the source used by the event but below is the source for the handler:

    public class RemovedUserEventHandler
    {
        public void OnUserDeletedUnlockFiles(object sender, EventArgs args)
        {
            if (EventDisabler.IsActive)
                return;

            var objList = new List<Item>();
            var userName = Event.ExtractParameter<string>(args, 0);

            Assert.IsNotNullOrEmpty(userName, "User name was null or empty");

            var lockedItems = Client.ContentDatabase.SelectItems($"search://*[@__lock='%{userName}%']");

            if (lockedItems == null || !lockedItems.Any())
                return;

            foreach (var lockedItem in lockedItems)
                objList.AddRange(lockedItem.Versions.GetVersions(true).Where(version =>
                    string.Compare(version.Locking.GetOwner(), userName, StringComparison.OrdinalIgnoreCase) == 0));

            ProgressBox.Execute(nameof(RemovedUserEventHandler), "Unlocking items", "Network/16x16/lock.png",
                UnlockAllItems, "lockeditems:refresh", Context.User, objList);

            SheerResponse.Alert($"Successfully unlocked {objList.Count} item(s) checked out by {userName}",
                Array.Empty<string>());
        }

        private static void UnlockAllItems(params object[] parameters)
        {
            Assert.ArgumentNotNull(parameters, nameof(parameters));

            if (!(parameters[0] is List<Item> parameter))
                return;

            var job = Context.Job;

            if (job != null)
                job.Status.Total = parameter.Count;

            foreach (var lockedItem in parameter)
            {
                job?.Status.Messages.Add(Translate.Text("Unlocking {0}", (object)lockedItem.Paths.ContentPath));
                lockedItem.Locking.Unlock();
                if (job != null)
                    ++job.Status.Processed;
            }
        }
    }

Which we then patch in to the event:

  <sitecore>
    <events>
      <event name="user:deleted">
        <handler type="Helixbase.Feature.ItemUnlock.Events.RemovedUserEventHandler, Helixbase.Feature.ItemUnlock" method="OnUserDeletedUnlockFiles" />
      </event>
    </events>
  </sitecore>

Upon deleting users, you should now see their files automatically unlocked.

You can also find this source on GitHub Gist.

Edit… since writing this blog post Michael West has created a useful script for SPE to transfer ownership from one user to another. This can be used in situations where you wish to maintain a record of a previous users locked items.

Using voice commands to add items to Sitecore. IFTTT: Complete walk through

Having purchased a few Google Home devices my mind turned to getting them to work with Sitecore. Wouldn’t it be cool to tell your Google Home or mobile device to interact with the Sitecore environment?! So I setup a POC to do just that, by using a custom route in an API controller we can create Sitecore items by asking Google Home to “Create Sitecore item [ItemName]“:

First visit the IFTTT Platform where we can add our Applet. By selecting ‘New Applet’ we’re first asked to create a Trigger, for this we select ‘Google Assistant’

Google assistant

This will present us with some options:

google trigger

We’ll be using a phrase with a text ingredient, this allows us to say something like “Hey Google, add [ingredient] to my request”. For the POC this ingredient will be an item name which will be used to add an item to Sitecore under /sitecore/content/Home/Google Home

Now we’ll tell Google that our phrase will be “Create Sitecore item [ItemName]:

google assitant sitecore

And we need a reply from the device we’re speaking to:

google assistant sitecore response

Great, so we’ve got an “if this”, now it’s time for a “then that”. Our devices know what we’re going to say and know how to respond, so we need to create our API controller in Sitecore that’s going to create the item for us. The rough example I used is below:

    public class GoogleTestController: SitecoreController
    {
        public ActionResult IFTTT(string itemName)
        {
            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                var masterDb = Sitecore.Data.Database.GetDatabase("master");

                var template = masterDb.GetItem("/sitecore/templates/System/Templates/Standard template");

                var parentItem = masterDb.GetItem("/sitecore/content/Home/Google Home");

                parentItem.Add(itemName, new Sitecore.Data.Items.TemplateItem(template));

                return Content($"{itemName} added");
            }
        }
    }

And we’ll need our custom route

        public void Process(PipelineArgs args)
        {
            RouteTable.Routes.MapRoute("Feature.GoogleTest", "GoogleTest/{itemName}",
                new { controller = "GoogleTest", action = "IFTTT" });
        }

That’s our Sitecore code setup, now we need to add a webhook so that Google Assistant can talk to our Sitecore API. Under ‘then’ we select ‘Add action’ and select ‘Webhooks’:

ifttt then

This presents us with a dropdown and we can select ‘Make a web request’. These fields should be self explanatory, so the one I’ll focus on is the URL. In the below I have used a sample URL but obviously this needs to be a publicly accessible server with the Sitecore item Web API enabled:

Sitecore google api

Now we’re calling our Sitecore API controller, and sending in the name of the item that the user has asked to create. We could stop there and deploy but let’s select ‘Add action’ again and choose ‘notifications’. We want a phone notification when an item has been added to Sitecore:

ifttt notification sitecore

Give the Applet a title and description and save it. The final step is to enable this Applet on our IFTTT devices, simply select ‘Turn on’:

sitecore ifttt applet.png

Once it’s on we can talk to any of the Google devices on our account. I tested this by saying the following to my Google Home… “Ok Google, create Sitecore item test item” I hear the response “Ok, creating item test item on your Sitecore instance”, you can hear her response on this recording. We can then confirm that our device has added the item to Sitecore:

test item sitecore

We also get a nice phone notification:

sitecore-item-created-ifttt

The possibilities for this one are endless – Sitecore, it’s invading our homes!