Try our conversational search powered by Generative AI!

Magnus Rahl
Nov 14, 2023
  1875
(6 votes)

Support for .NET 8

As .NET 8 is released at Dotnetconf today, me and the teams are also happy to announce support for .NET 8 in Optimizely products!

We have tested the latest CMS, Commerce and Search & Navigation packages as well as the most common Optimizely provided addons using the .NET 8 release candidates. After a quick verification with the RTM version of .NET 8 I can now officially say that we support .NET 8.

Our DXP cloud service also supports running .NET 8 applications. Which runtime version is used is determined by which version you compile your project for, through the metadata that the build outputs to the [ProjectName].runtimeconfig.json file. If you want to you can also update the transitive dependencies from our packages to the Microsoft.Extensions.* packages. We have tested running in .NET 8 both with the lowest dependency version we require (6.0) and the 8.0 versions of those.

As mentioned before we continue to release assemblies compiled for .NET 6, as it is not required for us as library authors to change our compilation target for you to run in .NET 8 now that we have verified compatibility. We will change our compilation target too in the future, most likely as part of a future major version release.

If you run into any issues, as usual contact support.

Now, go get .NET 8 and build even better applications with Optimizely!

Nov 14, 2023

Comments

Johan Book
Johan Book Nov 15, 2023 07:06 AM

Great news! Thanks for this update!

Otto G
Otto G Nov 15, 2023 02:07 PM

Hi Johan, this sounds great! However, I just tested an Optimizely website locally under .NET 8, and once the website is up and running and responding to requests, I get constant console messages like this (every 2 seconds or so):

info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      High memory pressure event received.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      Waiting for a Gen 2 GC before compacting the cache again.

When compiling and running the exact same website (with the latest versions of all Nuget packages) under .NET 6.0 or 7.0, there are no such messages printed.

Should this be a source of concern, or is it just something with .NET 8 that causes info messages to be printed for things that have been happening silently in previous versions?

Also, do you know if DXP hosting environments will automatically be prepared for .NET 8, or is it done upon request?

By the way, I noticed that authentication using Microsoft.AspNetCore.Authentication.WsFederation that works with 6.0 and 7.0 may fail with .NET 8.0. I guess that this issue is unlikely to be Optimzely-related, though.

Otto G
Otto G Nov 15, 2023 02:09 PM

Hi Magnus, this sounds great! However, I just tested an Optimizely website locally under .NET 8, and once the website is up and running and responding to requests, I get constant console messages like this (every 2 seconds or so):

info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      High memory pressure event received.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      Waiting for a Gen 2 GC before compacting the cache again.

When compiling and running the exact same website (with the latest versions of all Nuget packages) under .NET 6.0 or 7.0, there are no such messages printed.

Should this be a source of concern, or is it just something with .NET 8 that causes info messages to be printed for things that have been happening silently in previous versions?

Also, do you know if DXP hosting environments will automatically be prepared for .NET 8, or is it done upon request?

By the way, I noticed that authentication using Microsoft.AspNetCore.Authentication.WsFederation that works with 6.0 and 7.0 may fail with .NET 8.0. I guess that this issue is unlikely to be Optimizely-related, though.

Otto G
Otto G Nov 15, 2023 02:15 PM

Sorry for the double almost identical comments – the delete button gives a jQuery error message: “TypeError: undefined is not an object (evaluating 'c._buttons')”

Magnus Rahl
Magnus Rahl Nov 15, 2023 02:26 PM

Thanks for bringing this to our attention. When running locally it could just have been your computer being on high total memory load at that time. But we will definitely look into it, I know there have been changes in how the GC works in .NET 8.

DXP will automatically select the runtime based on your compilation target as mentioned in the article.

There may very well be breaking changes / issues in .NET itself or surrounding packages because they are new major versions.

Otto G
Otto G Nov 15, 2023 03:07 PM

The first website I tried has a rather large content database. I have now also tested a smaller website in terms of content, which has a similar code base.

When running that second website locally under .NET 8.0, it takes a little while and perhaps 10 or 20 page requests before the GC messages start to appear. With the bigger website, they appear immediately after the first request, or at the latest after a few requests.

Both websites run without GC messages when targeting .NET 7.0. I can stop either website, edit the csproj file to change the targeting back and forth, and build and run the website again – and if I changed to 8.0, the GC info messages appear, and if I changed to 7.0, they do not appear.

So, the overall memory load on the computer is the same in both cases. It is under macOS 14 Sonoma, by the way.

Magnus Rahl
Magnus Rahl Nov 15, 2023 06:09 PM

My investigations so far show that at least on DXP (i.e. in a Linux container) it gets the memory measurement correct on .NET 8. It reports the same memory load level as the azure monitoring.

