Loading...

Breaking changes in CMS 12

Recommended reading 
Note: This documentation is for the preview version of the upcoming release of CMS 12/Commerce 14/Search & Navigation 14. Features included here might not be complete, and might be changed before becoming available in the public release. This documentation is provided for evaluation purposes only.

This topic describes breaking changes for Optimizely CMS in relation to previous version 11, and the steps needed to update affected code. To view the complete list of changes, see the release notes feed.

Binary breaking changes do not necessarily require code changes but rather just a recompilation of the project. CMS 12 breaking changes to method signatures or to the behavior of methods, compared to the documented API in CMS 11,  are described in this topic.

Version 12 targets .NET 5.0 so breaking changes between ASP.NET and .NET Framework 4.x to .NET 5.0 apply to Optimizely CMS projects as well. For example, this includes ending support for writing templates that use WebForms or having WebForms views in MVC.

NuGet packages

You should use the Upgrade assistant tool to migrate from a CMS 11 project to CMS 12 because the tool manages most of the package updating.

There are some packages that are ASP.NET-specific and are available only for CMS 11. The following list of packages are no longer applicable in CMS 12. Corresponding packages are shown in the CMS 12 package column:

CMS 11 package CMS 12 package Description
EPiServer.CMS EPiServer.CMS Umbrella package for CMS. Includes both packages used for rendering and CMS UI.
EPiServer.Cms.AspNet EPiServer.CMS.AspNetCore, 
EPiServer.CMS.AspNetCore.Templating,
EPiServer.CMS.AspNetCore.Routing,
EPiServer.CMS.AspNetCore.Mvc,
EPiServer.CMS.AspNetCore.HtmlHelpers
EPiServer.Cms.AspNet contained general web-related CMS features including routing, WebForms, MVC and so on. This was split into several different packages. EPiServer.Cms.AspNetCore.HtmlHelpers is the "top" package of this, meaning referencing that gives indirect dependency to the other packages.
EPiServer.Framework.AspNet EPiServer.Framework.AspNetCore Some general web-related implementations such as VirtualPathProviders, FileProviders.
EPiServer.ServiceLocation.StructureMap Dependency injection implementation. Not applicable in CMS 12 since ASP.NET Core includes a DI framework.
EPiServer.Logging.Log4Net Logging implementation for log4net. Not applicable in CMS 12 since custom logging is configured directly towards .NET Core APIs.

Migration

You can use the optional package EPiServer.CMS.AspNetCore.Migration for projects that are migrating from CMS 11 to CMS 12. The package contains some old APIs such as DataFactory and support for XML-based .config files. If you use an existing web.config, you should rename the config file to app.config so that ConfigurationManager will load it.

Dependency Injection (CMS-16441)

In ASP.NET Core, a dependency injection (DI) framework is built into the platform. In prior versions, Optimizely CMS had its own DI hosting framework that supported different concrete DI systems. In version 12, there is no longer a DI hosting framework within CMS; instead, the DI system is a layer on top of the built-in DI framework in ASP.NET Core. See Dependecy injection.

Custom DI frameworks

In prior versions of CMS, there was a DI hosting framework within CMS and then concrete packages like EPiServer.ServiceLocation.StructureMap for specific DI implementations. By default, CMS is configured to use the default DI implementation in ASP.NET Core. The required code in Program.cs to connect the DI framework in ASP.NET Core with CMS is the call to the extension method ConfigureCmsDefault(), as in the following example:

public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
      .ConfigureCmsDefaults()
      .ConfigureWebHostDefaults(webBuilder =>
      {
         webBuilder.UseStartup<Startup>();
      });

We recommend using the built-in DI framework described above, which is the only framework we do testing on. To use a different DI framework than the built-in, the call to ConfigureCmsDefault() should be replaced with a call to UseServiceProviderFactory passing in an instance of ServiceLocatorProviderFactoryFacade and there passing in the actual implementation to use. Below is an example on how to configure the application to use Autofac (given that there is a reference to the Autofac NuGet package):

public static IHostBuilder CreateHostBuilder(string[] args) =>
   Host.CreateDefaultBuilder(args)
      .UseServiceProviderFactory(context => 
         new ServiceLocatorProviderFactoryFacade<ContainerBuilder>(
           context,
           new AutofacServiceProviderFactory()));
      .ConfigureWebHostDefaults(webBuilder =>
      {
         webBuilder.UseStartup<Startup>();
      });

Service Registration

Unlike DI frameworks like StructureMap that supports auto-resolving of concrete types, the built-in DI framework requires you to explicitly register used services in the IOC container.  

Lifetime

