none
Assembly.Load(byte[]) and XmlSerializer InvalidOperationException

    Question

  • Hello,

    I can't figure out what makes XmlSerializer of a type loaded by Assembly.Load(byte[]) in AssemblyResolve event handler go InvalidOperationException in Serialize or Deserialize?!

    Why, if:

    1. Assembly.Load(byte[]) loads the assembly without a load context(LoadNeither)?

    2. XmlSerializer loads the generated assembly using the same Assembly.Load(byte[]) overload?

    Why is there an InvalidOperationException then when casting from one type to another from the same assembly(serialized types)?! All assemblies should be loaded without the load context.

    Then, if I use Assembly.LoadFile instead of Load(byte[]) at the same point it works fine without the IOE!? It should be loaded without the context too or am I wrong?(obviously I am)

    If I make the assembly available to fusion in the probing path it loads(load context) and it also works. Default behaviour. 

    Tuesday, April 01, 2014 1:10 PM

Answers

  • "Or why it triggers the InvalidOperationExcpetion?"

    I don't really know why but the code generated by the serializer is different in those 2 cases. When Load(byte[]) is used the portion of code that converts from Vehicle to VehicleXml is missing, the result is that some other serialization code thinks it got the wrong type.

    It may be a bug in the XmlSerializer code but you're kind of pushing the boundaries here. You're relying on C#'s implicit conversions, I've never seen any documentation which claims that XmlSerializer supports this.


    • Edited by Mike DanesModerator Thursday, April 17, 2014 6:51 AM
    • Marked as answer by Arkej Friday, April 18, 2014 10:07 AM
    Thursday, April 17, 2014 6:51 AM

