Problem when deploying Commerce that's been installed to an already existing CMS-site

Vote:
 

Hello, 

We've just installed Commerce into an already existing CMS-site and everything works just fine when running the application locally.

The process we've done is that we've installed the EPiServer.Commerce Nuget package into the CMS-project and created a Commerce database using the "dotnet-episerver create-commerce-database" command. The site starts up just fine locally. 

Now we want to deploy this to our hosting environment so we've copied the Commerce database to the hosting environment and pushed the updated code. When doing that we get this error:

Initialize action failed for 'Initialize on class EPiServer.Commerce.Initialization.InitializationModule, EPiServer.Business.Commerce, Version=14.12.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7'"}]}}
Unhandled exception. EPiServer.Framework.Initialization.InitializationException: Initialize action failed for Initialize on class EPiServer.Commerce.Initialization.InitializationModule, EPiServer.Business.Commerce, Version=14.12.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7
 ---> EPiServer.Core.EPiServerException: Content type "SalesCampaignFolder" is not allowed to be created under parent of content type "SysRoot"
   at EPiServer.Core.Internal.DefaultContentRepository.ValidateContentTypeAvailability(IContent content, ContentReference parentLink)
   at EPiServer.Core.Internal.DefaultContentRepository.Save(IContent content, SaveAction action, AccessLevel access)
   at EPiServer.DataAbstraction.Internal.DefaultContentRootService.Register[T](String rootName, Guid contentRootId, ContentReference parentRoot)
   at EPiServer.Commerce.Initialization.InitializationModule.RegisterContentRoots(InitializationEngine context)
   at EPiServer.Commerce.Initialization.InitializationModule.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.<>c__DisplayClass4_0.<Initialize>b__0()
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Execute(Action a, String key)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()
   --- End of inner exception stack trace ---
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()
   at EPiServer.Framework.Initialization.InitializationEngine.ExecuteTransition(Boolean continueTransitions)
   at EPiServer.Framework.Initialization.InitializationEngine.Initialize()
   at EPiServer.Hosting.Internal.EPiServerFrameworkHost.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
 at Epi.Program.Main(String[] args) in /src/CMS/MotoritOnline.Epi/Program.cs:line 22

I can see in the CMS-database that content types have been created for Commerce (for instance above mentioned SalesCampaignFolder) but some kind of relation seems to be missing. Has anyone else come across this problem and now how to solve it? Is it not possible to use a copy of our local Commerce database, do we perhaps need to execute the "dotnet-episerver create-commerce-database" for each environment where we want to use Commerce?

Best regards

/Martin

#303612
Jun 16, 2023 8:11
Vote:
 

I've tried to go through the initialization code using DotPeek and think I see an issue there (if I interpret the flow correctly).

In an InitalizationModule for Commerce, a new folder of type SalesCampaignfolder is being created under the RootPage: 

private static void RegisterContentRoots(InitializationEngine context)
    {
      ContentRootService instance = context.Locate.Advanced.GetInstance<ContentRootService>();
      IEnumerable<IContent> items = context.Locate.ContentLoader().GetItems(instance.List(), new LoaderOptions());
      Guid sysCampaignRootGuid = new Guid("48E4889F-926B-478C-9EAE-25AE12C4AEE2");
      Func<IContent, bool> predicate = (Func<IContent, bool>) (x => x.ContentGuid == sysCampaignRootGuid && x.Name.Equals("SysCampaignRoot"));
      if (items.Any<IContent>(predicate))
        return;
      instance.Register<SalesCampaignFolder>("SysCampaignRoot", sysCampaignRootGuid, (ContentReference) ContentReference.RootPage);
    }

In the Register function Save is being called if the content is not already existing:

public override void Register<T>(
      string rootName,
      Guid contentRootId,
      ContentReference parentRoot)
    {
      Validator.ThrowIfNullOrEmpty(nameof (rootName), rootName);
      ContentReference contentLink = this._contentRootRepository.Load(rootName);
      if (ContentReference.IsNullOrEmpty(contentLink))
      {
        T content;
        if (!this._contentRepository.TryGet<T>(contentRootId, out content))
        {
          content = this._contentRepository.GetDefault<T>(ContentReference.IsNullOrEmpty(parentRoot) ? (ContentReference) ContentReference.RootPage : parentRoot);
          content.ContentGuid = contentRootId;
          content.Name = rootName;
          this._contentRepository.Save((IContent) content, SaveAction.Publish | SaveAction.SkipValidation, AccessLevel.NoAccess);
        }
        this._contentRootRepository.Save(rootName, content.ContentLink.ToReferenceWithoutVersion());
      }
      else
      {
        T content;
        if (!this._contentRepository.TryGet<T>(contentLink, out content) || !(content.ContentGuid == contentRootId))
          throw new NotSupportedException(string.Format("There is already a root registered for name '{0}' with id '{1}'", (object) rootName, (object) contentLink));
      }
    }