The event you see is when it detects memory load above what it sees as high (default 90% IIRC) and therefore compacts the cache. If you enable debug logging on the EPiServer.Hosting.Internal namespace you should see the actual percentage value reported. Note that this will be 0 until the first GC has happened.

Jørgen Helgheim
Jørgen Helgheim Nov 15, 2023 07:43 PM

This is great. I change the target from .NET 6 to .NET 8 today asweel and with simples checks it indicates a good running application (CMS and Customized Commerce B2C). 

I have the same info messages mentioned by Otto G, but I had them already in NET 6, so.. I'm planing to take a look at https://docs.developers.optimizely.com/content-management-system/docs/monitoring-memorycache for this issue. 

Otto G
Otto G Nov 16, 2023 10:45 AM

Hi again, when I turn on the debugging, the messages that appear after a few pages have been served (or sometimes before even a single page has been served) indicate “99 % of 24576 MB used”. 24 GB is the total amount of RAM installed in the computer. They go directly from no messages to 99%, and even after reportedly compacting the cache, the next message that comes shortly afterwards still says 99%.

I can monitor the process with the “top” command, and the memory consumption tends to be around 160 MB (differs somewhat between runs), and there is no real difference before and after the messages start appearing.

Before the messages have started:

PID    COMMAND      %CPU TIME     #TH  #WQ  #POR MEM   PURG CMPR PGRP  PPID STATE    BOOSTS    %CPU_ME %CPU_OTHRS UID  FAULTS COW  MSGSE
46073  dotnet       0.0  00:01.54 30   1    114  159M  0B   0B   46073 821  sleeping *0[1]     0.00000 0.00000    502  18872  359  6632

After the messages have started appearing:

PID    COMMAND      %CPU TIME     #TH  #WQ  #POR MEM   PURG CMPR PGRP  PPID STATE    BOOSTS    %CPU_ME %CPU_OTHRS UID  FAULTS COW  MSGSE
46073  dotnet       0.0  00:01.65 24   1    96   159M  0B   73M  46073 821  sleeping *0[1]     0.00000 0.00000    502  19007  359  6676

Here is part of the output from the same “dotnet run” invocation, with debug logging enabled, starting from the first memory-related line (these messages all appeared within a few seconds):

dbug: EPiServer.Hosting.Internal.GCMemoryMonitor[0]
      99 % of 24576 MB used.
info: EPiServer.Framework.Cache.Internal.MemoryPressureMonitor[0]
      Current highest memory pressure level reported by a memory monitor is High. Adjusting memory status poll interval to 1000 ms.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      High memory pressure event received.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      Initiating cache compacting removing 10 % of cached items.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      Finished compacting cache in 6 ms.
dbug: EPiServer.Hosting.Internal.GCMemoryMonitor[0]
      99 % of 24576 MB used.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      High memory pressure event received.
info: EPiServer.Framework.Cache.Internal.MemoryCacheCompactor[0]
      Waiting for a Gen 2 GC before compacting the cache again.

Otto G
Otto G Nov 16, 2023 10:51 AM

For completeness, here is a screen dump of the macOS activity monitor, right after a CMS process has stared outputting these messages – no visible effect on the overall memory pressure chart.

Otto G
Otto G Nov 16, 2023 11:00 AM

Under .NET 7.0, the memory consumption reported by “top” is about the same. With logging on, occasional memory debug messages are emitted, and they tend to show around 67%.

Output from “top”:

PID    COMMAND      %CPU TIME     #TH  #WQ  #POR MEM   PURG CMPR PGRP  PPID STATE    BOOSTS    %CPU_ME %CPU_OTHRS UID  FAULTS COW  MSGSE
46655  dotnet       0.0  00:01.70 23   1    91   166M  0B   72M  46655 821  sleeping *0[1]     0.00000 0.00000    502  21618  360  7423

Memory debug messages:

dbug: EPiServer.Hosting.Internal.GCMemoryMonitor[0]
      68 % of 24576 MB used.

dbug: EPiServer.Hosting.Internal.GCMemoryMonitor[0]
      67 % of 24576 MB used.

Magnus Rahl
Magnus Rahl Nov 16, 2023 12:39 PM

Ok, so that sounds like it is a Mac OS specific .NET 8 issue. The 67 % you see seems to approximately match the memory pressure you see in the GUI. We're using the output from the GC.GetGCMemoryInfo method. It is the total "physical" memory it looks at (or container limits). What you're seeing as a percentage is the ratio between the reported MemoryLoadBytes and TotalAvailableMemoryBytes. It seems like MemoryLoadBytes is then returned incorrectly on your Mac when running in .NET 8. So could be a bug in .NET. Not disasterous for a dev environment, and in DXP production environments as mentioned it seems to work correctly.

Otto G
Otto G Nov 16, 2023 04:07 PM