In prior versions of CMS, you could scope a service as 'HttpContext' or 'Hybrid'. In version 12, the lifetimes are the same as in .NET Core, that is, Transient, Scoped, or Singleton.

ServiceLocator.Current

Previously, when there was no built-in DI framework in ASP.NET, it was common to use the static property ServiceLocator.Current to access the DI container in WebForms or views (for example).

In CMS 12, when there is a built-in DI framework that is accessible, for example, through HttpContext, there are not many places where you need to use ServiceLocator.Current.

If, however, you use ServiceLocator.Current outside a web request you have to create custom scopes, you should create the scopes using extension method CreateServiceLocatorScope instead of the extension method CreateScope within .NET Core. This makes the static accessor ServiceLocator.Current aware of the custom-created scopes. A scope is for example automatically created by CMS for each scheduled job execution.

Logging

In ASP.NET Core, a logging framework is built into the platform. In prior versions, Optimizely CMS had its own logging framework that supported different concrete logging systems such as EPiServer.Logging.Log4Net.

In version 12, there is no longer a logging hosting framework within CMS; instead, the logging system in CMS (EPiServer.Logging namespace) is a layer on top of the built-in logging framework in ASP.NET Core. To configure logging for CMS, you configure logging in the same way as for a plain ASP.NET Core site, see Logging in .NET Core. To do custom logging, you should take a dependency to an ILogger or ILoggerFactory from Micrsoft.Extensions.Logging namespace. For projects that are upgraded that today use APIs in EPiServer.Logging, you can continue to do so because that API acts as a facade over the logging APIs in .Net Core.

Routing

In prior versions of CMS, the routing was based on the API defined in System.Web.Routing. This API is no longer available in ASP.NET Core so the routing system in CMS was rewritten to work with endpoint routing in ASP.NET Core. See Routing.

Routing extensions

Events

Previously, there were events exposed by EPiServer.Web.Routing.IContentRouteEvents. This interface was made obsolete and replaced by EPiServer.Core.Routing.IContentUrlGeneratorEvents and EPiServer.Core.Routing.IContentUrlResolverEvents

Partial routing

The interface IPartialRouter changed slightly and implementations should now be registered in DI container as IPartialRouter, rather than the previous way to register a partial router, which was through the extension method RegisterPartialRouter on RouteCollection.

Custom parameter routing

Previously, you could define content routing with custom parameters using extension methods starting with Map on RouteCollection.

In CMS 12, to register custom parameter routing for content routes, you can use the method MapTemplate on IContentEndpointRouteBuilder that is returned when extension method MapContent is called on IEndpointRouteBuilder.

Custom starting points for routes

Previously, you could use RouteCollection extension methods MapContentRoute, MapPageRootRoute or MapEnterpriseRoutes to register content routes for custom content roots (the content item where the routing starts from). You also could register optional static segements for the routes.

This was replaced by interface EPiServer.Core.Routing.Pipeline.IContentRouteRegister, which you can implement and register in a DI container to define a custom content root registration.

Extension methods

Previously, you could get content URLs using MVC extension methods like Html.ActionLink or Html.BeginForm. This no longer works. Instead, replace them with calls to Html.ContentLink or Html.BeginContentForm.

Configuration

In previous (ASP.NET-based) versions, you configured through web.config files.

In CMS 12, you can configure data through option classes, either programatically through code, or populated from a configuration file like appSettings.json, or through environment variables. See configuration in .NET Core.

You can use the EPiServer.CMS.AspNetCore.Migration package for projects that are upgrading to CMS 12. That project enables populating options from "old" XML-based .config files. You should also rename the current web.config file to app.config.

Note: This project handles only Optimizely-specific config sections like <episerver>, <episerver.framework>, <episerver.shell> and so on.

See Configuration to configure the CMS.

Template Selection

Previously when selecting which templates to use in On-page editing the template selector first searched for a template with tag 'Edit' then a template without a tag and finally a template with tag 'Preview'. This has been changed so in on page edit it only searches for template with tag 'Edit' and then template with no tag (that is template with tag 'Preview' is not searched for during on page edit).

So if you have a template that is just tagged with 'Preview' that was used both for preview and on page edit then you need to add tag 'Edit' to the template registration as well to get the template registered for on page edit as well. 

PlugIn

GuiPlugIn

Previously, you could use GuiPlugInAttribute to extend admin mode with UI plugins, which were based on the previous WebForms admin mode. The new admin mode does not support GuiPlugInAttribute.

Instead, you should extend admin mode to register a menu provider.

PagePlugIn

Previously, you could register PagePlugInAttribute to extend WebForms requests. However, WebForms is no longer supported, and neither is PagePluginAttribute.

