0

I need to store a NodaTime LocalDateTime in an Akavache cache.

I've created a simple app which takes the following class and stores/retrieves it in/from an Akavache cache:

public class TestModel
{
        public string Name { get; set; }
        public LocalDateTime StartDateTimeLocal {get; set;}
        public DateTime StartDateTimeUtc {get;set;}
}

When this is stored in and retrieved from the cache, the StartDateTimeLocal property hasn't been populated.

It seems that Akavache isn't aware of how to serialise/deserialize a LocalDateTime.

Is it possible to register types with Akavache or supply a custom serialisation for unknown types?

Console application to demonstrate it:

using Akavache;
using NodaTime;
using System;
using System.Reactive.Linq;

namespace AkavacheNodaTimeCore
{
    class Program
    {
        static TestModel BeforeModel;
        static TestModel AfterModel;
        static void Main(string[] args)
        {
            // Note that we're using Akavache 6.0.27, to match the version we're using in our live system.
            BlobCache.ApplicationName = "AkavacheNodaTimeCore";
            BlobCache.EnsureInitialized();

            BeforeModel = new TestModel()
            {
                StartLocalDateTime = LocalDateTime.FromDateTime(DateTime.Now),
                StartDateTime = DateTime.UtcNow,
            };

            Console.WriteLine($"Before:LocalDateTime='{BeforeModel.StartLocalDateTime}' DateTime='{BeforeModel.StartDateTime}'");

            CycleTheModels();

            Console.WriteLine($"After: LocalDateTime='{AfterModel.StartLocalDateTime}' DateTime='{AfterModel.StartDateTime}'");
            Console.WriteLine("Note that Akavache retrieves DateTimes as DateTimeKind.Local, so DateTime before and after above will differ.");

            Console.WriteLine("Press any key to continue.");
            var y = Console.ReadKey();

        }
        /// <summary>
        /// Puts a model into Akavache and retrieves a new one so we can compare.
        /// </summary>
        static async void CycleTheModels()
        {
            await BlobCache.InMemory.Invalidate("model");
            await BlobCache.InMemory.InsertObject("model", BeforeModel);

            AfterModel = await BlobCache.InMemory.GetObject<TestModel>("model");
        }
    }
}

TestModel class:

using NodaTime;
using System;

namespace AkavacheNodaTimeCore
{
    public class TestModel
    {
        public string Name { get; set; }
        public LocalDateTime StartLocalDateTime { get; set; }
        public DateTime StartDateTime {get;set;}

    }
}

I have added a Git repo with the above in a console application which demonstrates the problem.

James Lavery
  • 920
  • 4
  • 25
  • Would it be possible for you to write a [mcve] that we could use to explore this? I haven't used Akavache at all myself, but I'd be interested in trying to fix this if possible. The web site suggests that it can use Json.NET - so it's possible that adding the serializer settings for NodaTime in Json.NET would sort this. But I'd rather not start absolutely from scratch - that's where a complete example (with an in-memory cache) would really help. – Jon Skeet Jun 13 '19 at 06:06
  • I'll try to put an example together this morning. – James Lavery Jun 13 '19 at 07:15
  • Repo added which demonstrates the problem (see main post). – James Lavery Jun 13 '19 at 08:44
  • I think it would be good to put that code in the question itself - it's small enough. (Keeping the link is useful in order to just git clone too, mind you.) – Jon Skeet Jun 13 '19 at 09:14
  • Good idea - done! – James Lavery Jun 13 '19 at 09:19

1 Answers1

2

You need to configure the JsonSerializerSettings that Akavache uses with Json.NET. You'll need a reference to NodaTime.Serialization.JsonNet, at which point you can create a serializer settings instance, configure it for Noda Time, then add that as a dependency in Splat (which Akavache uses). I haven't used Splat before, so it's possible that this isn't the right way of doing it, but it works with your example:

using Newtonsoft.Json;
using NodaTime.Serialization.JsonNet;
using Splat;

...

// This should be before any of your other code.
var settings = new JsonSerializerSettings();
settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
Locator.CurrentMutable.RegisterConstant(settings, typeof(JsonSerializerSettings));

It may be worth filing in issue in the Akavache repo to request more documentation for customization of serialization settings - the above works, but was guesswork and a little bit of source code investigation.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks - that's exactly what I was after. I thought the solution was something like this, but wasn't sure how to hook it together. I will post an Akavache issue to ask for clearer serialization documentation. – James Lavery Jun 13 '19 at 09:33