Search code examples
c#asp.net-mvcserializationstackexchange.redisazure-redis-cache

An exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll but was not handled in user code


I have the following objects:

 [Serializable]
    public class Module
    {
        [Key]
        public int Id { get; set; }
        public string ModuleName { get; set; }
        public string FontAwesomeClass { get; set; }
    }

   [Serializable]
    public class ModulosPorUsuario
    {
        [Key]
        public int Id { get; set; }
        public string Email { get; set; }
        public virtual ICollection<Module> Modules{ get; set; }
    }

And I am using the method to cache in a specific key for each user its assigned modules.

 /// <summary>
        /// Gets the modules activated for a user
        /// </summary>
        /// <param name="email">Email address of the user</param>
        /// <returns>List of modules for the selected user</returns>
        public static List<Models.ModulosPorUsuario> GetModulesForUser(string identityname)
        {
            /// It needs to be cached for every user because every user can have different modules enabled.
            var cachekeyname = "ApplicationModulesPerUser|" + identityname;

            IDatabase cache = CacheConnectionHelper.Connection.GetDatabase();
            List<Models.ModulosPorUsuario> listOfModulesPerUser = new List<Models.ModulosPorUsuario>();
            listOfModulesPerUser = (List<Models.ModulosPorUsuario>)cache.Get(cachekeyname);
            if (listOfModulesPerUser == null)
            {
                listOfModulesPerUser = dbApp.ModulosPorUsuario.Where(p => p.Email == identityname).ToList();
                cache.Set(cachekeyname, listOfModulesPerUser, TimeSpan.FromMinutes(SettingsHelper.CacheModuleNames));
                return listOfModulesPerUser;
            }
            else
            {
                return listOfModulesPerUser;
            }
        }

The first time its working, of course its returning the data from DB. But the second time, (there is an object on cache), but the list of Modules is null:

http://screencast.com/t/OFB1vkvno

I also have this helper classes from stackexchange redis cache:

public static class SampleStackExchangeRedisExtensions
    {
        public static T Get<T>(this IDatabase cache, string key)
        {
            return Deserialize<T>(cache.StringGet(key));
        }

        public static object Get(this IDatabase cache, string key)
        {
            return Deserialize<object>(cache.StringGet(key));
        }

        public static void Set(this IDatabase cache, string key, object value, TimeSpan expiration)
        {
            cache.StringSet(key, Serialize(value), expiration);
        }

        static byte[] Serialize(object o)
        {
            if (o == null)
            {
                return null;
            }
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            using (MemoryStream memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, o);
                byte[] objectDataAsStream = memoryStream.ToArray();
                return objectDataAsStream;
            }
        }

        static T Deserialize<T>(byte[] stream)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            if (stream == null)
                return default(T);

            using (MemoryStream memoryStream = new MemoryStream(stream))
            {
                T result = (T)binaryFormatter.Deserialize(memoryStream);
                return result;
            }
        }
    }

I know there is data related because of the SEED:

 #region Seed Modules
            var module1 = new Module() { Id = 1, ModuleName = "Contabilidad", FontAwesomeClass = "fa-ambulance" };
            var module2 = new Module() { Id = 2, ModuleName = "Recursos Humanos", FontAwesomeClass = "fa-heartbeat" };
            var module3 = new Module() { Id = 3, ModuleName = "Inventario", FontAwesomeClass = "fa-anchor" };
            var module4 = new Module() { Id = 4, ModuleName = "Produccion", FontAwesomeClass = "fa-binoculars" };
            var module5 = new Module() { Id = 5, ModuleName = "Produccion", FontAwesomeClass = "fa-binoculars" };
            var module6 = new Module() { Id = 6, ModuleName = "Ventas", FontAwesomeClass = "fa-coffee" };
            var module7 = new Module() { Id = 7, ModuleName = "Compras", FontAwesomeClass = "fa-calendar-o" };
            var module8 = new Module() { Id = 8, ModuleName = "Cotizaciones", FontAwesomeClass = "fa-building" };
            context.Modulos.Add(module1);
            context.Modulos.Add(module2);
            context.Modulos.Add(module3);
            context.Modulos.Add(module4);
            context.Modulos.Add(module5);
            context.Modulos.Add(module6);
            context.Modulos.Add(module7);
            context.Modulos.Add(module8);

            context.SaveChanges();
            #endregion

  #region Seed ModulosPor Usuario
            context.ModulosPorUsuario.Add(new ModulosPorUsuario()
            {
                Id=1,
                Email = "[email protected]",
                Modules = new List<Module>() { module1, module2 }
            });

            context.ModulosPorUsuario.Add(new ModulosPorUsuario()
            {
                Id=2,
                Email = "[email protected]",
                Modules = new List<Module>() { module3, module5 }
            });

            context.ModulosPorUsuario.Add(new ModulosPorUsuario()
            {
                Id=3,
                Email = "[email protected]",
                Modules = new List<Module>() { module4, module6 }
            });

            context.ModulosPorUsuario.Add(new ModulosPorUsuario()
            {
                Id=4,
                Email = "[email protected]",
                Modules = new List<Module>() { module7, module7 }
            });


            context.SaveChanges();
            #endregion

Update1:

This is the error I get when I try to get the cached value the 2nd time:

An exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll but was not handled in user code

Additional information: Unable to find assembly 'EntityFrameworkDynamicProxies-Inspinia_MVC5, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

stack trace

   at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
   at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)

Solution

  • I believe you need to create an empty constructor for this type (see code snippet below) and instantiate your collection. The Serialization engine will not do it for you and it will expect that that collection is instantiated.

    Further, since your collection property is an interface and not a class, the Serializer is probably getting confused when trying to initialize this property and add items.

    public class ModulosPorUsuario
    {
        // added this constructor
        public ModulosPorUsuario()
        {
            this.Modules = new List<Module>();
        }        
    
        [Key]
        public int Id { get; set; }
        public string Email { get; set; }
        public virtual ICollection<Module> Modules { get; set; }
    }
    

    Though you are initializing it before you serialize, the Serializer is instantiating objects on your behalf when you deserialize.

    Happy Coding.