Bundled Skills
This plugin includes the following additional skills for comprehensive development context:
Skill Purpose
frontend-classic
CSS/SASS organization, JavaScript/jQuery patterns, BEM naming
frontend-razor
Razor view syntax, layouts, partials, tag helpers
backend-csharp
C#/.NET DI patterns, service architecture, async/await
fullstack-classic
jQuery AJAX integration, form handling, anti-forgery tokens
These skills are automatically included when you install the Sitecore Classic Analyzer plugin.
Sitecore 10.x Development Patterns
Helix Architecture
Layer Responsibilities
Foundation Layer
-
Cross-cutting concerns shared across features
-
Examples: Logging, Serialization, DI registration, Indexing, ORM
-
No business logic, only technical infrastructure
-
Can reference other Foundation modules
Feature Layer
-
Business capabilities and features
-
Examples: Navigation, Search, Forms, Checkout, Articles
-
Each feature is independent and self-contained
-
Can reference Foundation modules only
-
NEVER reference other Feature modules
Project Layer
-
Tenant/site-specific implementations
-
Examples: Website, Corporate, MobileApp
-
Assembles features for specific sites
-
Can reference Feature and Foundation modules
Dependency Rule
Project → Feature → Foundation
Features must NEVER reference other Features. If shared logic is needed, extract to Foundation.
Folder Structure
src/ ├── Foundation/ │ └── {Module}/ │ ├── code/ │ │ ├── DependencyInjection/ │ │ ├── Services/ │ │ └── Foundation.{Module}.csproj │ └── serialization/ │ └── {Module}.module.json ├── Feature/ │ └── {Module}/ │ ├── code/ │ │ ├── Controllers/ │ │ ├── Models/ │ │ ├── Services/ │ │ ├── Repositories/ │ │ └── Feature.{Module}.csproj │ └── serialization/ │ ├── Templates/ │ ├── Renderings/ │ └── {Module}.module.json └── Project/ └── {Site}/ ├── code/ │ ├── Layouts/ │ └── Project.{Site}.csproj └── serialization/ ├── Content/ └── {Site}.module.json
Dependency Injection
Service Registration
using Microsoft.Extensions.DependencyInjection; using Sitecore.DependencyInjection;
public class RegisterDependencies : IServicesConfigurator { public void Configure(IServiceCollection services) { // Transient: New instance each time services.AddTransient<ISearchService, SearchService>();
// Scoped: One instance per HTTP request
services.AddScoped<INavigationService, NavigationService>();
// Singleton: One instance for application lifetime
services.AddSingleton<ICacheService, CacheService>();
}
}
Config Registration
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <services> <configurator type="Foundation.DI.RegisterDependencies, Foundation.DI" /> </services> </sitecore> </configuration>
Constructor Injection
public class NavigationController : Controller { private readonly INavigationService _navigationService; private readonly ILogger<NavigationController> _logger;
public NavigationController(
INavigationService navigationService,
ILogger<NavigationController> logger)
{
_navigationService = navigationService;
_logger = logger;
}
public ActionResult Index()
{
var datasource = RenderingContext.Current.Rendering.Item;
if (datasource == null)
{
_logger.LogWarning("Navigation datasource is null");
return new EmptyResult();
}
var model = _navigationService.GetNavigation(datasource);
return View(model);
}
}
Content Access Patterns
Use Content Search (Recommended)
public class SearchRepository : ISearchRepository { private readonly ISearchIndex _index;
public SearchRepository(ISearchIndexResolver indexResolver)
{
_index = indexResolver.GetIndex("sitecore_web_index");
}
public IEnumerable<ArticleSearchResult> GetArticles(
ID templateId,
string language,
int maxResults = 100)
{
using (var context = _index.CreateSearchContext())
{
return context.GetQueryable<ArticleSearchResult>()
.Where(x => x.TemplateId == templateId)
.Where(x => x.Language == language)
.OrderByDescending(x => x.Created)
.Take(maxResults)
.ToList();
}
}
}
Avoid Sitecore.Query (Slow)
// BAD - Tree traversal, O(n) where n = tree size var items = item.Axes.SelectItems(".//*[@@templateid='{GUID}']");
// BAD - Even "fast:" queries traverse the tree var items = Database.SelectItems("fast:/sitecore/content//*");
// GOOD - Use Content Search instead var items = searchContext.GetQueryable<SearchResultItem>() .Where(x => x.TemplateId == templateId) .ToList();
Avoid GetDescendants (Memory Heavy)
// BAD - Loads entire subtree into memory var allItems = rootItem.GetDescendants().ToList();
// GOOD - Use Content Search with path filter var items = searchContext.GetQueryable<SearchResultItem>() .Where(x => x.Paths.Contains(rootItem.ID)) .Take(1000) .ToList();
Caching
HTML Cache (Rendering Level)
Configure in rendering item or config:
<r> <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"> <r uid="{RENDERING-UID}" s:caching="1" s:varybydata="1" s:varybydevice="0" s:varybylogin="0" s:varybyuser="0" s:varybyquerystirng="0" s:timeout="01:00:00" /> </d> </r>
Custom Cache
public class NavigationCache : CustomCache { public NavigationCache(string name, long maxSize) : base(name, maxSize) { }
public NavigationModel Get(string cacheKey)
{
return GetObject<NavigationModel>(cacheKey);
}
public void Set(string cacheKey, NavigationModel model, TimeSpan duration)
{
SetObject(cacheKey, model, duration);
}
}
Register cache:
public class RegisterCaches : IServicesConfigurator { public void Configure(IServiceCollection services) { services.AddSingleton<NavigationCache>(provider => new NavigationCache("NavigationCache", StringUtil.ParseSizeString("50MB"))); } }
Configuration Patching
Basic Patch
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <settings> <setting name="MySetting" value="MyValue" /> </settings> </sitecore> </configuration>
Environment-Specific Settings
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/"> <sitecore> <settings> <!-- Only in Production --> <setting name="Analytics.Enabled" env:require="Production"> <patch:attribute name="value">true</patch:attribute> </setting>
<!-- Only in Development -->
<setting name="Analytics.Enabled" env:require="Development">
<patch:attribute name="value">false</patch:attribute>
</setting>
</settings>
</sitecore> </configuration>
Role-Specific Settings
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/"> <sitecore> <settings> <!-- Only on Content Management servers --> <setting name="Preview.Enabled" role:require="ContentManagement"> <patch:attribute name="value">true</patch:attribute> </setting>
<!-- Only on Content Delivery servers -->
<setting name="Preview.Enabled" role:require="ContentDelivery">
<patch:attribute name="value">false</patch:attribute>
</setting>
</settings>
</sitecore> </configuration>
Pipeline Processors
Custom Processor
public class EnrichContextProcessor : HttpRequestProcessor { public override void Process(HttpRequestArgs args) { // Early exit for efficiency if (Context.Item == null) return; if (Context.Site == null) return; if (Context.PageMode.IsExperienceEditor) return;
// Processor logic here
EnrichContext();
}
private void EnrichContext()
{
// Custom enrichment logic
}
}
Config Registration
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <httpRequestBegin> <processor type="Feature.Context.EnrichContextProcessor, Feature.Context" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" /> </httpRequestBegin> </pipelines> </sitecore> </configuration>
Solr Index Configuration
Custom Index
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <contentSearch> <configuration> <indexes> <index id="custom_articles_index"> <configuration ref="contentSearch/indexConfigurations/defaultSolrIndexConfiguration"> <documentOptions> <fields hint="raw:AddComputedIndexField"> <field fieldName="article_author" returnType="string"> Feature.Articles.ComputedFields.ArticleAuthor, Feature.Articles </field> <field fieldName="article_category" returnType="stringCollection"> Feature.Articles.ComputedFields.ArticleCategories, Feature.Articles </field> </fields> </documentOptions> </configuration> </index> </indexes> </configuration> </contentSearch> </sitecore> </configuration>
Computed Field
public class ArticleAuthor : IComputedIndexField { public string FieldName { get; set; } public string ReturnType { get; set; }
public object ComputeFieldValue(IIndexable indexable)
{
var item = (indexable as SitecoreIndexableItem)?.Item;
if (item == null) return null;
var authorItem = item.GetReferenceField("Author")?.TargetItem;
return authorItem?["Name"];
}
}
Sitecore Content Serialization (SCS)
module.json Structure
{ "namespace": "Feature.Navigation", "items": { "includes": [ { "name": "templates", "path": "/sitecore/templates/Feature/Navigation", "allowedPushOperations": "CreateUpdateAndDelete" }, { "name": "renderings", "path": "/sitecore/layout/Renderings/Feature/Navigation", "allowedPushOperations": "CreateUpdateAndDelete" }, { "name": "settings", "path": "/sitecore/system/Settings/Feature/Navigation", "allowedPushOperations": "CreateUpdateAndDelete" } ] } }
CLI Commands
Pull items from Sitecore to disk
dotnet sitecore ser pull
Push items from disk to Sitecore
dotnet sitecore ser push
Validate serialization
dotnet sitecore ser validate
Glass Mapper Patterns
Model Definition
[SitecoreType(TemplateId = "{GUID}", AutoMap = true)] public class ArticlePage { [SitecoreId] public virtual Guid Id { get; set; }
[SitecoreField("Title")]
public virtual string Title { get; set; }
[SitecoreField("Content")]
public virtual string Content { get; set; }
[SitecoreField("Author")]
public virtual Author Author { get; set; }
[SitecoreChildren]
public virtual IEnumerable<RelatedArticle> RelatedArticles { get; set; }
}
Service Usage
public class ArticleService : IArticleService { private readonly ISitecoreService _sitecoreService;
public ArticleService(ISitecoreService sitecoreService)
{
_sitecoreService = sitecoreService;
}
public ArticlePage GetArticle(Guid id)
{
return _sitecoreService.GetItem<ArticlePage>(id);
}
}
Avoid N+1 Queries
// BAD - N+1 queries with lazy loading foreach (var article in articles) { var author = article.Author; // Lazy load for each article }
// GOOD - Eager load or use Content Search var articleIds = articles.Select(a => a.Id).ToList(); var authors = searchContext.GetQueryable<AuthorSearchResult>() .Where(x => articleIds.Contains(x.ArticleId)) .ToList();