All replies

  • Hi Arkej,

    Please post some code to help reproduce. The following image is the algorithm for looking assembly.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Wednesday, April 02, 2014 6:29 AM
  • Reproduction is quite simple:

    Have a library project(say TypesLibrary) with this code:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml.Serialization;
    using System.IO;
    
    namespace TypesLibrary
    {
        public class Class1
        {
            public Class1() 
            {
                _sr = new XmlSerializer(typeof(Class2));
            }
            XmlSerializer _sr;
    
            public void Serialize(string filePathName, Class2 data) 
            {
                using (FileStream fs = new FileStream(filePathName, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    _sr.Serialize(fs, data);
                } 
            }
    
            public Class2 DeSerialize(string filePathName) 
            {
                Class2 res = null;
                using (FileStream fs = new FileStream(filePathName,FileMode.Open,FileAccess.Read))
                {
                    res = (Class2)_sr.Deserialize(fs);
                }
                return res;
            }
        }
    
        public class Class2
        {
            public Class2() { Vehicles = new List<Vehicle>(); }
            public string EnElement { get; set; }
            [XmlElement(Type = typeof(VehicleXml), ElementName = "vehicles")]
            public List<Vehicle> Vehicles { get; set; }
        }
    
        public class VehicleXml
        {
            #region Static
            public static implicit operator
                          VehicleXml(Vehicle p)
            {
                return p == null ? null : new VehicleXml(p);
            }
    
            public static implicit operator
                          Vehicle(VehicleXml p)
            {
                return p == null ? null : p.Value;
            }
            #endregion Static
    
            public VehicleXml() { }
    
            public VehicleXml(Vehicle parameter)
            {
                this.Value = parameter;
            }
    
            [XmlElement("car", typeof(Car))]
            [XmlElement("truck", typeof(Truck))]
            public Vehicle Value
            {
                get;
                set;
            }
        }
    
        [XmlInclude(typeof(Car))]
        [XmlInclude(typeof(Truck))]
        public abstract class Vehicle
        {
            public string RegistrationNumber { get; set; }
        }
    
        public class Car:Vehicle 
        {
            public CarTypeEnum CarType
            {
                get;
                set;
            }
        }
    
        public enum CarTypeEnum 
        {
            Cabriolet,
            Coupe,
            Limo,
            Caravan
        }
    
        public class Truck : Vehicle 
        {
            public long Capacity { get; set; }
        }
    }
    

    Then have an exe project(console app for example LoadContextsTester) with the folowing code:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using TypesLibrary;
    using System.Reflection;
    using System.IO;
    
    namespace LoadContextsTester
    {
        class Program
        {
            static Program() 
            {
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            }
            static void Main(string[] args)
            {
    
                Class1 c1 = new Class1();
                Class2 c2 = new Class2();
                c2.EnElement = "This is a test.";
                c2.Vehicles.Add(new Car() { RegistrationNumber = "KP64983", CarType=CarTypeEnum.Caravan });
                c2.Vehicles.Add(new Truck() { RegistrationNumber = "KR9800",Capacity = 10000});
                c1.Serialize("test1.xml", c2);//it breaks here
                Class2 c3 = c1.DeSerialize("test1.xml");//or here
                
            }
    
            static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                Assembly ass = null;
                if (args.Name.StartsWith("TypesLibrary,"))
                {
    			    Assembly  thisExe = System.Reflection.Assembly.GetExecutingAssembly();
                    using (System.IO.Stream rs = thisExe.GetManifestResourceStream("LoadContextsTester.TypesLibrary.dll"))
                    
                    {
                        try
                        {
                            byte[] buffer = new byte[rs.Length];
                            rs.Read(buffer, 0, (int)rs.Length);
                            ass = Assembly.Load(buffer,null,null);
                        }
                        catch{}
                    }
                    //ass = Assembly.LoadFile(@"...\Documents\Visual Studio 2010\Projects\TypesLibrary\bin\Debug\TypesLibrary.dll");//this one works?!
    
                }
                return ass;
            }
        }
    }
    

    and have TypesLibrary.dll as an embeded resource in LoadContextsTest project and not in the fusion probing path. Also add reference to the TypesLibrary.

    Why does it fail on Serialize/Deserialize? What context gets loaded what assembly? I guess there are 3 of them - TypesLibrary, LoadContextsTest and the XmlSerializer generated temp assembly. Please explain what is happening behind the scenes here?

    Thanks.

    Wednesday, April 02, 2014 8:07 AM
  • Hi Arkej,

    I build a project to test your code, but it works. You can get my test project form the following link. http://1drv.ms/1g6qx7R.

    Could you please try it on another computer and see the result.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Wednesday, April 09, 2014 8:28 AM
  • You've missed the whole point with the project in the link. Two projects are needed as I described. Get this one from here http://1drv.ms/1kNffoT
    • Edited by Arkej Thursday, April 10, 2014 9:22 AM
    Thursday, April 10, 2014 8:53 AM
  • Hi Arkej,

    Please have a look at this reference and try the method. http://robboxman.wordpress.com/2008/.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Tuesday, April 15, 2014 7:17 AM
  • Hi,

    it does not help. Also, how would this help with assembly loading? I must correct myself though, the LoadFile method behaves the same as the Load(byte[]), it leads to InvalidOperationException.

    If class definitions get loaded without load context(neither) and then XmlSerializer is constructed(don't know what context the generated assembly is loaded into) it fails with InvalidOperationException.

    Or why it triggers the InvalidOperationExcpetion? I'm supposing here because of types being loaded into two different load contexts?

    Thursday, April 17, 2014 6:31 AM
  • "Or why it triggers the InvalidOperationExcpetion?"

    I don't really know why but the code generated by the serializer is different in those 2 cases. When Load(byte[]) is used the portion of code that converts from Vehicle to VehicleXml is missing, the result is that some other serialization code thinks it got the wrong type.

    It may be a bug in the XmlSerializer code but you're kind of pushing the boundaries here. You're relying on C#'s implicit conversions, I've never seen any documentation which claims that XmlSerializer supports this.


    • Edited by Mike DanesModerator Thursday, April 17, 2014 6:51 AM
    • Marked as answer by Arkej Friday, April 18, 2014 10:07 AM
    Thursday, April 17, 2014 6:51 AM
  • Yes, that's it, you've found it. I haven't checked the generated code, I've
    supposed to much things here.

    When loaded with Load(byte[]) it skeeps this part:

    global::System.Collections.Generic.List<global::TypesLibrary.Vehicle> a = (global::System.Collections.Generic.List<global::TypesLibrary.Vehicle>)((global::System.Collections.Generic.List<global::TypesLibrary.Vehicle>)o.@Vehicles);

    that actually does the implicit conversion with this one:

    System.Collections.ICollection a = 
    (System.Collections.ICollection)prop5_Vehicles[o];

    I guess it is a bug since it obviously supports generic List when loaded in
    Load context. I've tried a few things to workaround it, but only this one
    works:

    public class Class2_2
    { 
    public Class2_2() { Vehicles = new List<VehicleXml>(); }
    public string EnElement { get; set; }
    [XmlElement(Type = typeof(VehicleXml), ElementName = "vehicles")]
    public List<VehicleXml> Vehicles { get; set; }
    }
    

    But it is breaking, it needs the TypesLibrary to be
    changed. I guess I'll stop here since I can't change XmlSerializer nor make him
    generate "correct" methods. Pregenerating correct assembly might work
    though.

    Thank you very much!

    Friday, April 18, 2014 10:06 AM