Home

Request: Provide workarounds for caching unserializable types

When writing LINQPad scripts, these days I exclusively use records and immutable types for my data structures. Occasionally I need to cache the results as I work on an implementation so I'm not doing constantly making expensive queries as I test it. The only requirement is that all types to be cached must be marked as serializable.

void Main()
{
    var list = Util.Cache(() => new Record(ImmutableList.Create("foo", "Bar")));
    DumpIt(list);
}

[Serializable]
record Record(ImmutableList<string> Items);

void DumpIt<T>(T obj, [CallerArgumentExpression(nameof(obj))] string? expr = default)
{
    obj.Dump(expr);
}
SerializationException: Don't know how to serialize System.Collections.Immutable.ImmutableList`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
   at LINQPad.Util.Cache[T](Func`1 dataFetcher, String key, Boolean& fromCache)
   at LINQPad.Util.Cache[T](Func`1 dataFetcher, String key)
   at LINQPad.Util.Cache[T](Func`1 dataFetcher)
   at UserQuery.Main(), line 3

https://share.linqpad.net/83mv2om3.linq

The types in System.Collections.Immutable do not mark them serializable so they cannot be cached by linqpad. To work around this, I try to cache the string representations. However this approach is not always feasible without writing a lot of code, especially when working with third party libraries.

Can other mechanisms be added to allow for caching unserializable types?

Maybe a way to register a serialize/deserialize function/interface that would handle the conversions to serializable forms. Maybe fallback to JSON for simple types and include implementations for more commonly used types?

Comments

  • edited January 3

    Something like this and this and implicit ImmutableList<T> conversion operator?

    [Serializable]
    class SerializableImmutableList<T>
    {
        [NonSerialized]
        private ImmutableList<T> _value;
    
        public ImmutableList<T> Value => _value;
    
        [OnSerializing]
        // ...
    
        [OnSerialized]
    
        // ...
        [OnDeserialized]
        // ...
    
        public static implicit operator ImmutableList<T>(SerializableImmutableList<T> v) => Value;
    }
    
  • LINQPad's inbuilt serialization supports arrays and ordinary lists, so the following will work:

    void Main()
    {
        var list = Util.Cache (() => new Record (["foo", "bar"]));
        list.DumpTell();
    }
    
    [Serializable]
    record Record (string[] Items);
    

    Personally, I use arrays for read-only collections when scripting because it's less noisy.

    Supporting immutable collections is tricky because they lack ordinary constructors. Would probably end up having to code each one separately, taking care to ensuring that it doesn't break anything with scripts that target any of the older .NET versions that LINQPad supports.

Sign In or Register to comment.