omer schleifer
omer schleifer

Reputation: 3935

Serialization breaks in .NET 4.5

We have a serialization issue which only happens in .NET 4.5 - same code works fine in .NET 4. we're trying to serialize an inherited type with a few fields, both base and inherited class are marked with SerializableAttribute. We get an exception on the client side of Web service saying that there was a MethodAccessException in the server , the server itself does not throw any exceptions , it seems to be a problem in the client serialization process. It is important to note that we are compiling in .NET 4- not .4.5

Update: After implementing the ISerailize and ignoring the "Value" property the program did run correctly, but it means we had to give up on serializing this field.

any help would be most appreciated. Thanks, Omer

The exception details:

System.Web.Services.Protocols.SoapException occurred
  HResult=-2146233087
  Message=System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.InvalidOperationException: There was an error generating the XML document. ---> System.MethodAccessException: Attempt by method 'Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write88_DeviceSiteTypeInfo(System.String, System.String, IOSIGHT.Info.DeviceSiteTypeInfo, Boolean, Boolean)' to access method 'IOSIGHT.Info.DeviceSiteTypeInfo.get_Value()' failed.
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write88_DeviceSiteTypeInfo(String n, String ns, DeviceSiteTypeInfo o, Boolean isNullable, Boolean needType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write1310_GetSiteTypesResponse(Object[] p)
   at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer2089.Serialize(Object objectToSerialize, XmlSerializationWriter writer)
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at System.Web.Services.Protocols.SoapServerProtocol.WriteReturns(Object[] returnValues, Stream outputStream)
   at System.Web.Services.Protocols.WebServiceHandler.WriteReturns(Object[] returnValues)
   at System.Web.Services.Protocols.WebServiceHandler.Invoke()
   --- End of inner exception stack trace ---
  Source=System.Web.Services
  Actor=""
  Lang=""
  Node=""
  Role=""
  StackTrace:
       at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
       at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
       at IOSIGHT.BLL.localhost.IOSightWS.GetSiteTypes() in C:\IOSIGHT\Common\IOSight.BLL\Web References\localhost\Reference.cs:line 25019
       at IOSIGHT.BLL.TypeBankBLL.GetSiteTypes() in C:\IOSIGHT\Common\IOSight.BLL\Entities\TypeBanksBLL.cs:line 477
  InnerException: 

Attached is the base and inherited classes code: Base:

[Serializable()]
public class TypeBankInfo
{


    #region "Fields"
    private int _id = 0;
    private string _Name = string.Empty;
    private string _description = string.Empty;
    private object _value = null;

    #endregion

    #region "Constructors"
    public TypeBankInfo()
    {
    }

    public TypeBankInfo(int ID, string name)
        : this()
    {
        this._id = ID;
        this.Name = name;
    }

    public TypeBankInfo(int ID, string name, string description)
        : this(ID, name)
    {
        this._description = description;
        this._value = Value;
    }

    public TypeBankInfo(int ID, string name, string description, object value)
        : this(ID, name, description)
    {
        this._value = value;
    }

    #endregion

    #region "Properties"
    public virtual string Name
    {
        get
        {
            return this._Name;
        }
        set
        {
            this._Name = value;
        }
    }

    public virtual string Description
    {
        get
        {
            return _description;
        }
        set
        {
            _description = value;
        }
    }

    public virtual int ID
    {
        get
        {
            return _id;
        }
        set
        {
            _id = int.Parse(value.ToString());
        }
    }


    public virtual object @Value
    {
        get
        {
            return this._value;
        }
        set
        {
            this._value = value;
        }
    }

    #endregion

}

Intheried:

[Serializable()]
public class DeviceSiteTypeInfo : TypeBankInfo, ISerializable
{


    #region "Fields"
    private EntityTypeEnum _entitytype = EntityTypeEnum.Site;
    private DeviceIOTemplateInfo[] _IOTemplates;
    private CaptionInfo[] _Captions;
    private int _parentClassID;
    #endregion

    #region "Constructors"
    public DeviceSiteTypeInfo()
    {
    }

    public DeviceSiteTypeInfo(int id, string name)
        : base(id, name)
    {
    }

    public DeviceSiteTypeInfo(int id, string name, string description)
        : base(id, name, description)
    {
    }

    // The special constructor is used to deserialize values. 
    public DeviceSiteTypeInfo(SerializationInfo info, StreamingContext context)
    {
        //parent  fields
        ID = (int)info.GetValue("_id", typeof(int));
        Name = (string)info.GetValue("_Name", typeof(string));
        Description = (string)info.GetValue("_description", typeof(string));


        //my fields
        _entitytype = (EntityTypeEnum)info.GetValue("_entitytype", typeof(EntityTypeEnum));
        _IOTemplates = (DeviceIOTemplateInfo[])info.GetValue("_IOTemplates", typeof(DeviceIOTemplateInfo[]));
        _Captions = (CaptionInfo[])info.GetValue("_Captions", typeof(CaptionInfo[]));
        _parentClassID = (int)info.GetValue("_parentClassID", typeof(int));

    }



    #endregion

    #region "Properties"
    public EntityTypeEnum EntityTypeID
    {
        get
        {
            return this._entitytype;
        }
        set
        {
            this._entitytype = value;
        }
    }



    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    private new object Value
    {
        get
        {
            return base.Value;
        }
        set
        {
            base.Value = value;
        }
    }

    public CaptionInfo[] Captions
    {
        get
        {
            return this._Captions;
        }
        set
        {
            this._Captions = value;
        }
    }

    public DeviceIOTemplateInfo[] IOTemplates
    {
        get
        {
            return this._IOTemplates;
        }
        set
        {
            this._IOTemplates = value;
        }
    }

    public int ParentClassID
    {
        get
        {
            return this._parentClassID;
        }
        set
        {
            this._parentClassID = value;
        }
    }

    #endregion


    #region Methods

   /// <summary>
   /// Called on serialization
   /// </summary>
   /// <param name="info">serialiation info</param>
   /// <param name="context">context</param>
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // parent fields
        info.AddValue("_id", ID, typeof(int));
        info.AddValue("_Name", Name, typeof(string));
        info.AddValue("_description", Description, typeof(string));

        //my fields
        info.AddValue("_entitytype", _entitytype, typeof(EntityTypeEnum));
        info.AddValue("_IOTemplates", _IOTemplates, typeof(DeviceIOTemplateInfo[]));
        info.AddValue("_Captions", _Captions, typeof(CaptionInfo[]));
        info.AddValue("_parentClassID", _parentClassID, typeof(int));
    }

    #endregion

}

