OrmLite JSON serialization reverts to JSV?

I’m serializing complex objects as JSON in my Sqlite3 db, using this in Startup:

SqliteDialect.Provider.StringSerializer = new JsonStringSerializer();

However, running the same code, occationally it still gets serialized as JSV – same places as previously (and after) it would serialize as JSON. So it’s like sometimes it just decides to do JSV.

My program is too large for a working sample, but is there any chance ServiceStack “forgets” the serializer setting somehow? Is there a better place to set it? Or hint to what I should investigate. I’m running in Visual Studio via debugger, could hot code replacement trigger this (of my own code)?

I don’t see how it wouldn’t use the configured Dialect Provider Serializer unless it’s not using the Dialect Provider. You can try using the BeforeExecFilter to intercept DB commands before they’re executed like printing the generated SQL output which should identify queries with the issue:

OrmLiteUtils.PrintSql();

We would need a stand-alone repro to be able to investigate.

We’re having this problem again, and it’s hard to track, especially since it happens without any warning.

So to recap:

I’ve got a class which I serialize to our DB. Since 2021/2022 these lines are in my code to force JSON always:

        SqlServerDialect.Provider.StringSerializer = new JsonStringSerializer();
        SqlServer2017Dialect.Provider.StringSerializer = new JsonStringSerializer();
        SqliteDialect.Provider.StringSerializer = new JsonStringSerializer();

        // followed by this:
        ServiceStack.JS.Configure(); // this fixes a lot, don't remember why

Dialect provider is set explicitly to SqlServer2017Dialect, except for unit testing where Sqlite is used:

var dialectProvider = connectionString.StartsWith("Server=") ? SqlServer2017Dialect.Provider : SqliteDialect.Provider;
var dbFactory = new OrmLiteConnectionFactory(connectionString, dialectProvider);
services.AddSingleton<IDbConnectionFactory>(dbFactory);


At one point we had to add [Serializable] because we were getting deserialization error, but this was a very explicit error that threw exceptions. However it was around the same time (november) when we got the errors the first time (timeline later).

[Serializable]  // to be able to serialize it in GlobalState, ref. https://docs.servicestack.net/releases/v6_06#msvr-76883
public class ActionInstanceReference
{
    // some public properties
}

So what happens is that we have a class which we save to the db.
It is basically

public class ProcessInstance
{
    [AutoIncrement]
    public int Id { get; set; }

