Logo

dev-resources.site

for different kinds of informations.

Sitecore Code Generation with Unicorn in 2020

Published at
5/24/2020
Categories
sitecore
helix
unicorn
codegeneration
Author
kmac23va
Author
8 person written this
kmac23va
open
Sitecore Code Generation with Unicorn in 2020

I've been undertaking a "Helix recipe" project for my company of late, looking at some of the best ideas out there to come up with a starter kit of sorts for Helix with Sitecore. Because of the ease in configuration and to reduce the number of projects in Visual Studio, I've been integrating Unicorn into the mix. And while TDS has code generation features built into it (although you still have to provide the T4 templates), Unicorn doesn't have anything "out of the box" for that.

A few years ago, however, someone did take a stab at it with the RainbowCodeGeneration Github project.

GitHub logo heikof / RainbowCodeGeneration

A simple set of utility classes and a sample T4 template that allow easy code generation for Sitecore templates from Rainbow / Unicorn serialized items. Currently, only the YAML serialization format is supported.

It hasn't been updated in three years, but I still see it discussed, and recently an upgrade project crossed my desk that was using it with Sitecore 8. I'm trying to tailor this to 9.3 (the latest as of this writing), and I wanted to see about using it with my recipe project.

I've become a fan of fluent configuration when it comes to Glass, though I know others like the attribute configuration. Some may like good old Sitecore API calls. But in all cases, having the template and field IDs is a big plus because while the names are changeable, the IDs aren't (I mean, unless you delete the field and recreate it, but I can't help everything!). Code generation means the field name would change, sure, but it'd generate a compile error if you need to refactor the name, whereas specifying the ID as a string value yourself could leave junk behind. So while you could take this and generate many scenarios, I'm concentrating on simply getting the IDs for templates and fields.

The Problem

The sample in the RainbowCodeGeneration project was set for Sitecore 9 and Unicorn 4, but the later versions of Sitecore have gotten more complex, and when I tried using the samples and updating the references to 9.3, I kept getting errors where the code was looking for different missing assemblies. In addition, modern Helix projects are switching to the SDK project format and the package references model for NuGet. But the samples rely on references in the old "packages" folder, and the default location for package references is an app data folder in your computer profile...not exactly shareable in source control.

Step One: Configure NuGet

You should already have a NuGet.config file for your project that references the Sitecore feed. You just need to add one section to it, the globalPackagesFolder, as shown below:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <config>
        <clear />
        <add key="globalPackagesFolder" value="c:\packages" />
    </config>
    <solution>
        <add key="disableSourceControlIntegration" value="true" />
    </solution>
    <packageSources>
        <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
        <add key="Sitecore" value="https://sitecore.myget.org/F/sc-packages/api/v3/index.json" />
    </packageSources>
    <activePackageSource>
        <add key="All" value="(Aggregate source)" />
    </activePackageSource>
</configuration>
Enter fullscreen mode Exit fullscreen mode

Now your packages are set in a commonly-accessible location, and you can write your T4 templates accordingly.

Step Two: The T4 Include File

Since this is a Helix solution, we'll have a separate ID file for each module, typically in the format [layer].[module] (for example, Feature.Maps). That usually translates to a Unicorn folder location for [layer].[module].Templates (for example, Feature.Maps.Templates). If we follow this pattern consistently, we can use an include file across the various projects for consistency. But we also have the assembly references, and if Sitecore gets upgraded, these will have to be updated too...they can be in the include file as well. In fact, as you'll see in step three, the real T4 template will have almost nothing in it!

Here's the include file:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<# 
// ASSEMBLIES
// These assemblies need to be changed if there is an upgrade of the specified packages
#>
<#@ assembly name="C:\packages\rainbow.core\2.1.1\lib\net452\Rainbow.dll" #>
<#@ assembly name="C:\packages\rainbow.storage.yaml\2.1.1\lib\net452\Rainbow.Storage.Yaml.dll" #>
<#@ assembly name="C:\packages\rainbowcodegeneration\0.3.0\lib\net452\RainbowCodeGeneration.dll" #>
<#@ assembly name="C:\packages\sitecore.kernel\9.3.0\lib\net471\Sitecore.Kernel.dll" #>
<#@ assembly name="C:\packages\sitecore.logging\9.3.0\lib\net471\Sitecore.Logging.dll" #>
<#@ assembly name="C:\packages\microsoft.extensions.dependencyinjection.abstractions\2.1.1\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll" #>
<#@ assembly name="C:\packages\microsoft.extensions.dependencyinjection\2.1.1\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll" #>
<#@ assembly name="c:\packages\microsoft.extensions.logging\2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.dll" #>
<#@ assembly name="c:\packages\microsoft.extensions.logging.abstractions\2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll" #>
<#@ assembly name="c:\packages\microsoft.extensions.options\2.1.1\lib\netstandard2.0\Microsoft.Extensions.Options.dll" #>
<#@ assembly name="c:\packages\microsoft.extensions.diagnostics.healthchecks.abstractions\2.2.0\lib\netstandard2.0\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.dll" #>
<# 
// CONFIGURATION
var physicalFileStore = @"..\..\serialization"; // the path to your serialization items
var treeName = layerName + "." + moduleName + ".Templates";
var treePath = "/sitecore/templates/" + layerName + "/" + moduleName; // the matching path in Sitecore for the configuration
var templates = RainbowCodeGeneration.RainbowReader.GetTemplates(Host.ResolvePath(physicalFileStore), treeName, treePath);
#>
<#@ import namespace="RainbowCodeGeneration" #>
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated based on the Unicorn serialization items
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

// ReSharper disable InconsistentNaming
using Sitecore.Data;

namespace <#= layerName #>.<#= moduleName #>.Models {
<# try { foreach (var template in templates) { #>
    public static class <#= StringExtensions.AsClassName(template.Item.Name.StartsWith("_") ? "Meta" + template.Item.Name.Substring(1) : template.Item.Name).Replace("_", string.Empty) #>Constants {
        public static readonly ID TemplateId = new ID("<#= template.Item.Id.ToString() #>");
    <#foreach(var field in template.Fields){#>    public static readonly ID <#= StringExtensions.AsPropertyName(field.Name).Replace("_", string.Empty) #>FieldId = new ID("<#=field.Id.ToString()#>");
    <#}#>}
<#  } } catch (Exception ex){ Console.WriteLine(ex); } #>
}
Enter fullscreen mode Exit fullscreen mode