Instead, you should extend requests to register action filters and/or middleware components.

Dashboard

The dashboard has not been ported to CMS12. An alternative to show custom user interfaces in the CMS UI is to extend the menu system as described in Extending the navigation section. 

Reports

The Report section in CMS UI was based on WebForms and has not been ported to CMS12.

VirtualPathProviders 

In ASP.NET, you could register virtual path providers to map virtual paths to files. CMS contained some virtual path provider implementations such as VirtualPathNonUnifiedProvider. A similar concept in .NET Core is IFileProviders and CMS registers a composite file provider that delegates requests to registered file providers. The corresponding file provider implementation to VirtualPathNonUnifiedProvider is MappingPhysicalFileProvider. To add custom fileproviders, register them towards CompositeFileProviderOptions

See section File Providers to work with file providers within CMS.

Security

In ASP.NET, you can restrict access to certain virtual paths by defining an authorization element in web.config for a specific path and there define which users or roles can access that path. In a default CMS 11 installation, virtual path /EPiServer is restricted to roles WebEditors, WebAdmins, and Administrators. In ASP.NET Core, web.config is gone so you cannot restrict access to virtual paths in the same way. 

In CMS 12, shell modules are registered with a policy that specifies who can access resources from the module. A shell module's manifest defines which policy applies to a module. If you specify no policy, then CmsPolicyNames.DefaultShellModule is registered by default. You can configure the members of the default policies using CmsPolicyOptions.

Membership and Role providers are not supported in ASP.NET Core.

Profile/EPiServerProfile

The profile provider system that exist in ASP.NET is not available in ASP.NET Core. If you use ASP.NET identity, you can manage additional user data there.

Virtual application

In previous versions, you could not configure the CMS to run as a virtual application in IIS. Virtual application is an IIS specific feature and CMS 12 is designed to be cross platform, so it does not require IIS. Running CMS as a virtual application is no longer supported. 

IInitializableHttpModule

In previous versions, you could initialization modules implement interface IInitializableHttpModule and get access to the HttpApplication, which could setup event handlers for ASP.NET events. The HTTP pipeline is different in ASP.NET Core than in ASP.NET, so there are no longer similar events; the interface IInitializableHttpModule no longer exists.

In ASP.NET Core, you should register a middleware component to get access to each request.

Event provider

In CMS 11, there was a WCF-based event provider that supported communication over TCP or UDP. WCF is not supported in .NET Core and that provider was not converted, so there is no event provider that communicates over TCP or UDP included in CMS. We recommend using the provider based on Azure Service Bus.

CMS Search

In CMS 11, there was a WCF-based search service that was based on a local Lucene index.

WCF is not supported in .NET Core and that search service was not converted, so there is no local search service that you can install.

In CMS 12, use Search & Navigation for search.

Partial Controllers

In ASP.NET MVC, you could have a partial controller that was called as part of the execution of another controller. For example, you could use the BlockController<TBlock> and PartialContentController<TContent> classes as base controllers for such "partial" controller, but they were converted to view components in CMS 12.

In CMS 12, you should change the base class to BlockComponent<TBlock> or PartialContentComponent<TContent>, AsyncPartialContentComponent<TContent>.

Mirroring

Mirroring is not available in CMS 12 because it was based on WCF, which is not supported in .NET Core.

XForms

XForms is not available in CMS 12. You should use EPiServer Forms instead.

Dynamic properties

The user interface to edit dynamic properties was based on WebForms and was not ported, so you cannot edit dynamic properties through the UI. The API support for dynamic properties is obsoleted and disabled by default but can be enabled via options as a temporary solution when upgrading a site. API support will be removed in a future version.

HttpHandlers

HttpHandlers are no longer supported in ASP.NET Core and,  so MediaHandlerBase, StaticFileHandler, and BlobHttpHandler classes are no longer available.

Alternatives to http handlers in ASP.NET Core are middleware components or custom endpoints.

UrlRewriteProvider

UrlRewriteProvider generated friendly URLs for WebForms. CMS 12 does not support WebForms, so you cannot use UrlRewriteProviders. To extend routing, see Routing.

Visitorgroups 

Datetime handling of visitor group criteria TimeOfDayModel, NumberOfVisitsModel, TimePeriodModel and EventModel that has changed from DatabaseDateTimeKind.Unspecific to DatabaseDateTimeKind.Utc.
The time in the database should be converted to UTC. For more information, see Storing UTC date and time in database.

Do you find this information helpful? Please log in to provide feedback.

Last updated: Jul 02, 2021

Recommended reading