locked
Windows Azure Cache Service (Preview) - how to store Linq to Sql objects in Session RRS feed

  • Question

  • I have just signed up for the Windows Azure Cache Service (Preview). I'd like to use the cache service to enable load balancing for ASP.NET web sites hosted on multiple Virtual Machines. We unfortunately are still locked into using Session variables and what's worse, we're storing Linq to Sql objects in the Session.

    Because of issues with serializing Linq Entityset I get these types of errors:

    Exception type: System.Runtime.Serialization.InvalidDataContractException
    Exception message: Type 'System.Data.Linq.EntitySet`1[MyLinqObject]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.  If the type is a collection, consider marking it with the CollectionDataContractAttribute.  See the Microsoft .NET Framework documentation for other supported types.

    Based on an article on stackoverflow and an article in the Azure documenation I tried to use a Custom Serializer. Unfortunately, when I try to use a custom serializer as listed below, this error occurs:

    Exception information:

    Exception type: System.ArgumentNullException
    Exception message: Value cannot be null.

    Parameter name: Custom cache object serializer specified was either incorrectly specified or not specified.

    public class AzureSerializer<T> : IDataCacheObjectSerializer
        {
            object IDataCacheObjectSerializer.Deserialize(System.IO.Stream stream)
            {
                DataContractSerializer dcs = new DataContractSerializer(typeof(T));
                return dcs.ReadObject(stream);
            }
    
            void IDataCacheObjectSerializer.Serialize(System.IO.Stream stream, object value)
            {
                if (!(value is T))
                {
                    throw new ArgumentException();
                }
                DataContractSerializer dcs = new DataContractSerializer(typeof(T));
                dcs.WriteObject(stream, value);
            }
        }

    If I declare the class without <T> and hard coding the type I get this error:

    Type 'Microsoft.Web.DistributedCache.SerializableSessionStateStoreData' with data contract name 'SerializableSessionStateStoreData:http://schemas.datacontract.org/2004/07/Microsoft.Web.DistributedCache' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.

    Any hints on how to implement a custom serializer that works with the new Azure caching service or a different solution for storing Linq to Sql objects in Session would be most appreciated.

    Edit - I have to take the links out for now until my account is verified, apologies!


    • Edited by FlyingDutchman26 Thursday, September 5, 2013 7:11 PM Discovered through more research the question had to be adjusted
    Thursday, September 5, 2013 6:15 AM

