HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Migrate to Optimizely Forms 6.0.0

Migrate from Optimizely Forms 5.x (EPiServer.Forms 5.x) to the major version 6.0.0.

This guide covers the steps required to migrate a project from Optimizely Forms 5.x to Optimizely Forms 6.0.0. Optimizely Forms 6.0.0 targets Optimizely Content Management System (CMS) 13, so you must migrate to CMS 13 before you can migrate to Optimizely Forms 6.0.0.

Prerequisites

  • .NET 10 or later
  • CMS 13
  • CMS 13 migration (if not already on CMS 13)

Migration steps

  1. Update NuGet package references – Update the Forms NuGet package version references to 6.0.0 in your .csproj:

    <PackageReference Include="EPiServer.Forms.Core" Version="6.0.0" />
    <PackageReference Include="EPiServer.Forms.UI" Version="6.0.0" />
    <!-- Or using umbrella package: -->
    <PackageReference Include="EPiServer.Forms" Version="6.0.0" />
    <!-- If using Azure Key Vault crypto: -->
    <PackageReference Include="EPiServer.Forms.Crypto.AzureKeyVault" Version="3.0.0" />

    Remove the following package reference (the package no longer exists):

    <PackageReference Include="EPiServer.AddOns.Helpers" />
  2. Register Forms services in DI – Forms 6.0.0 drops the old [ServiceConfiguration] auto-registration pattern (where possible). You must now call the Forms extension methods explicitly in your Program.cs or Startup.cs.

    • Before (5.10.7) – Services were auto-registered with InitializationModule.ConfigureContainer() and [ServiceConfiguration] attributes. No explicit registration required.
    • After (6.0.0) – Add the following code in Program.cs or Startup.csto to register all Forms, FormsUI, and FormsCore services:
      builder.Services.AddForms(); 
      If you need only partial registration:
      builder.Services.AddFormsCore();   // Core only
      builder.Services.AddFormsUI();     // UI + Core
      builder.Services.AddForms();       // Full stack (recommended)
      For Azure Key Vault encryption:
      builder.Services.AddFormsCryptoAzureKeyVault();
  3. Migrate configuration from Forms.config to appsettings.json. The legacy Forms.config XML configuration file is no longer supported. All settings must be in appsettings.json.

    • Forms.config before (5.10.7)

      <EPiServerForms
        WorkInNonJSMode="false"
        InjectFormOwnJQuery="false"
        InjectFormOwnStylesheet="true"
        SendMessageInHTMLFormat="true"
        DefaultUploadExtensionBlackList=".exe,.bat,.cmd"
        RenderingFormUsingDivElement="false"
        DisableFormCookies="false"
        HidePartialSubmissions="false"
      />
    • appsettings.json after (6.0.0)

      {
        "Forms": {
          "WorkInNonJSMode": false,
          "InjectFormOwnJQuery": false,
          "InjectFormOwnStylesheet": true,
          "SendMessageInHTMLFormat": true,
          "DefaultUploadExtensionBlackList": ".exe,.bat,.cmd",
          "RenderingFormUsingDivElement": false,
          "DisableFormCookies": false,
          "HidePartialSubmissions": false
        }
      }

      You must also move your storage provider configuration from Forms.config to appsettings.json:

      {
        "Forms": {
          "Providers": {
            "DefaultProvider": "ddsProvider",
            "ddsProvider": {
              "type": "EPiServer.Forms.Core.Data.Internal.DdsEncryptedPermanentStorage, EPiServer.Forms.Core"
            }
          }
        }
      }
  4. Remove references to deleted interfaces and classesIEPiServerFormsCoreConfig, IEPiServerFormsUIConfig, and the EPiServer.Forms.Configuration namespace were removed.

    • IEPiServerFormsCoreConfig and IEPiServerFormsUIConfig Before
      public class MyService
      {
          private readonly IEPiServerFormsCoreConfig _config;
          public MyService(IEPiServerFormsCoreConfig config) { _config = config; }
          public bool IsDisabled => _config.DisableFormCookies;
      }
      After
      using Microsoft.Extensions.Options;
      using EPiServer.Forms.Core.Options;
      
      public class MyService
      {
          private readonly FormsConfigOptions _config;
          public MyService(IOptions<FormsConfigOptions> config) { _config = config.Value; }
          public bool IsDisabled => _config.DisableFormCookies;
      }
    • EPiServer.Forms.Configuration namespace – Remove all using EPiServer.Forms.Configuration; directives and replace with appsettings.json-based configuration.
  5. Update custom post-submission actors – Remove ActiveExternalFieldMappingTable usage and removing overrides of removed methods in SendEmailAfterSubmissionActor. If your code subclasses SendEmailAfterSubmissionActor and overrides GetFriendlySummaryText or GetPredefinedPlaceHolders, remove those overrides. The logic now lives in DefaultPlaceHolderProvider.

    Before

    public class MyActor : PostSubmissionActorBase
    {
        public override object Run(object input)
        {
            var table = ActiveExternalFieldMappingTable; // REMOVED
            // ...
        }
    }

    After

    public class MyActor : PostSubmissionActorBase
    {
        private readonly IExternalFieldMappingService _externalFieldMappingService;
    
        public MyActor(IExternalFieldMappingService externalFieldMappingService)
        {
            _externalFieldMappingService = externalFieldMappingService;
        }
    
        public override object Run(object input)
        {
            var table = _externalFieldMappingService.GetActiveFieldMappingTable(FormIdentity);
            // ...
        }
    }
  6. Update validator types implementations – Update IExcludeValidatorTypes implementations.

    Before

    public class MyElement : ValidatableElementBlockBase, IExcludeValidatorTypes
    {
        public Type[] ValidatorTypesToBeExcluded => new[] { typeof(RegularExpressionValidator) };
    }

    After

    [AvailableValidatorTypes(Include = new[] { typeof(RequiredValidator) })]
    public class MyElement : ValidatableElementBlockBase
    {
        // Use AvailableValidatorTypesAttribute instead (AvailableValidatorTypesAttribute inherits from IAvailableValidatorTypesAttribute)
    }
  7. Update subclasses of InitializationModule – If you have a partial class or subclass of EPiServer.Forms.EditView.InitializationModule:

    • Remove any ConfigureContainer(ServiceConfigurationContext) override and move its logic to services.AddForms() registration.
    • Remove overrides of Instance_DeletedContent and Instance_PublishedContent. If you need to handle these events, subscribe directly to IContentEvents.
  8. Replace removed FormBusinessService method calls – Replace the following removed FormBusinessService method calls:

    • ReCalculateStepIndexIfNeeded(FormIdentity, FormContainerBlock, Submission, int, Guid, bool) – Replace with ReCalculateStepIndexIfNeeded(FormContainerBlock, Submission, int, bool).
    • IsMatchDependCondition(IElementDependant, IDictionary<string, object>) – Replace with IsConditionMatched(IElementDependant, object submittedFieldValue).
    • ReCalculateStepIndex(FormContainerBlock, int, IDictionary<string, object>, bool) – Replace with ReCalculateStepIndexIfNeeded(FormContainerBlock, Submission, int, bool).
    • BuildStepsAndElements(FormContainerBlock, bool) – Use overload requiring currentForm parameter.
    • ToHtmlStringWithFriendlyUrls(XhtmlString) – Replace with XhtmlRenderService.ToHtmlStringWithFriendlyUrls(...).
    • ReplaceFragmentWithFriendlyUrls(IStringFragment) – Replace with XhtmlRenderService.ReplaceFragmentWithFriendlyUrls(...).
  9. Replace removed extension methods – Replace the following removed extension methods:

    • FormsExtensions.HasAnyExternalSystem() – Replace with ExternalSystemService.HasExternalSystem().
    • contentRef.GetContent(string language) (two-parameter) – Replace with contentRef.GetContent(bool shouldFallback, string language).
    • str.SplitBySeparator(string separator) – Replace with str.SplitBySeparators(string[] separators).
    • FormsExtensionsUI.RenderWebFormViewToString(...) – Use standard ASP.NET Core view rendering.
    • IExternalFieldMappingService.GetAllExternalSystems() – Replace with ExternalSystemService.GetAllExternalSystems().
    • ExternalSystemEditorDescriptors.GetSelectedFieldsName(FormIdentity, string) – Use its protected method GetSelectedFieldsName(FormContainerBlock, ElementBlockBase, string).
  10. Remove form data usageIForm.Data and Form.Data usage.

    Before

    var data = form.Data; // PropertyBag

    After

    var data = submission.Data; // IDictionary<string, object>
  11. Update subclasses – Update sublcasses of PropertyGenericList\<T\>.

    Before

    public override object SaveData(PropertyDataCollection properties)
    {
        return base.SaveData(properties);
    }

    After

    public override object SaveData()
    {
        return base.SaveData();
    }
  12. Review PropertyGenericList\<T\> model types for System.Text.Json compatibilityPropertyGenericList<T> now uses System.Text.Json with camelCase naming. If your model types use Newtonsoft.Json-specific attributes, update them.

    Before

    using Newtonsoft.Json;
    
    public class MyModel
    {
        [JsonIgnore]
        public string InternalProp { get; set; }
    
        [JsonProperty("my_key")]
        public string MyKey { get; set; }
    }

    After

    using System.Text.Json.Serialization;
    
    public class MyModel
    {
        [JsonIgnore]
        public string InternalProp { get; set; }
    
        [JsonPropertyName("my_key")]
        public string MyKey { get; set; }
    }
    🚧

    Important

    Existing data serialized with Newtonsoft.Json (camelCase or otherwise) should round-trip correctly if the property names match. Verify this for each model type used in PropertyGenericList<T>.

  13. Update scheduled job references – If you reference scheduled job types directly, switch to IScheduledJobRepository.

    Before

    var job = new UpdateUploadFolderACLJob(); // REMOVED (now internal)

    After

    var job = _scheduledJobRepository.Get(Guid.Parse("EB289EAA-5485-4886-A59C-5C5E0B950199"));
  14. Update SpecializedProperties if subclassed – If you subclass any SpecializedProperties type, verify database property definition names are consistent. CMS 13 does not allow spaces in property definition names, so the new display names are without spaces. The following display names changed:

    • Dependency conditions collection – Changed to DependencyConditionsCollection.
    • Message Template – Changed to MessageTemplate.
    • Property Field Mapping Collection – Changed to PropertyFieldMappingCollection.
    • Property Connected Data Source Collection – Changed to PropertyConnectedDataSourceCollection.
    • Validator with message collection – Changed to ValidatorWithMessageCollection.
    • Web Hook – Changed to WebHook.
    📘

    Note

    This should sync automatically when the CMS starts, but if you have custom code that queries property definitions by name, you may need to update it.

  15. Update FreeGeolocationProvider configuration – If you configured FreeGeolocationProvider in Forms.config or web.config provider XML, update it to appsettings.json.

    Before

    <providers>
      <add name="FreeGeoIP" type="..." geoApiUrl="https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY" />
    </providers>

    After

    services.AddFreeGeolocationProvider(options =>
    {
        options.GeoApiUrl = "https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY";
    });
    
    // OR
    
     services.AddFreeGeolocationProvider("https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY");
    📘

    Note

    If you are using the EPiServer.CloudPlatform.Cms package, it registers a default IGeolocationProvider. Register the Forms FreeGeoLocationProvider after AddCmsCloudPlatformSupport(...) so it takes precedence.

     services.AddCmsCloudPlatformSupport(_configuration); 
     services.AddFreeGeolocationProvider("https://api.freegeoip.app/json/{0}?apikey=YOUR_KEY");
  16. Update custom actor constructors – If you have custom actors with parameterless constructors that use ServiceLocator, update it to let the DI container provide dependencies.

    Before

    public class MyActor : PostSubmissionActorBase
    {
        public MyActor() : this(ServiceLocator.Current.GetInstance<MyService>()) { }
        public MyActor(MyService service) { ... }
    }

    After

    public class MyActor : PostSubmissionActorBase
    {
        public MyActor(MyService service) { ... }
    }

