Porting from .NET Framework 4.8 to .NET Core 8

You’ve added this to your Porting from .NET Framework v4.8 to .NET Core 8 thread, can you confirm that the same code has different behavior after changing to .NET 8? i.e. what was the behavior when the same code was running on v4.8? Is both the client and server running on .NET 8?

Hi, I can confirm that both client and server are running on .NET 8 but I had to change some code (expecially server-side) during the porting.
Now I found and fixed the previous issue and now I am able to load the ServerEventFeature plugin with no errors.
But when I try to call serverEventsClient.SubscribeToChannels(myChannels) I get this error:

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:

HTTP: POST /event-subscribers/{Id}HTTP: POST /event-subscribers/{Id}at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(Span1 candidateState) at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ProcessFinalCandidates(HttpContext httpContext, Span1 candidateState)at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.Select(HttpContext httpContext, Span`1 candidateState)at Microsoft.AspNetCore.Routing.Matching.DfaMatcher.MatchAsync(HttpContext httpContext)at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

If I go to http://localhost:12031/operations/metadata I can see this line:

UpdateEventSubscriber | UpdateEventSubscriberResponse | UpdateEventSubscriberService | [ ANY ] | [ /event-subscribers/{Id}, /event-subscribers/{Id} ]

I think the error is that I’ve got a duplication endpoint.
Can you tell me what I am doing wrong?
Do you need any code or any other information?

Thank you very much.

I’m not seeing this behavior, I’m able to subscribe to channels with that API, can you make sure haven’t registered multiple ServerEventsFeature plugins. If not please provide a repro on GitHub I can run locally to repro the issue.

Unfortunately, I’m having trouble uploading the project to GitHub, but I can post all the AppHost code here:

public class AppHost : AppHostBase
{
    readonly string _privateKey;
    public AppHost(string privateKey)
        : base("KlisFaReServer",
                typeof(ServiceNoAuth).Assembly,
                typeof(Services).Assembly,
                typeof(ServiceCallback).Assembly,
                typeof(ServicesMobile).Assembly)
    {
        _privateKey = privateKey;
    }

    public override void Configure(Container container)
    {
        // Configure ServiceStack, Run custom logic after ASP.NET Core Startup
        SetConfig(new HostConfig
        {
            WriteErrorsToResponse = true,
            EnableOptimizations = true,
        });

        container.AddPlugin(new AuthFeature(() => new AuthUserSession(),
            [
                new CustomCredentialsAuthProvider()
                {
                    CustomValidationFilter = (AuthContext arg) =>
                    {
                        return  null;
                    },
                },
            ])
        {
            IncludeDefaultLogin = false,
        });

        // Encrypted messages
        container.AddPlugin(new EncryptedMessagesFeature
        {
            PrivateKeyXml = _privateKey,
        });

        // COR
        container.AddPlugin(new
            CorsFeature(allowedOrigins: "*",
            allowedMethods: CorsFeature.DefaultMethods,
            allowedHeaders: "*"
            ));

        // Server events
        container.AddPlugin(new ServerEventsFeature());

        AfterInitCallbacks.Add(host =>
        {
            ServiceMonitor.Instance().ServerEvents = host.Resolve<IServerEvents>();

            foreach (var op in HostContext.Metadata.OperationsMap)
            {
                foreach (var route in op.Value.Routes)
                {
                    Console.WriteLine($"ROUTE: {route.Path} [{route.Verbs}] from {op.Value.ServiceType.Assembly.FullName}");
                }
            }

            Console.WriteLine();
            Console.WriteLine("=== REGISTERED PLUGINS ===");
            foreach (var plugin in Plugins)
            {
                Console.WriteLine(plugin.GetType().FullName);
            }

            Console.WriteLine();
            Console.WriteLine("=== LOADED ASSEMBLIES ===");
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (asm.FullName.Contains("ServiceStack"))
                    Console.WriteLine(asm.Location);
            }

            Console.WriteLine();
            Console.WriteLine("=== REGISTERED SERVICES ===");
            foreach (var op in HostContext.Metadata.OperationsMap)
            {
                Console.WriteLine($"{op.Key} --> {op.Value.ServiceType.FullName}");
            }

            Console.WriteLine();
            Console.WriteLine(">>> AppHost INIT " + DateTime.Now);

            var count = HostContext.Metadata.OperationsMap
                .Count(x => x.Value.ServiceType == typeof(ServerEventsFeature));

            Console.WriteLine();
            Console.WriteLine(">>> ServerEventsFeature count: " + Plugins.Count(p => p is ServerEventsFeature));
            
            Console.WriteLine();
        });
    }
}

As you can see, I’ve added some logs to the AfterInitCallbacks event, and this is what it displays when the application starts:

ROUTE: /types [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/metadata [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/csharp [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/fsharp [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/vbnet [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/typescript [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/typescript.d [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/js [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/mjs [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/php [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/python [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/ruby [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/go [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/rust [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/zig [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/dart [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/swift [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/java [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /types/kotlin [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /auth/{provider} [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /auth [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /auth/logout [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /assignroles [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /unassignroles [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /metadata/app [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /metadata/nav [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /metadata/nav/{Name} [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /publickey [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /event-unregister [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /event-subscribers [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /event-subscribers/{Id} [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
ROUTE: /event-subscribers/{Id} [System.String] from ServiceStack, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null

=== REGISTERED PLUGINS ===
ServiceStack.PreProcessRequest
ServiceStack.Formats.HtmlFormat
ServiceStack.Formats.CsvFormat
ServiceStack.Formats.JsonlFormat
ServiceStack.PredefinedRoutesFeature
ServiceStack.MetadataFeature
ServiceStack.NativeTypesFeature
ServiceStack.HttpCacheFeature
ServiceStack.RequestInfoFeature
ServiceStack.SvgFeature
ServiceStack.UiFeature
ServiceStack.Validation.ValidationFeature
ServiceStack.VirtualFilesFeature
ServiceStack.AuthFeature
ServiceStack.EncryptedMessagesFeature
ServiceStack.CorsFeature
ServiceStack.ServerEventsFeature
ServiceStack.SessionFeature

=== LOADED ASSEMBLIES ===
C:_Projects\KlisFaRe\trunk v3.6\Server\KlisFaReServer\bin\x64\Debug\net8.0\ServiceStack.Client.dll
C:_Projects\KlisFaRe\trunk v3.6\Server\KlisFaReServer\bin\x64\Debug\net8.0\ServiceStack.Text.dll
C:_Projects\KlisFaRe\trunk v3.6\Server\KlisFaReServer\bin\x64\Debug\net8.0\ServiceStack.dll
C:_Projects\KlisFaRe\trunk v3.6\Server\KlisFaReServer\bin\x64\Debug\net8.0\ServiceStack.Interfaces.dll
C:_Projects\KlisFaRe\trunk v3.6\Server\KlisFaReServer\bin\x64\Debug\net8.0\ServiceStack.Common.dll

=== REGISTERED SERVICES ===

[ There are a lot of rows. Tell me if you need to know which services were registered and I’ll paste all the rows. ]

AppHost INIT 04/05/2026 10:20:21

ServerEventsFeature count: 1

Finally, I’ve checked several times, but I instantiate ServerEventsFeature only once (and I do so in the code I posted above). If I comment it out, there’s no line relating to UpdateEventSubscriber on the metadata page; if I uncomment it, I have the UpdateEventSubscriber line but with a duplicate subscription.

Let me know if you need anything else.

Thank you.

This isn’t close to a stand-alone App I can compile or run, if you want me to debug your App I’ll need a stand-alone repro I can run.

I’ve already mentioned twice above in this thread that you must register plugins with services.AddPlugin().

This can be added in each plugin, e.g:

[assembly: HostingStartup(typeof(MyApp.ConfigureServerEvents))]

namespace MyApp;

public class ConfigureServerEvents : IHostingStartup
{
    public void Configure(IWebHostBuilder builder) => builder
        .ConfigureServices(services => {
            services.AddPlugin(new ServerEventsFeature());
        });
}

Or it can be added in your AppHost with:

[assembly: HostingStartup(typeof(MyApp.AppHost))]

namespace MyApp;

public class AppHost() : AppHostBase("MyApp"), IHostingStartup
{
    public void Configure(IWebHostBuilder builder) => builder
        .ConfigureServices((context,services) => {
            services.AddPlugin(...);
        });

    //...
}

Thank you for your answer.

I cannot send you all the code but here: Dropbox you can download just the portion of code I use to start my service and service stack. I was able to reproduce the problem in this small piece of code.

I hope this is enough to help me figure out what I’m doing wrong.

Thanks again.

I can see from your AppHost.cs that you’re still not using registering plugins properly as I’ve specified multiple times.

Why are you still refusing to use services.AddPlugin() after having been told multiple times that you need to register all Plugins in ConfigureServices()??

[assembly: HostingStartup(typeof(Euklis.FaRe.Function.Server.ConnectionFramework.AppHost))]

namespace Euklis.FaRe.Function.Server.ConnectionFramework;

//..
public class AppHost() : AppHostBase("MyApp"), IHostingStartup
{
        public void Configure(IWebHostBuilder builder) => builder
            .ConfigureServices((context,services) => {
                services.AddPlugin(new ServerEventsFeature());
                //...
            });

    //...
}

Plugins that register any APIs or dependencies will not work if they’re not registered properly.

I tried registering the plugins as you instructed, but when I set a breakpoint in the `public void Configure(IWebHostBuilder builder)` method, I see that the debugger never hits it, and in fact, the plugins aren’t initialized. However, when I use the `public override void Configure(Container container)` method, the breakpoint is hit.

I also tried adding the IHostingStartup interface to AppHost, but the Configure(IWebHostBuilder builder) method is ignored by the breakpoint.

If you’d like, you can also try it on the sample code I sent you. I get the same result there as well.

How can I make the Configure(IWebHostBuilder builder) method active?

Your AppHost needs to inherit IHostingStartup and you need to register your AppHost, e.g. [assembly: HostingStartup(typeof(MyApp.AppHost))] as a hosting startup class.

I did this:

[assembly: HostingStartup(typeof(Euklis.FaRe.Function.Server.ConnectionFramework.AppHost))]

namespace Euklis.FaRe.Function.Server.ConnectionFramework
{
    public class AppHost(string privateKey) : AppHostBase("KlisFaReServer",
                                                   typeof(ServiceNoAuth).Assembly), IHostingStartup

but nothig changed.

I am looking for a way to register AppHost.

Please update your AppHost that’s rewritten to register all plugins within ConfigureServices()

Hi, I did the changes you suggested, but the result doesn’t seem to have changed. I can successfully instantiate a ServerEventClient on the client side, but when I try to call UpdateSubscriber, I still get the following error:

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:

HTTP: POST /event-subscribers/{Id}
HTTP: POST /event-subscribers/{Id}
at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(Span1 candidateState) at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ProcessFinalCandidates(HttpContext httpContext, Span1 candidateState)
at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.Select(HttpContext httpContext, Span`1 candidateState)
at Microsoft.AspNetCore.Routing.Matching.DfaMatcher.MatchAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
Accept: application/json
Host: localhost:12032
Accept-Encoding: gzip, deflate, br
Content-Type: application/json; charset=utf-8
Cookie: ss-pid=0ugM8eytNj3u2gXiYo6N; ss-id=oyalSMxnuMCuArPY3jvq
Content-Length: 70