All replies

  • Hi,

    Are you facing any error if you specify with the generic <T> as well ?

    When directly specifying the type did you pass the new sub types (if any) as knownTypes parameter to DataContractSerializer.

    Thursday, September 5, 2013 10:04 AM
  • Thanks for your response! I get errors with and without the generic <T>. When I use the below as my custom serializer 

    public class MySerializer : IDataCacheObjectSerializer
        {
            public object Deserialize(System.IO.Stream stream)
            {
                // Deserialize the System.IO.Stream 'stream' from
                // the cache and return the object.
                DataContractSerializer dcs = new DataContractSerializer(typeof(DateTime));
                return dcs.ReadObject(stream);
                
            }
    
            public void Serialize(System.IO.Stream stream, object value)
            {
                // Serialize the object 'value' into the System.IO.Stream 'stream'
                // which will then be stored in the cache.
                DataContractSerializer dcs = new DataContractSerializer(typeof(DateTime));
                dcs.WriteObject(stream, value);
            }
        }

    then stick this in page_load:

    Session["test"] = DateTime.Now;
                Response.Write(string.Format("current date/time:{0}", Session["test"].ToString()));

    I get the following:

    Type 'Microsoft.Web.DistributedCache.SerializableSessionStateStoreData' with data contract name 'SerializableSessionStateStoreData:http://schemas.datacontract.org/2004/07/Microsoft.Web.DistributedCache' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.


    Thursday, September 5, 2013 3:16 PM
  • I found another article about session state and azure caching. In this article it is stated that:

    Note that the ASP.NET providers for Cache do not support binary or custom serialization types. If these serializers are used for session state, the following exception occurs: "Type 'Microsoft.Web.DistributedCache.SerializableSessionStateStoreData' in Assembly 'Microsoft.Web.DistributedCache, Version=101.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' is not marked as serializable"

    Apparently a custom serializer is not an option. Does anybody have another trick up their sleeve for dealing with Linq to Sql objects in session and getting this error:

    Exception type: System.Runtime.Serialization.InvalidDataContractException
    Exception message: Type 'System.Data.Linq.EntitySet`1[MyLinqObject]' cannot be serialized

    Thursday, September 5, 2013 4:31 PM
  • Hi,
    From your error message, I guess you may didn’t  configure Cache Client to use the custom serializer. When we custom serializer, we need to specified our data type through code or through xml configuration. Like this:
    Through Xml configuration:

    <dataCacheClient>
        <serializationProperties serializer="CustomSerializer" 
           customSerializerType="MyNamespace.MySerializer, MyNamespace" />
        <!-- Other dataCacheClient Elements, such as hosts -->
    </dataCacheClient>

    Through Code:

    DataCacheFactoryConfiguration configuration = new DataCacheFactoryConfiguration();
    configuration.SerializationProperties = 
       new DataCacheSerializationProperties(DataCacheObjectSerializerType.CustomSerializer, 
       new MyNamespace.MySerializer());
    
    // Assign other DataCacheFactoryConfiguration properties...
    
    // Then create a DataCacheFactory with this configuration
    DataCacheFactory factory = new DataCacheFactory(configuration);
    Please refer to this code sample tutorials (http://blogs.msdn.com/b/jagan_peri/archive/2012/08/23/custom-serializer.aspx) and this article (http://msdn.microsoft.com/en-us/library/windowsazure/hh552969.aspx ).
    If I misunderstanding, please let me know!
    Thanks.

    Will
    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Friday, September 6, 2013 9:24 AM
  • Hi,

    ASP .NET providers for Cache do not support custom serialization. Kindly note only serializable objects can be added to Windows Azure Cache. You can try as a work around, serializing the object before storing in cache into a base type like say string format, put the object in cache as say string. Get the object, deserialize it before use.

    Serialization similar to this blog post, can be tried : http://www.west-wind.com/weblog/posts/2007/Sep/02/LINQ-to-SQL-and-Serialization

    Hope this helps.



    Friday, September 6, 2013 2:46 PM
  • Thanks for all who have responded. Like I said in my follow up and some of you have noted, it's not possible to use a custom serializer with the new Azure Caching preview. I'm still looking for a way to make this work with Linq to Sql. I've included a code example to illustrate the issue below. Thanks for continuing to look at this issue.

    dbml file:

    <?xml version="1.0" encoding="utf-8"?><Database Class="TestModelDataContext" Serialization="Unidirectional" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
      <Table Name="" Member="Parents">
        <Type Name="Parent">
          <Column Member="Name" Type="System.String" CanBeNull="false" />
          <Column Member="ParentId" Type="System.Int32" IsPrimaryKey="true" CanBeNull="false" />
          <Association Name="Parent_Child" Member="Childs" ThisKey="ParentId" OtherKey="ParentId" Type="Child" />
        </Type>
      </Table>
      <Table Name="" Member="Childs">
        <Type Name="Child">
          <Column Member="Name" Type="System.String" CanBeNull="false" />
          <Column Member="ChildId" Type="System.Int32" IsPrimaryKey="true" CanBeNull="false" />
          <Column Member="ParentId" Type="System.Int32" CanBeNull="false" />
          <Association Name="Parent_Child" Member="Parent" ThisKey="ParentId" OtherKey="ParentId" Type="Parent" IsForeignKey="true" />
        </Type>
      </Table>
    </Database>

    Page using the session:

    namespace cachetest
    {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Parent newParent = new Parent { Name = "John", ParentId=1 };
            Child child1 = new Child { ChildId = 1, Name="John Jr." };
            Child child2 = new Child { ChildId = 2, Name="Betty" };
            newParent.Childs.Add(child1);
            newParent.Childs.Add(child2);
            Session["parent"] = newParent;
    
        }
    }

    web.config (sensitive data removed)

    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="dataCacheClients" type="Microsoft.ApplicationServer.Caching.DataCacheClientsSection, Microsoft.ApplicationServer.Caching.Core" allowLocation="true" allowDefinition="Everywhere"/>
        <section name="cacheDiagnostics" type="Microsoft.ApplicationServer.Caching.AzureCommon.DiagnosticsConfigurationSection, Microsoft.ApplicationServer.Caching.AzureCommon" allowLocation="true" allowDefinition="Everywhere"/>
      </configSections>
      <connectionStrings>
      </connectionStrings>
      <system.web>
        <compilation debug="true" targetFramework="4.5"/>
        <httpRuntime/>    
        <sessionState mode="Custom" customProvider="AFCacheSessionStateProvider">
          <providers>
            <add name="AFCacheSessionStateProvider" type="Microsoft.Web.DistributedCache.DistributedCacheSessionStateStoreProvider, Microsoft.Web.DistributedCache" cacheName="default" dataCacheClientName="default" applicationName="AFCacheSessionState"/>
          </providers>
        </sessionState>
        <pages controlRenderingCompatibilityVersion="4.0"/>
      </system.web>
      <dataCacheClients>
        <dataCacheClient name="default">
          <autoDiscover isEnabled="true" identifier="[cachename].cache.windows.net"/>
          <securityProperties mode="Message" sslEnabled="false">
            <messageSecurity authorizationInfo="[CacheKey]"/>
          </securityProperties>
        </dataCacheClient>
      </dataCacheClients>
    </configuration>


    Friday, September 6, 2013 6:23 PM
  • Thanks for your response. After more research and as others have pointed out, it's simply not possible to use a Custom Serializer with the Caching Preview. I'm still looking for a way to get Linq to Sql objects with EntitySets to serialize, see the code that I posted lower in the thread.
    Friday, September 6, 2013 6:48 PM
  • I don't know if this will help you but take a look at this artile on Link to Sql serialization: http://msdn.microsoft.com/en-us/library/bb546184.aspx


    Ibrahim Malluf MCP http://www.malluf.com

    Friday, September 6, 2013 7:03 PM
  • Thank you. I had indeed set my serialization mode to Unidirectional. Also, I tried decorating the Parent Class in my example with 
    KnownType(typeof(EntitySet<Child>))]
    to no avail.
    Friday, September 6, 2013 7:14 PM
  • Hi,

    From your error message, you didn’t register the class type in your configuration. Please add this property into your “dataCacheClient” node and change “[Your class NameSpace]” to your setting. Please try it.

    <serializationProperties serializer="Parent" customSerializerType="[Your class NameSpace].Parent,[Your class NameSpace]" />

    Thanks.

    • Proposed as answer by Billgiee Tuesday, September 10, 2013 6:28 AM
    Tuesday, September 10, 2013 6:28 AM
  • hi FlyingDutchman,

    I will mark this this thread as answer,if you find it no help,please fell free to unmark.
    Thanks!


    Will
    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Monday, September 16, 2013 3:16 PM
  • Apologies, but the answer you marked is incorrect, Azure caching simply does not allow you to use a custom serializer. I'll mark the best workaround as answer
    Monday, September 16, 2013 3:23 PM
  • LINQ to SQL objects belong to data accessing tire, while session belong to the front end web presentation tire. It is highly recommended you store DTO (data transfer objects) in session. If your design is correct, those custom objects can be easily serialized by data contract serializer. You can do a search on ASP.NET DTO, and you'll find plenty of resources, even tools that can generate the classes automatically, such as http://weblogs.asp.net/shahar/archive/2009/10/01/dtogenerator-easily-generate-data-transfer-objects-from-ado-net-entity-framework-or-linq-to-sql-data-classes.aspx.

     

    Wednesday, September 18, 2013 1:20 AM