I have been struggling with the following error all morning.
I have a custom workflow activity in CRM 2011, that generates a PDF summary from CRM records. To maintain a reasonable good performance and flexibility, I have based the layout on a XML web ressource, where the layout configurations and images are cached using MemoryCache.
The code runs perfectly in Unit Test, but when running the actual workflow, I get the following error:
Workflow paused due to error: Unhandled Exception: System.InvalidCastException: [A]System.Collections.Generic.List`1[Crm.Pdf.PdfGenerator+AttributeDescription] cannot be cast to [B]System.Collections.Generic.List`1[Crm.Pdf.PdfGenerator+AttributeDescription]. Type A originates from 'mscorlib, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b77a5c561934e089' in the context 'LoadNeither' at location 'C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_220.127.116.11__b77a5c561934e089\mscorlib.dll'. Type B originates from 'mscorlib, Version=18.104.22.168, Culture=neutral, PublicKeyToken=b77a5c561934e089' in the context 'LoadNeither' at location 'C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_22.214.171.124__b77a5c561934e089\mscorlib.dll'. ... ... at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)
It turned out the error occurs when caching types that are defined in the workflow assembly. CRM workflows are versioned, in the sence that running workflows will not update their implementation even if the actual workflow assembly is updated through plugin registration Tool. In this way, each time a workflow is writing to the MemoryCache, a new instance of the workflow assembly is being used. This causes a problem when fetching data from MemoryCache, because in order to cast the data back to the original type, not only must it be the same type and assembly, but also the same instance of the assembly.
In order to make caching work in this scenario, there are two options:
- Rewrite your caching code to store general types, for example by doing serialization
- Add the custom types that needs caching to a separate assembly that can be stored in the Global Assembly Cache
I found the first option most preferable in my specific case, from a maintenance and deployment perspective.