I tried downloading a template from Create new .NET 8 project - ServiceStack that already includes ServerEvents. I saw that it creates them the same way I do now, but there isn’t a code example included that uses the ServerEventClient. I saw on http://localhost:5000/operations/metadata that the demo app also creates a double routing for UpdateEventSubscriber like this: [ /event-subscribers/{Id}, /event-subscribers/{Id} ]. Assuming the demo app is correct, the only possibility is that I’m calling UpdateSubscriber incorrectly. Could you please write me a few lines of sample code showing how I should setting up a correct subscription?

Thank you very much for your time.

Can you try the latest v10.0.7 from the pre-release NuGet packages

Hi, I updated to pre-release version 10.0.7, but nothing has changed. I still get the same error and still see the double routing.

Is there a workaround I can use to get around this issue? Maybe there’s another way to register a channel for ServerEvents that doesn’t throw an error?

We never had a problem as long as we used the .NET Framework, but now that we’re switching to .NET Core, we’re seeing faster performance and cleaner code, yet we’ve been stuck on this issue for too long.

I’m asking for your help in finding a solution.

Thank you very much for your time and support.

The solution is using the pre-release packages.

Did you clear your NuGet packages cache?

dotnet nuget locals all --clear

Also make sure all your Package Reference 10.* so they use the latest version.

I cleared everything except the NuGet cache. Everything seems to be working now. I’ll keep working on the adaptation, and if I run into any more issues, I’ll get back to you.

For now, thank you so much for your help; using the 10.0.7 libraries solved the problem.

Thanks again.

1 Like