Upvotes: 15

Views: 16186

Answers (5)

MadManMarkAu
MadManMarkAu

Reputation: 180

I recently had the same issue, and found the actual cause and the proper solution.

This problem is caused by the XML serializer testing the default value of a property/field. If the field value matches the DefaultValue attribute, it will not serialize it to save space. There is no overload for DefaultValue of type "decimal", so a decimal value will be converted to a float, which is one of the overloads.

You have to explicitly tell the attribute it is a decimal:

[DevaultValue(typeof(decimal), "1.56")]

Be aware that using DefaultValue on a field will not set that field to that value on deserialization. You still have to set the correct value in your constructor, or as a field initializer:

[DefaultValue(typeof(decimal), "1.56")]
decimal MyValue { get; set; } = 1.56;

Upvotes: 0

I had a also such a serialization failure. In my case it was caused by a type mismatch of the [DefaultValue(..)] attributes. I had an attached default value of "1.0d" (a double) for a property of type decimal. It seems that the new implementation of the XmlSerializer can't convert such values anymore but the old one could. There is also the option to switch back to the old version of XmlSerializer by adding an attribute in 'App.config' but this is not recommended by Microsoft (and me). Hope this helps someone.

Upvotes: 3

Varun
Varun

Reputation: 274

We're looking to address this issue in upcoming .NET Framework 4.5 Update. I'll update the post with the download link as soon as update is released. Please contact netfx45compat at Microsoft dot com if you have mission critical app that's impacted, and fix is required urgently. I can help direct you to Microsoft support that can help with your request.

Upvotes: 4

Youssef Moussaoui
Youssef Moussaoui

Reputation: 12395

I looked at your types more closely, and the problem is likely caused by a conflict between:

public virtual object @Value
{
}

on the Base and:

private new object Value
{
}

on the Derived class. Here are two things I would try:

  1. Try making the "new object Value" public. There might be a member access problem.
  2. Try renaming the Base's "@Value" to something like "BaseValue" or some other appropriate name. There might be a problem with your use of the '@' sign.

Upvotes: 1

Youssef Moussaoui
Youssef Moussaoui

Reputation: 12395

In 4.5, the implementation of XmlSerializer was replaced with one that isn't dependent on the C# compiler. While it provides better startup performance and stability, you might be running into a compatibility issue between the implementations. Can you try adding the following to your app.config file and see if that fixes the issue?

<configuration>
  <system.xml.serialization>
    <xmlSerializer useLegacySerializerGeneration="true"/>
  </system.xml.serialization>
</configuration>

If you're concerned about having this work on 4.0, you could try detecting the version of the framework at runtime, and dynamically change the configuration if the runtime is 4.5 or higher. I wrote a blog post a while back explaining how to do that:

http://blogs.msdn.com/b/youssefm/archive/2010/01/21/how-to-change-net-configuration-files-at-runtime-including-for-wcf.aspx

Upvotes: 17

Related Questions