Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure caching and entity framework deserialization issue

I have a web project deployed in azure using colocated caching. I have 2 instances of this web role.

I am using Entity framework 5 and upon fetching some entities from the db, I cache them using colocated caching.

My entities are defined in class library called Drt.BusinessLayer.Entities

However when I visit my web app, I get the error:

The deserializer cannot load the type to deserialize because type 'System.Data.Entity.DynamicProxies.Country_4C17F5A60A033813EC420C752F1026C02FA5FC07D491A3190ED09E0B7509DD85' could not be found in assembly 'EntityFrameworkDynamicProxies-Drt.BusinessLayer.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Check that the type being serialized has the same contract as the type being deserialized and the same assembly is used.

Also sometimes I get this too:

Assembly 'EntityFrameworkDynamicProxies-Drt.BusinessLayer.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not found.

It appears that there is an error getting the entities out/deserialized. Since they are 2 instances of my web role, instance1 might place some entity objects in the cache and instance2 might get them out. I was expecting this to work, but I am unsure why I am getting this error....

Can anyone help/advise?

like image 287
Ilyas Avatar asked Oct 05 '22 01:10

Ilyas


1 Answers

I ran into the same issue. At least in my case, the problem was the DynamicProxies with which the EF wraps all the model classes. In other words, you might think you're retrieving a Country class, but under the hood, EF is actually dynamically generating a class that's called something like Country_4C17F5A60A033813EC420C752F1026C02FA5FC07D491A3190ED09E0B7509DD85. The last part of the name is obviously generated at run-time, and it can be expected to remain static throughout the life of your application - but (and this is the key) only on the same instance of the app domain. If you've got two machines accessing the same out-of-process cache, one will be storing an object of the type Country_4C17F5A60A033813EC420C752F1026C02FA5FC07D491A3190ED09E0B7509DD85, but that type simply won't exist on the other machine. Its dynamic Country class will be something like Country_JF7ASDF8ASDF8ADSF88989ASDF8778802348JKOJASDLKJQAWPEORIU7879243AS, and so there won't be any type into which it can deserialize the serialized object. The same thing will happen if you restart the app domain your web app is running in.

I'm sure the big brains at MS could come up with a better solution, but the one I've been using is to do a "shallow clone" of my EF objects before I cache them. The C# method I'm using looks like this:

public static class TypeHelper
{
    public static T ShallowClone<T>(this T obj) where T : class
    {
        if (obj == null) return null;
        var newObj = Activator.CreateInstance<T>();
        var fields = typeof(T).GetFields();
        foreach (var field in fields)
        {
            if (field.IsPublic && (field.FieldType.IsValueType || field.FieldType == typeof(string)))
            {
                field.SetValue(newObj, field.GetValue(obj));
            }
        }
        var properties = typeof(T).GetProperties();
        foreach (var property in properties)
        {
            if ((property.CanRead && property.CanWrite) && 
                (property.PropertyType.IsValueType || property.PropertyType == typeof(string)))
            {
                property.SetValue(newObj, property.GetValue(obj, null), null);
            }
        }
        return newObj;
    }
}

This takes care of two problems at once: (1) It ensures that only the EF object I'm specifically interested in gets cached, and not the entire object graph - sometimes huge - to which it's attached; and (2) The object that it caches is of a common type, and not the dynamically generated type: Country and not Country_4C17F5A60A033813EC420C752F1026C02FA5FC07D491A3190ED09E0B7509DD85.

It's certainly not perfect, but it does seem a reasonable workaround for many scenarios.

It would in fact be nice, though, if the good folks at MS were to come up with a way to cache EF objects without this.

like image 189
Ken Smith Avatar answered Oct 13 '22 11:10

Ken Smith