Yes that seems to be the case. I have now installed the website under a Ubuntu virtual machine, connected to the same database. The memory usage shown in the debug messages slowly increases from about 25% to about 30% during use of the website. The virtual machine is configured with 4 GB of RAM, and the debug messages show 3911 MB as the total available. This seems more reasonable – but “top” shows just 5.1% in the memory column for the process, though. I am not sure exactly how to interpret the values from “top” in this context. But it does seem like a lot if the process indeed uses around 1 GB right from the start, increasing to around 1.2 GB, so possibly, the numbers are a bit off under Linux, too.

Sample debug printout:

dbug: EPiServer.Hosting.Internal.GCMemoryMonitor[0]
    30 % of 3911 MB used.

Output from “top”:

top - 16:00:45 up  2:37,  2 users,  load average: 0.00, 0.02, 0.00
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   3911.5 total,    566.5 free,    851.2 used,   2493.9 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   2714.6 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                             
   2906 ottog     20   0  260.6g 205964 118872 S   0.0   5.1   0:02.88 dotnet                                                              

Magnus Rahl
Magnus Rahl Nov 16, 2023 04:15 PM

How the GC measures the available memory and how that relates to what top will show you is way beyond my knowledge. But just pointing out again that the percentage is supposed to represent the total memory load on the "machine" (or container limit), including all other running processes, the OS itself etc. So there is no contradiction in the reported 30% memory usage on a freshly started container even if the site process running the site itself only uses 200 MB or so. The memory monitoring is designed for production scenarios when the site is the primary thing running on the host, so it makes sense to monitor the overall memory usage of the host rather than of the process.

Otto G
Otto G Nov 16, 2023 04:30 PM

Ah, right, I misunderstood the explanation of the percentage part of the debug printouts in the previous comments. Thank you for clarifying. Then it does seem plausible that the figures are right under Linux with .NET 8.0. That is reassuring.

The macOS bug is a bit annoying when testing things locally, though, and I wonder how it might affect testing of code that uses https://learn.microsoft.com/en-us/aspnet/core/performance/caching/output?view=aspnetcore-8.0 and/or ISynchronizedObjectInstanceCache. (That is, if things will repeatedly and frequently be evicted from the caches, even though the memory is not actually full.)

Johan Kronberg
Johan Kronberg Nov 17, 2023 03:34 PM

Great to get the performance improvements and to use C# 12 etc but to really be happy we need support for Blazor SSR as well. Please put some prioritization on this, and not just only on the out-of-process setups/SaaS etc, to remain in good light with .NET developers. Still plenty of apps that will run on-prem or in PaaS that will live long with in-process ASP.NET templates.

Help out with votes: https://feedback.optimizely.com/ideas/CMS-I-416

Otto G
Otto G Nov 17, 2023 09:27 PM

Regarding Blazor, I suppose there cannot be much, if anything, that prevents an Optimizely CMS website from being rendered with Blazor SSR templates, though?

Articles like https://medium.com/capgemini-microsoft-team/using-blazor-in-your-existing-asp-net-core-mvc-applications-f1b09bd66f4b indicate that there has been good interoperability with cshtml Razor templates for quite some time. So, they are probably not so different from each other in how they integrate with routing and view resolution concepts in ASP.NET Core. Presumably, some configuration of view engines will be needed, and perhaps adjustment of the ViewResult objects returned by controllers.

Of course, making navigation between pages work like in a typical SPA website could potentially be a lot more work, unless there is already some clever solution to this built into Blazor SSR. (I do not know much about the capabilities of Blazor – I just got curious and wondered whether it really depends on the packaged part of the CMS if you can use Blazor SSR templates or not, or if it is just a matter of writing the website-specific code according to the desired template type.)

And things like Forms, which have a lot of automatic things going on behind the scenes in the cshtml views, may definitely be potential issues. But then again, perhaps they will work even inside of Blazor SSR templates (just a bit differently from other parts of the page), thanks to the similarities in technology.

Perhaps a demo project is needed, more than implementation of support as such?

Johan Kronberg
Johan Kronberg Nov 18, 2023 04:58 PM

I have done a lot of work with Blazor SSR already (using the Preview and RC versions, but not related to EPiServer.CMS).

Even with things are in dotnet 8 like HtmlRenderer().RenderComponentAsync<T>() that allows you to render a page/component into a string from anywhere and new RazorComponentResult<T>(someReadOnlyDictionary()) that you can use to return a "set SSR page/componenent" from MVC Controllers and minimal endpoints I'd say using that in the current CMS version will probably just mean a lot of "hacks" with extra steps and switching modes (between the new lightweight smooth way, and the old *Contexts and Renderers mess when needing to render something) resulting in poor performance.

You won't be able to use EditForm component as designed and you won't get the benefit of putting all code in the .razor-file - you need to model-bind in a separate controller or endpoint instead of using a OnSubmit-handler in the Blazor SSR page/component. Also, Tag and HTML Helpers are not a concept in any Blazor mode.