If you've looked at the sample files in the RainbowCodeGeneration tests project, you'll see the biggest change immediately...how many assemblies are referenced! I went through a process of updating the Rainbow and Sitecore assemblies from the original T4, and every time I saved the file, I got another "assembly is not found" error. I just kept adding them on until the compiler stopped complaining to me! As you can see, the location for the assemblies relies on my NuGet.config information, and the file/folder format is in the package references style.

For the serialization location, I'm putting my T4 file in a Models folder, so it goes up two levels to get outside the website folder and to the serialization folder. Then you'll see there are variables in place for the layer and module, which comes into play in step three.

I keep my T4 include file in the root of my solution, and have it included as a solution item just for reference and easy editing if needed.

Step Three: The T4 File

In each Helix modules, I have a Models folder, and this is where I put my actual T4 file. This is going to be a complex file...ready?

<# 
// MODULE CONFIGURATION
var layerName = "Foundation";
var moduleName = "Kernel";
#>
<#@ include file="$(SolutionDir)IDCodeGen.ttinclude" once="true" #>
Enter fullscreen mode Exit fullscreen mode

Pretty straightforward and easy to maintain from project to project! This is where we establish the layer and module variables, which if you use a Helix module generator, you can easily parameterize. Then an include reference to my T4 include from above, and we're good to go.

Conclusion

As noted, you can modify the T4 include to generate whatever you want. If you want to autogenerate full models with Glass attributes, you can do it with this. If you use the templates from the RainbowCodeGeneration project, you can see from this the modifications needed for the assemblies to get them working with the latest Sitecore. As always, when you upgrade Sitecore, upgrade these references appropriately (and be ready if you need to add another assembly or two).

Happy code generating!

codegeneration Article's
30 articles in total
Favicon
Anvil: An attempt of saving time
Favicon
Spending Less Time on Boilerplate with Blackbird
Favicon
Boost Your Coding: Easy AI Code Generation Tricks
Favicon
How to Use AI Code Generation to Enhance Developer Productivity
Favicon
Understanding Abstract Syntax Trees
Favicon
Component Generation with Figma API: Bridging the Gap Between Development and Design
Favicon
How to Perform Code Generation with LLM Models
Favicon
Get rid of Copy/Paste with Plop Js!
Favicon
ABP Suite: Best CRUD Page Generation Tool for .NET
Favicon
Generating TypeScript Code for a Dynamic Country Flag React Component
Favicon
Top Free Code Generation tools, APIs, and Open Source models
Favicon
Introduction to Code Generation in Rust
Favicon
Best Code Generation APIs in 2023
Favicon
Crafting Prompt Templates for Code Generation
Favicon
NEW: Code Generation APIs available on Eden AI
Favicon
Declarative code generation in Unity
Favicon
Build a WebAssembly Language for Fun and Profit: Code Generation
Favicon
Using EDMX file and T4 in .NET Core to Generate Code (Entities, DTO, API, Services etc.)
Favicon
Freezed Kullanarak Flutter'da JSON Nasıl Ayrıştırılır? 💫 🌌 ✨
Favicon
Dotnet code generation overview by example
Favicon
Sparky's Tool Tips: NimbleText
Favicon
Coding the code versus coding the code that codes
Favicon
Build an entire React application in one command
Favicon
Ensure auto-generated code is always up-to-date with compile-time assertions in Go
Favicon
Sitecore Code Generation with Unicorn in 2020
Favicon
gosli: a little attempt to bring a bit of LINQ to Golang
Favicon
How to make a code generator in 5 minutes (or less)
Favicon
Adding Contexts via Go AST (Code Instrumentation)
Favicon
How to Add Generated HttpClient to ASP.NET Core Dependency Injection (Right Way)
Favicon
Using code generation to survive without generics in Go

Featured ones: