Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there anyway to serilize linq object for Memcached?

I'm just start switching to memcached and currently on testing with memcached.

I'm having 2 object, I created an object and put [Serializable] on it (for instance, let call this Object1), the other object is created using Linq DBML (Object2)..

I tried to memcached List<Object1>, it work just fine, like charm, everything here is cache and loaded properly.

But then, i move on to the Linq object, now i try to add to memcached List<Object2> this does not work, it did not add to memcached at all. no key was added

I move on and change the Serialization Mode to Unidirectional, do the add again, still no hope.

Is there anyway to make this work?

Here is the simple test I just wrote, using MemcachedProvider from codeplex to demonstrate:

public ActionResult Test()
{
    var returnObj = DistCache.Get<List<Post>>("testKey");
    if (returnObj == null)
    {
        DataContext _db = new DataContext();
        returnObj = _db.Posts.ToList();
        DistCache.Add("testKey", returnObj, new TimeSpan(29, 0, 0, 0));
        _db.Dispose();
    }

    return Content(returnObj.First().TITLE);
}

this is from Memcached, no STORE was called:

> NOT FOUND _x_testKey
>532 END
<528 get _x_testKey
> NOT FOUND _x_testKey
>528 END
<516 get _x_testKey
> NOT FOUND _x_testKey
>516 END

And in my SQL profiler, it called 3 query for 3 test time => Proved that the object called back from Memcached is null, then it query.

like image 975
DucDigital Avatar asked Nov 05 '22 17:11

DucDigital


1 Answers

It looks like the default implementation (DefaultTranscoder) is to use BinaryFormatter; the "unidirectional" stuff is an instruction to a different serializer (DataContractSerializer), and doesn't add [Serializable].

(Note: I've added a memo to myself to try to write a protobuf-net transcoder for memcached; that would be cool and would fix most of this for free)

I haven't tested, but a few options present themselves:

  1. write a different transcoder implementation that detects [DataContract] and uses DataContractSerializer, and hook this transcoder
  2. add [Serializable] to your types via a partial class (I'm not convinced this will work due to the LINQ field types not being serializable)
  3. add an ISerializable implementation in a partial class that uses DataContractSerializer
  4. like 3, but using protobuf-net, which a: works with "unidirectional", and b: is faster and smaller than DataContractSerializer
  5. write a serializable DTO and map your types to that

The last is simple but may add more work.

I'd be tempted to to look at the 3rd option first, as the 1st involves rebuilding the provider; the 4th option would also definitely be on my list of things to test.


I struggled with 3, due to the DCS returning a different object during deserialization; I switched to protobuf-net instead, so here's a version that shows adding a partial class to your existing [DataContract] type that makes it work with BinaryFormatter. Actually, I suspect (with evidence) this will also make it much efficient (than raw [Serializable]), too:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using ProtoBuf;

/* DBML generated */
namespace My.Object.Model
{
    [DataContract]
    public partial class MyType
    {
        [DataMember(Order = 1)]
        public int Id { get; set; }

        [DataMember(Order = 2)]
        public string Name { get; set; }
    }
}
/* Your extra class file */
namespace My.Object.Model
{
    // this adds **extra** code into the existing MyType
    [Serializable]   
    public partial class MyType : ISerializable {
        public MyType() {}
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
            Serializer.Serialize(info, this);
        }
        protected MyType(SerializationInfo info, StreamingContext context) {
            Serializer.Merge(info, this);
        }
    }
}
/* quick test via BinaryFormatter */
namespace My.App
{
    using My.Object.Model;
    static class Program
    {
        static void Main()
        {
            BinaryFormatter bf = new BinaryFormatter();
            MyType obj = new MyType { Id = 123, Name = "abc" }, clone;
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, obj);
                ms.Position = 0;
                clone = (MyType)bf.Deserialize(ms);
            }
            Console.WriteLine(clone.Id);
            Console.WriteLine(clone.Name);
        }
    }
}
like image 108
Marc Gravell Avatar answered Nov 17 '22 07:11

Marc Gravell