   public Dictionary<string, object> GlobalState { get; set; } = new Dictionary<string, object>();

The GlobalState is where things sometimes goes wrong, it will only serialize the first level to JSON, then it’ll revert to JSV for the deeper nested objects.

Incorrect:

{
  "employee": "{fullName:xxxx yyyy,firstName:xxxx,lastName:yyyyy,...}",
  "endDate": "2025-01-31",
  "__REFS__": "{confirmEmailMgr:{__type:MasterProcessServer.ServiceModel.Interfaces.ActionInstanceReference, MasterProcessServer.ServiceModel,type:Message,prettyPrint:Message to MDK 169},confirmEmailEmp:{type:Message,prettyPrint:Message to MDK 965}}"
}

Correct (taken from another row in the DB):

{
  "employee": {
    "fullName": "xxxx yyyy",
    "firstName": "xxxx",
    "lastName": "yyyy",
    ...
  },
  "endDate": "2024-11-30",
  "__REFS__": {
    "confirmEmailMgr": {
      "__type": "MasterProcessServer.ServiceModel.Interfaces.ActionInstanceReference, MasterProcessServer.ServiceModel",
      "type": "Message",
      "prettyPrint": "Message to MDK 1597"
    },
    "confirmEmailEmp": {
      "type": "Message",
      "prettyPrint": "Message to MDK 580"
    },
    ....
}

This has happened intermittently. From our logs and our backups, we can see that the GlobalState object was correctly JSON-only, then turned bad at the same time.

237	2024-10-28 14:34:56.023 BAD
240	2024-10-29 15:23:05.633 GOOD
236	2024-10-31 10:18:42.287 GOOD
243	2024-11-27 14:12:45.287 GOOD
238	2024-12-16 11:00:16.037 GOOD
239	2024-12-23 18:22:03.313 BAD
241	2024-12-23 18:22:03.653 BAD
242	2024-12-23 18:22:03.900 BAD
246 2024-12-27 08:00:12.860	GOOD

From the dates, we can assume that no one was working at the time.

I could find this in my logs:

2024-12-23 18:22:03.191 +01:00 [ERR] Activity 8557 of process instance 239 failed w. exception The best overloaded method match for 'MasterProcessServer.ServiceModel.Utils.ExpandoObjectHelper.ToExpando(System.Collections.Generic.IDictionary<string,object>)' has some invalid arguments
2024-12-23 18:22:03.970 +01:00 [INF] Failed to write error to response: {0}
ServiceStack.ConfigurationErrorsException: ServiceStack: AppHost does not exist or has not been initialized. Make sure you have created an AppHost and started it with 'new AppHost().Init();'  in your Global.asax Application_Start() or alternative Application StartUp
   at ServiceStack.HostContext.AssertAppHost() in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/HostContext.cs:line 34
   at ServiceStack.HostContext.RaiseAndHandleException(IRequest httpReq, IResponse httpRes, String operationName, Exception ex) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/HostContext.cs:line 261
   at ServiceStack.Host.Handlers.HttpAsyncTaskHandler.HandleException(IRequest httpReq, IResponse httpRes, String operationName, Exception ex) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/Host/Handlers/HttpAsyncTaskHandler.cs:line 180

So once we got the error about “ToExpando” then I traced back and finally arrived at a DB ORMLite save call. It was guarded by this:

// just a bug hunt:
if (SqlServerDialect.Provider.StringSerializer.GetType() != typeof(JsonStringSerializer)
|| SqliteDialect.Provider.StringSerializer.GetType() != typeof(JsonStringSerializer)) {
    throw new MasterServiceException("Wrong db serializer! ");
}

However I note that it’s missing SqlServer2017Dialect and only has SqlServerDialect so of course there was nothing in the logs.

Question: Is there any case where OrmLite would revert to JSV ?

  • Like if somehow my Dictionary<string,object> (where object can be another dict of same type) got messed up somehow into another class or something?
  • Or could it be something with that last line in the log about the AppHost. Because after that I have no more log lines for the failed processes (so probably that’s when they failed). Perhaps some “default apphost” started after that? One without the JSON serialization options set?

I’m not going to be able to identify what the issue is without being able to reproduce the behavior but the most likely explanation from your “AppHost does not exist” Exception in your Error log and change in Dictionary<string,object> behavior is that when your App is being shutdown it will dispose the AppHost which will in addition to disposing all registered IDisposable dependencies will revert the JS.Configure() behavior handling of Dictionary<string,object> from JS Utils using #Script and ServiceStack.Common back to ServiceStack.Text behavior.

In which case you should probably guard against running any logic whilst your AppHost is being shutdown by adding something in your AppHost that checks the protected isDisposed and throws an Exception:

public void ThrowIfDisposed()
{
    if (isDisposed)
        throw new ObjectDisposedException(GetType().Name);
}

Or just create a public IsDisposed property that your App can check to avoid running any logic.

Thanks it may look like this is the case. When we look at the logs, it might be that some app recycling has started, and at some point our code starts to fail. First we’re loosing the JSON config that forces ISO dates instead of timestamp, then shortly after we loose JSON all together and get the default JSV.

One thing I’m unsure about is where to call the ThrowIfDisposed (or check property), assuming this is added to our AppHost. If the recycling can happen at any time… we can’t really check for disposed everywhere. Or does it need to be on the top of every service or what?

Can you elaborate a bit?

Call it before logic that relies on Dictionary<string,object> behavior.

You could check and call it from a Global Request Filter to prevent all API Requests from executing.

As documented in the Order of Operations the closest check before your Service is executed would be in your Services OnBeforeExecute method, e.g:

public class MyServices : Service
{
    public override void OnBeforeExecute(object requestDto)
    {
        ServiceStackHost.ThrowIfDisposed();
    }
}

Which would apply to all APIs implemented in this Service class or any sub classes if this is a base class for other Services. To implement it for all Services you can create a Custom Service Runner.

Note: I’ve added a public AppHost IsDisposed instance property and a static ServiceStackHost.ThrowIfDisposed() in the latest v8.5.3+ that’s now available in pre-release packages.

Thanks for including it in new releases.

Today we’ve corrected incorrectly serialized data (i.e. not JSON w. ISO) and discovered that it’s not only the Dictionary<string,object> but also other objects, such as this fully typed one, where dates and capitalization settings reverted (lost the JSConfig) – and it usually works perfectly:

public class ActivityInstance
{
    [AutoIncrement]
    public int Id { get; set; }
    public List<StatusUpdate> StatusHistory { get; set; } // this one lost ISO date format
}

public class StatusUpdate
{
    public DateTime EnteredStatus { get; set; }
    public DateTime? ExitedStatus { get; set; }
    public enActivityStatus Status { get; set; }
    public string Message { get; set; }
}

public enum enActivityStatus
{   
    [EnumMember(Value = "INACTIVE")]
    INACTIVE,
    [EnumMember(Value = "READY")]
    READY,
    ...
}

We will investigate more, but it seems very impractical to check “if a disposal/recycling is underway” everywhere we read/write to DB. The global service request filter sounds more practical, but will it catch all?

From our logs we have observed that within a single API call, the trouble can happen (so disposal check PASS and then disposal happens…):

  1. The RunLoop is started from an API call, and the first 151, 200, 209 goes well. So a disposal was not ongoing at the time, hence I don’t think a check/ReqFilter would catch it.
  2. Then 212 was partly affected, as we lost our ISO time and CamelCase option for the JSON serializer (but still no JSV at this time)
  3. Then 237, 239, 241, 242 went all the way to JSV.
  4. Message about AppHost trouble and restart complete
2024-12-23 18:22:02.698 +01:00 [INF] RunLoop found processes: 151, 200, 209, 212, 237, 239, 241, 242
2024-12-23 18:22:02.698 +01:00 [INF] Running process 151
2024-12-23 18:22:02.766 +01:00 [INF] Refreshed case 3101573 w. thirdparty id ...
2024-12-23 18:22:02.806 +01:00 [INF] Running process 200
2024-12-23 18:22:02.874 +01:00 [INF] Refreshed case 3071010 w. thirdparty id ...
2024-12-23 18:22:02.909 +01:00 [INF] Running process 209
2024-12-23 18:22:02.978 +01:00 [INF] Running process 212
2024-12-23 18:22:03.046 +01:00 [ERR] <<<unrelated error here>>>
2024-12-23 18:22:03.084 +01:00 [INF] Running process 237
2024-12-23 18:22:03.156 +01:00 [INF] Running process 239
2024-12-23 18:22:03.191 +01:00 [ERR] <<<same unrelated error here but for 239>>>
2024-12-23 18:22:03.201 +01:00 [ERR] <<<same unrelated error here but for 239>>>
2024-12-23 18:22:03.211 +01:00 [ERR] <<<same unrelated error here but for 239>>>
2024-12-23 18:22:03.388 +01:00 [INF] Running process 241 <-- no errors but still serialization bug
2024-12-23 18:22:03.701 +01:00 [INF] Running process 242 <-- no errors but still serialization bug
2024-12-23 18:22:03.960 +01:00 [INF] RunLoop DONE <-- API call done
2024-12-23 18:22:03.970 +01:00 [INF] Failed to write error to response: {0}
ServiceStack.ConfigurationErrorsException: ServiceStack: AppHost does not exist or has not been initialized. Make sure you have created an AppHost and started it with 'new AppHost().Init();'  in your Global.asax Application_Start() or alternative Application StartUp
   at ServiceStack.HostContext.AssertAppHost() in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/HostContext.cs:line 34
   at ServiceStack.HostContext.RaiseAndHandleException(IRequest httpReq, IResponse httpRes, String operationName, Exception ex) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/HostContext.cs:line 261
   at ServiceStack.Host.Handlers.HttpAsyncTaskHandler.HandleException(IRequest httpReq, IResponse httpRes, String operationName, Exception ex) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/Host/Handlers/HttpAsyncTaskHandler.cs:line 180
2024-12-23 18:23:04.959 +01:00 [WRN] Multiple ValidationFeature Plugins detected. Removing default ValidationFeature plugin...
2024-12-23 18:23:05.281 +01:00 [INF] Initializing Application MasterProcessServer took 834.9024ms. No errors detected.

Last in the log we see the reload/restart was done. Since it took 834 ms, it should have started around 18:23:04.447, but I don’t know if we can trust the timestamps, perhaps it started earlier, because we got the error about the AppHost already at 18:22:03.970.

Point is that it seems we must check right before reading/writing via OrmLite, and that’s very many places, and quickly gets ugly. Is there an option to use BeforeExecFilter and put the check in there?

Also I don’t quite understand “…will revert the JS.Configure() behavior handling of Dictionary<string,object> from JS Utils using #Script and ServiceStack.Common back to ServiceStack.Text behavior.” I guess since the config is global (static) then it’s shared somehow.

Some useful info perhaps? I’ve noticed that our Configure.Db.cs has this Priority attribute to make sure the DB connections are registered early enough, since we’re dependent on them for different Repository classes later (see below Startup.cs).

[Priority(-1)]      // run before AppHost's Startup()
public class ConfigureDb : IConfigureServices, IConfigureAppHost

// to serialize complex objects as JSON instead of JSV
SqlServerDialect.Provider.StringSerializer = new JsonStringSerializer();
SqlServer2017Dialect.Provider.StringSerializer = new JsonStringSerializer();
SqliteDialect.Provider.StringSerializer = new JsonStringSerializer();

ServiceStack.JS.Configure(); // GLOBAL: Configure ServiceStack.Text JSON Serializer to use Templates JS parsing

Then in Startup.cs:

     JsConfig.Init(new ServiceStack.Text.Config
     {
         DateHandler = DateHandler.ISO8601,
         AlwaysUseUtc = false,
         TextCase = TextCase.CamelCase,
         ExcludeDefaultValues = false,       
         IncludeNullValues = false
     });

     container.RegisterAutoWired<FormRepository>(); // ex of class that needs the DB conn from IOC

Perhaps if the JsConfig.Init was moved into Configure.Db.cs close to ServiceStack.JS.Configure(); ? I don’t really know when the JSUtils are recreated/changed.

BTW we could continue on e-mail/meeting etc. (as part of our subscription), then it would be easier to share code that I can’t do publicly – don’t know how much value this is for others anyway.

Is there an option to use BeforeExecFilter and put the check in there?

Yes there is, but there’s only one global BeforeExecFilter handler:

OrmLiteConfig.BeforeExecFilter = dbCmd => {
    ThrowIfDisposed();
};

Also I don’t quite understand “…will revert the JS.Configure() behavior handling of

Here’s the implementation of JS.Configure():

I.e. it takes over handling of ObjectDeserialization with the ServiceStack.Common JS Utils implementation. When the AppHost disposes it reverts the behavior back to ServiceStack.Text default implementation.

public static void UnConfigure() => JsonTypeSerializer.Instance.ObjectDeserializer = null;

One thing you could try is reattach the behavior after the AppHost is disposed:

protected override void Dispose(bool disposing)
{
    base.Dispose(disposing);
    JS.Configure();
}
1 Like