Porting from .NET Framework 4.8 to .NET Core 8

I hadn’t realized that the problem was using JsonServiceClient instead of JsonApiClient. Using JsonApiClient, as you told me, everything is working correctly now.
Does this mean I also have to migrate my client application from .NET Framework to .NET Core? I don’t think there’s a way to keep my client application in .NET Framework and still connect to the new .NET Core server, is that correct?

Thank you so much for your help; I should have everything I need to move forward now.

If it works you don’t have to. I’ve pushed a pre-release version of ServiceStack v10.0.7 where GetPublicKey is marked with IGet so it can be called via your Get as you were doing before.

A major difference between .NET and .NET Framework is that APIs are only available on the primary HTTP Method, instead of ServiceStack where APIs were typically available from all HTTP Methods.

Thanks for the pre-release. I tried it out. If I use the test client I built yesterday (which was working yesterday), I get the “Method Not Allowed” error. If, on the other hand, I try using the .NET Framework client, I get the error “Underlying connection closed: Unexpected error during a send operation…”.

I’m just letting you know.

In the meantime, I’ll run some more tests and let you know.

Hi again,
Thanks to your advice, I can make unauthenticated calls, but when I need to authenticate, I run into problems.

To continue using the old CustomCredentialsAuthProvider, I’m starting the server like this:

public void Configure(IWebHostBuilder builder) => builder
    .ConfigureServices(services =>
    {
        //Enable Authentication
        services.AddPlugin(new AuthFeature(() => new AuthUserSession(),
            [
                new CustomCredentialsAuthProvider(),
            ]
        ));

I’ve also added manual routing like this:

Routes.Add<AuthenticateService>("/auth", "POST");
Routes.Add<AuthenticateService>("/auth/{provider}", "POST");
Routes.Add<AssignRolesService>("/assignroles", "POST");
Routes.Add<UnAssignRolesService>("/unassignroles", "POST");

But when I try to make the authentication call from the client, I always get the error “Method not allowed”.

For completeness, here is the call that gives me an error on the client side:

              using (JsonApiClient client = new JsonApiClient($"http://{IpAddress}:{PortHttp}/"))
              {
                  AuthResponse = client.Post(new Authenticate
                  {
                      provider = CredentialsAuthProvider.Name,
                      UserName = _gp.CurrUser.Base64Encode(),
                      Password = _gp.Password.Base64Encode(),
                      RememberMe = true,
                  });

What am I doing wrong?

Thank you.

PS
I’ve reverted to version 10.0.6 of the libraries

Don’t add duplicate routes, the AuthFeature plugin has it’s own routes.

As I said previously you shouldn’t using with HttpClient, the lifetime should be managed from the IOC which you can get with dependency injection. Otherwise just use a singleton instance, instead of creating/disposing a HttpClient each time.

Explain the type of App that’s running the client and the server you’re trying to connect to. E.g. what version of .NET, ServiceStack and if they use Endpoint Routing.

In the server you’re trying to connect to (i.e. that has AuthFeature plugin) are you able to authenticate with the UI form? https://localhost:5001/ui

I am using .NET Core 8 and ServiceStack version 10.0.6, and I am porting the application from .NET Framework 4.8 with ServiceStack version 8.0.0.

I’m now trying to see if I can authenticate via the UI; in the meantime, I’m trying to use the application without client authentication, and in the responses I’m losing class inheritance. Let me explain. In a response I have an array of class A; from the server I send an array of B which inherits from A, but I receive an array of A. In the previous .NET Framework version, I didn’t experience this behaviour.
Am I missing something?

It looks like you’re connecting App A which is trying to connect to App B which has the AuthFeature plugin. What is the architecture of App A and the architecture of App B.

This is getting frustrating, I have no idea what this is asking. Instead of this unknown word dump, provide the actual code and the result of the code in both your old App and your New App. I should be able to run the code to verify the behavior you’re referring to as I’m not going to be able identify any issue with code I can’t see or test.

Sure.
This is the response:

[Serializable]
public class GetIdentitiesResponse : ResponseModel
{
    #region [ Public property ]
    public IdentityModelLite[] Identities { get; set; }
    public int TotalItems { get; set; }
    #endregion

    #region [ ctor ]
    public GetIdentitiesResponse(int totalItems, IdentityModelLite[] identities) : base(identities == null ? false : true)
    {
        Identities = identities;
        TotalItems = totalItems;
    }

    public GetIdentitiesResponse(Exception ex, int msgCode, params string[] param) : base(ex, msgCode, param)
    {

    }
    #endregion
}

This is the classe IdentityModelLite:

[Serializable]
[XmlInclude(typeof(IdentityHumanModel))]
public class IdentityModelLite : FaReElement, ITenantID
{
    #region [ Public property ]
    public long IdentityID { get; set; }
    public string Guid { get; set; }
  ...
}

This is the class IdentityHumanModel which inherits from IdentityModelLite:

 [Serializable]
 public class IdentityHumanModel : IdentityModelLite
 {
     #region [ Public properties ]
     public new string Name { get; set; }
     public new string Surname { get; set; }
  ...
 }

This is what I do on server-side on a request:

IdentityHumanModel[] identities = idb.GetIdentities(request.IdentityFilter);
int identityCount = identities.Length;
return new GetIdentitiesResponse(identityCount, identities);

On my old client, I would receive the response and, by casting the Identities array to IdentityHumanModel, I would get an array of IdentityHumanModel objects.

On the new client, I receive the response but cannot cast the Identities array to IdentityHumanModel because it has been ‘flattened’ to IdentityModelLite.

Thank you!

DTOs should have a parameter-less constructor and it shouldn’t have any implementation as they should just be Data Transfer Objects. The [Serializable] attribute also doesn’t do anything.

The new .NET Apps use System.Text JSON APIs so they would need to be serializable with System.Text JSON serializer. You can also control which DTOs are serialized with which serailizer. E.g. using [SystemJson(UseSystemJson.Never)] on the Request DTO would use the ServiceStack.Text JSON Serilaizer instead of .NET’s System.Text JSON.

I’ve tried various combinations, and the only one that doesn’t throw an error is when I use the [SystemJson(UseSystemJson.Never)] attribute on the GetIdentitiesResponse class like this:

[SystemJson(UseSystemJson.Never)]
public class GetIdentitiesResponse : ResponseModel
{
    #region [ Public property ]
    public IdentityModelLite[]? Identities { get; set; }
    public int TotalItems { get; set; }
    #endregion

    #region [ ctor ]
    public GetIdentitiesResponse() { }
    public GetIdentitiesResponse(int totalItems, IdentityModelLite[]? identities) : base(identities != null)
    {
        Identities = identities;
        TotalItems = totalItems;
    }
    public GetIdentitiesResponse(Exception ex, int msgCode, params string[] param) : base(ex, msgCode, param) { }
    #endregion
}

But I still can’t cast Identities to the original subclass.

On the plus side, I’ve realised where I was going wrong and can now authenticate correctly. We’re slowly getting there. I just need to figure out what I’m doing wrong with the casting and the serialisation/deserialisation of the response.

Do you have any suggestions for me? Would any specific code be helpful?

Thank you so much!

The [SystemJson(UseSystemJson.Never)] needs to be added to the Request DTO.

Also it’s still not clear what the structure of your client App is, I’m assuming your Server App is .NET 10, but what is your Client App using?

My Server App is .NET Core 8 and my Client App is .NET Core 8 too.
And I am using ServiceStack version 10.0.6 for both.

It’s not clear you’ve added [SystemJson(UseSystemJson.Never)] on the Request DTO which is what you need to do to serialize with ServiceStack JSON.

You can also force clients to use ServiceStack JSON:

ClientConfig.UseSystemJson = UseSystemJson.Never;

And you can force Server to always serialize with ServiceStack JSON with:

app.UseServiceStack(new AppHost(), options => {
    // Don't use System.Text.Json for APIs
    options.MapEndpoints(useSystemJson:UseSystemJson.Never);
});

Otherwise you would need to make your APIs serializable with System.Text JSON.

Hi again,
I’ve tried to simplify the process as much as possible. I started with the sample MyApp application available on the website and made some changes.

The Hello.cs file in MyApp.ServiceModel now looks like this:

using ServiceStack;

namespace MyApp.ServiceModel;

[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
    public required string Name { get; set; }
}

public class HelloResponse
{
    public required string Result { get; set; }
    public required Father Father { get; set; }
}


public class Father
{
    public required string FatherName { get; set; }
    public Father() { }
}
public class Son : Father
{
    public required string SonName { get; set; }
    public Son() { }
}

And the MyServices.cs file in MyApp.ServiceInterface now looks like this:

using ServiceStack;
using MyApp.ServiceModel;

namespace MyApp.ServiceInterface;

public class MyServices : Service
{
    public HelloResponse Any(Hello request)
    {
        return new HelloResponse
        {
            Result = $"Hello, {request.Name}!",
            Father = new Son { FatherName = "Father", SonName = "Son" }
        };
    }
}

I added a project called MyApp.Client. This is the Program.cs:

using MyApp.ServiceModel;
using ServiceStack;

JsonApiClient client = new($"http://localhost:5001/");
var response = client.Post(new Hello { Name = "World" });

Console.WriteLine($"Result: {response.Result}");
Console.WriteLine($"Father type: {response.Father.GetType().Name}");
Console.ReadLine();

The result I get in the console is as follows:

Result: Hello, World!
Father type: Father

Obviously, I expected ‘Father type’ to be ‘Son’, but instead I get ‘Father’. What am I doing wrong? Is it no longer possible to get the Son class with the .NET Core version?

PS
If it’s helpful, I can send you the entire test solution

If you’re using ServiceStack.Text JSON base classes need to be abstract.

If you’re using System.Text JSON you need to use [JsonDerivedType] attributes on the base class listing all the derived types:

Hi,

Thanks to your valuable advice, I’ve finally managed to serialise and deserialise correctly.

Now I have another problem: callbacks. I think I’ve configured the client correctly, whilst on the server I’ve added the ServerEventsFeature in plugin like this:

 public override void Configure(Container container)
 {
     base.Configure(container);

     // Other plugins and configurations
     // ...

     container.AddPlugin(new ServerEventsFeature());
 }

and I’ve initialised the ServerEvents property like this:

    [Authenticate]
    public partial class Services : Service
    {
        public IServerEvents ServerEvents { get; set; } = default!;

        // ...
    }

but it’s still null.

Am I missing something?

Many thanks.

In addition to registering plugins in ConfigureServices you need to use constructor injection when using ASP.NET IOC:

class Services(IServerEvents serverEvents) : Service
{
 //...
}