Troubleshoot common migration errors

Missing services.AddForms() call

  • SymptomInvalidOperationException: Unable to resolve service for type 'EPiServer.Forms.Core.Internal.DataSubmissionService'.
  • Fix – Add services.AddForms() in Program.cs.

Forms.config settings silently ignored

  • Symptom – Behaviors configured in Forms.config (like WorkInNonJSMode and upload blacklist) no longer apply after upgrade.
  • Fix – Migrate all settings to appsettings.json under the Forms key.

PropertyGenericList deserialization failures

  • Symptom – Editors show empty lists or errors for properties that previously had data.
  • Fix – Check that your model types are System.Text.Json-compatible. Verify camelCase property naming matches stored JSON. Consider a one-time data migration if necessary.

Scheduled jobs not found

  • Symptom – Accessing UpdateUploadFolderACLJob and others causes a compile or runtime error.
  • Fix – Use IScheduledJobRepository.Get(Guid) with the job GUIDs instead.

Azure Key Vault crypto engine not registered

  • Symptom – Decrypted CSV export not available, or IFormCryptoEngine not resolved.
  • Fix – Call services.AddFormsCryptoAzureKeyVault() in addition to services.AddForms().

Email not sent after upgrade

  • Symptom – Emails are not delivered with errors in logs mentioning ISmtpClient.
  • Fix – Ensure FormsSmtpClient (MailKit) is properly registered with services.AddFormsUI(). Verify SMTP settings in appsettings.json.

Migration checklist

  • Update EPiServer.Forms NuGet package to 6.0.0.
  • Remove EPiServer.AddOns.Helpers package reference.
  • Add services.AddForms() in Program.cs.
  • Migrate Forms.config settings to appsettings.json.
  • Remove all references to IEPiServerFormsCoreConfig, IEPiServerFormsUIConfig, EPiServer.Forms.Configuration.*.
  • Update or remove custom InitializationModule subclass code.
  • Update custom actors by removing ActiveExternalFieldMappingTable and updating constructors.
  • Remove IExcludeValidatorTypes implementations and use IAvailableValidatorTypesAttribute.
  • Replace removed FormBusinessService methods with their non-obsolete equivalents.
  • Replace removed FormsExtensions and FormsExtensionsUI extension methods.
  • Update PropertyGenericList model types for System.Text.Json compatibility.
  • Update SaveData() overrides to remove parameter.
  • Verify scheduled job GUIDs are accessible in IScheduledJobRepository if needed.
  • Verify property definitions in CMS database after upgrade.
  • Test existing form submission data for deserialization correctness.
  • Configure geo provider URL in appsettings.json if used.
  • Call services.AddFormsCryptoAzureKeyVault() if using AzureKeyVault crypto.