I wrote that feature request quite long ago but knowing more how dotnet 8 turned out my guess is that the order of things needed to be built by Optimizely to use Blazor SSR is something like this:

  • Tap into routing and select the correct Blazor page to initialize, set a CascadingParameter for CurrentPage.
  • Build a component library for things like rendering XhtmlString and ContentArea properties that locates the wanted component to use for a BlockData instance, adds the on-page-edit stuff etc.

Personally I don't need to any support for the Blazor SPA modes and that type of routing.

I guess having the above SSR support, inititializing an interactive component "island" that uses Server or WASM mode would work without anything else needed.

huseyinerdinc
huseyinerdinc Nov 20, 2023 09:18 AM

Great news! I have tried to upgrade one of the projects but it didn't go well for me. The application threw an exception saying a constructor couldn't be found.  Method not found: 'Void Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping..ctor

It seems that the fail originates from EPiServer.Cms.UI.AspNetIdentity.ApplicationUserProvider`1.GetUserAsync(String username)

The full stack trace:

System.MissingMethodException: Method not found: 'Void Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping..ctor(System.String, System.Type, System.Nullable`1<System.Data.DbType>, Boolean, System.Nullable`1<Int32>, Boolean, System.Nullable`1<Int32>, System.Nullable`1<Int32>)'.
         at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerSqlVariantTypeMapping..ctor(String storeType)
         at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerTypeMappingSource..ctor(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies)
         at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
         at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
         at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
         at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
         at Microsoft.Extensions.DependencyInjection.SqlServerServiceCollectionExtensions.<>c.<AddEntityFrameworkSqlServer>b__1_2(IServiceProvider p)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
         at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
         at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
         at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
         at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
         at Microsoft.EntityFrameworkCore.DbContext.Set[TEntity]()
         at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.get_UsersSet()
         at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.get_Users()
         at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.FindByNameAsync(String normalizedUserName, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Identity.UserManager`1.FindByNameAsync(String userName)
         at EPiServer.Cms.UI.AspNetIdentity.ApplicationUserProvider`1.GetUserAsync(String username)

Magnus Rahl
Magnus Rahl Nov 23, 2023 01:58 PM

huseyinerdinc did you figure this out? It looks like a version conflict between different versions of entity framework related projects. Do you have multiple projects in your solution referencing different EF package versions perhaps? If you cannot figure it out please send this as a support ticket if you haven't already. 

huseyinerdinc
huseyinerdinc Dec 1, 2023 07:59 AM

Magnus Rahl, not yet. We will revisit this when it's time to upgrade the site to .NET 8. We don't have any other projects in the solution but we do reference the designer package to be able to use migrations.

        <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.16">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>

Johan Book
Johan Book Dec 11, 2023 11:07 AM

When we run the VS profiler during startup we can see that there seems to be a lot of allocations going on in EPiServer.Web.Routing.ContentEndpointRouteBuilderExtensions.MapContent()

In fact, 2.3M allocations (!), 145 MB of memory and this represent about 1/4 of the total memory allocated by the app during startup.

Not sure if this is intentional or if there is logic in your route builder extension that has run amoc.

We're on EPiServer.Cms.AspNetCore.Routing 12.19.1.

Johan Book
Johan Book Dec 11, 2023 11:15 AM

Sorry, was not able to edit my post (or delete it for some reason), I mean to say these tests were made with .NET 6 and the routing extensions represent 1/4 of the _allocations_ (not the actual memory consumption).

I will report this as a support ticket, just thought it was interesting finding with regards to the discussion.

Johan Book
Johan Book Jan 7, 2024 07:36 PM

CMS-31285 was raised with the development team re: heavy allocations during startup.

Apparently there is not that much that CMS allocates itself. Most allocations happen when registering endpoints for all shell modules using ASP.NET Core API, which is where the allocations are said to happen.

Please login to comment.
Latest blogs
Roll Your Own Security Headers

Proper security headers are a must for your Optimizely driven website. There are a variety of tools out there that will help with this, but when...

Ethan Schofer | Feb 21, 2024

Migrate Catalog content properties

A colleague asked me yesterday – how do we migrate properties of catalog content. There is, unfortunately, no official way to do it. There are...

Quan Mai | Feb 20, 2024 | Syndicated blog

Adjust log levels in Optimizely DXP

You may adjust the log levels for your site in Optimizely DXP yourself, but only for the Integration environment. Follow this step-by-step guide.

Tomas Hensrud Gulla | Feb 20, 2024 | Syndicated blog

Introducing Search & Navigation Dashboard for Resource Usage

We're excited to unveil the latest addition to the Search & Navigation suite: a dashboard designed to proactively monitor your resource usage. It's...

Edvin Dackelid Johansson | Feb 20, 2024