I assume that the call to Save that's being called in this case is the one that sends "SaveAction.Publish | SaveAction.SkipValidation" (the exception message also points in this direction).

public override ContentReference Save(IContent content, SaveAction action, AccessLevel access)
    {
      if (content == null)
        throw new ArgumentNullException(nameof (content), "Parameter is empty");
      if (content.IsReadOnly())
        throw new EPiServerException("You cannot save a content that is read-only. Call CreateWritableClone() on content and pass the cloned instance instead.");
      action.ThrowIfInvalid(nameof (action));
      StatusTransition transition = this._statusTransitionEvaluator.Evaluate(content, action);
      DefaultContentRepository.CheckStatusTransition(transition, content, action, nameof (action));
      action = DefaultContentRepository.StandardizeSaveAction(action, transition);
      if (access == AccessLevel.Undefined)
        access = this._requiredAccessResolver.GetRequiredAccessLevel(content, transition);
      if (transition.IsNewContent())
      {
        this.ValidateContentTypeAvailability(content, content.ParentLink);
        this.InitializeSortIndex(content as PageData);
      }
      content.ContentLink = this._versionResolver.Resolve(content, action);
      SaveContentEventArgs saveEventArgs = this._contentEventsHandler.CreateSaveEventArgs(content, action, access, transition);
      bool isNew = content.IsNew();
  ... Code removed due to readability
    }

Then I'm a bit confused to why the the exception that's being thrown comes from the function called "ValidateContentTypeAvailability"? I would assume that the flag SaveAction.SkipValidation would skip this validation step?

Maybe this should be registered as a support ticket to Optimizely?

/Martin

#303621
Jun 16, 2023 14:05
Vote:
 

OK, an update about this issue. I found this forum post which mentions a similar problem: https://world.optimizely.com/forum/developer-forum/Commerce/Thread-Container/2021/11/episerver-commerce-upgrade-to-version---12-17-2---error---commercereportingfolder-is-not-allowed-to-be-created-under-parent-of-content-type-sysroot/

When looking for an appropriate place to add that specific code I found code where we explicitly set that one of our own page types are an (only?) allowed content type under the SysRoot-page. I guess this caused this error to occur to begin with. 

However, I still don't understand:
- Why this problem didn't also occur on my local machine but only after deploying to our test-environment?
- Also don't really understand what the meaning of SaveAction.SkipValidation is in the code where the SalesCampaignFolder is being created as a subpage to SysRoot?
I've restored our databases and removed the code that sets this content type restriction and now it seems to work as expected.
So if anyone else stumbles upon this problem, make sure that you don't have any code that sets which content types that are allowed under the SysRoot-page. 
/Martin
#303937
Jun 22, 2023 12:07
Vote:
 

Good investigation!

One possible explanation is that in your local machine, the EPiServer.Commerce.Initialization.InitializationModule was executed before your module, while it was excuted after your module on test. If two initialization modules are not depend on each other, their order of execution is undetermined. Of course, just a guess.

Regarding your (2) point - it seems arguable that SkipValidation should skip all validations, or certain validations. I can raise the question to Cms team who wrote the current code. 

#303938
Jun 22, 2023 12:45
Vote:
 

Thanks for bringing that question forward to the CMS-team Quan! I actually also started a support case (with id 1173786) when I didn't get any response in this question here on the forum if you want to connect the issues with each other. 

I know it's off topic from this initial question here but I mention it also in the support case that now when I've managed to get past this issue I stumble upon the issue mentioned here: https://world.optimizely.com/forum/developer-forum/Commerce/Thread-Container/2018/7/getting-error-when-loading-commerce-catalog/. I get this message when trying to load the Commerce Catalog:

I've tried adding my account to the Administrators-group and run the migrations but still no success, do you have any suggestions about how to fix this issue? 

#304085
Jun 26, 2023 8:43
Quan Mai - Jun 26, 2023 12:36
let's take that in another thread, shall we? anything you found in log ?
Martin Emanuelsson - Jun 26, 2023 13:02
Sounds good, I'll start a new thread and try to explain the problem in as much detail as possible. Will add a link to it here when I'm done.
Martin Emanuelsson - Jun 26, 2023 13:55
Here is some more input in a separate thread about this issue: https://world.optimizely.com/forum/developer-forum/commerce-14/thread-container/2023/6/getting-failed-loading-content-when-opening-commerce-catalog-on-site-where-commerce-has-been-installed-after-cms/
Vote:
 

The feedback is 

SkipValidation is designed to disable the validation of the content, not the structure. We agree that it's a bit confusing, however, changing it would be a breaking change so it's nothing we could do in v12.

#304095
Jun 26, 2023 12:36
Martin Emanuelsson - Jun 26, 2023 13:01
OK, then I understand why this occured in our case.
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.