简体   繁体   中英

XmlSerializer serialization error with inherited types in IIS/ASP.NET but not in a desktop .NET Application

I am primarily asking this question to document a problem that took me several hours to solve where I couldn't find a solution online.

I am using the XmlSerializer to serialize and deserialize a base class where there are several inherited classes:

public abstract class WorkRequest
{}

[WorkRequest]
public class A : WorkRequest
{}

[WorkRequest]
public class B : WorkRequest
{}

The types for the derived classes are loaded via reflection. The key point is how the references for the DLLs are found and loaded:

string path;
Assembly main = Assembly.GetEntryAssembly();

if (main == null) // IIS 
    path = AppDomain.CurrentDomain.RelativeSearchPath;
else // Standard .NET Service
{
    path = main.Location;
    path = path.Substring(0, path.LastIndexOf(Path.DirectorySeparatorChar));
}

string[] fileList = Directory.GetFiles(path, "*.dll");

if (main != null)
    RegisterInterfaces(main);
foreach (string file in fileList)
{
    try
    {
        Assembly asm = Assembly.LoadFile(file);
        RegisterInterfaces(asm);
    }
    catch (Exception e)
    {
    }
}

When the application is running in asp.net, AppDomain.CurrentDomain.RelativeSearchPath returns the bin directory of the web application in IIS. When running as a standard .NET service, the entry assembly Location is used to load referenced assemblies to find the request types. The classes in the DLLs are probed for all the types with the WorkRequestAttribute.

The serialization is done as follows:

var settings = new XmlWriterSettings()
{
    //Encoding = Encoding.UTF8,
    Indent = false,
    OmitXmlDeclaration = true
};

using (var tw = new StringWriter())
using (var xmlWriter = XmlWriter.Create(tw, settings))
{
   var serializer = GetSerializer(typeof(WorkRequest));
   serializer.Serialize(xmlWriter, input);
   tw.Flush();
   return tw.ToString();
}

The serializer is loaded as follows:

var serializer = new XmlSerializer(theType, _knownTypes.Value);

As recommended, the serializer is allocated once and cached, and the type is typeof(WorkRequest). The _knownTypes are the classes inherited from the WorkRequest found by reflection using the WorkRequest attribute by the above code.

This code runs fine as a .NET service and the referenced types loaded as above. When running within IIS, when the XmlSerializer.Serialize() function is called, the following exception was reported:

The type A was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

I also tried using a serializer for the inherited type A and the same known types list, but the following error was reported:

Types A' and 'A' both use the XML type name, 'MyClass', from namespace ''. Use XML attributes to specify a unique XML name and/or namespace for the type.

It appears that the IIS app is loading the DLLs differently then how I'm loading them, and the known types don't match and the serializer can't find the type. Are there any suggestions what I'm doing wrong to load the types when running in IIS?

Is there another way I could address this to resolve the problem?

I solved the problem by using the type of the request passed in as the explicit known type and still using a serializer for typeof(WorkRequest). The reference to the known types is correct. Note that this does not solve the issue for deserialization within IIS, but I don't have a need for that at this time.

It seems the core problem is loading the assemblies in .NET, the types, even though they're the same, .NET does not think they're the same types and is not using the loaded known types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM