views:

594

answers:

1

When constructing XmlSerializer instances in .NET, assemblies for serializing and deserializing the specified type are generated dynamically. This is a time-consuming process. The sgen.exe tool from Microsoft can be used to precompile XmlSerializer instances to use them later without generating them dynamically. Unfortunately this is not possible with XmlSerializer instances which use XmlAttributeOverrides.

Is there any way to precompile these XmlSerializer instances to avoid generation at run-time?

+3  A: 

Andreas, this is not a problem of the sgen tool itself, this is due to the XmlSerializer implementaion.

When you create an instance of XmlSerializer, using the constructor with only one Type argument, it goes and checks the cache and looks for the pre-generated assemblies. But when you use the constructor with XmlAttributeOverrides, XmlSerializer doesn't check any caches and goes generating the temp assembly right away.

Most probably, that's because of the quite radical changes in the serialization logic that you can achive using the XmlAttributeOverrides argument, which cannot be "foreseen" in compile-time by tools like sgen.

If you need to have things pre-compiled, you [sigh] have to avoid the XmlAttributeOverrides. If this is not possible, try creating the required XmlSerializer instances ahead-of-time, maybe in a background thread.

Just for your interest, here is the code for the default constructor (checks cache and tries to find a pre-generated assembly):

public XmlSerializer(Type type, string defaultNamespace)
{
    this.events = new XmlDeserializationEvents();
    if (type == null)
    {
        throw new ArgumentNullException("type");
    }
    this.mapping = GetKnownMapping(type, defaultNamespace);
    if (this.mapping != null)
    {
        this.primitiveType = type;
    }
    else
    {
        this.tempAssembly = cache[defaultNamespace, type];
        if (this.tempAssembly == null)
        {
            lock (cache)
            {
                this.tempAssembly = cache[defaultNamespace, type];
                if (this.tempAssembly == null)
                {
                    XmlSerializerImplementation implementation;
                    Assembly assembly = TempAssembly.LoadGeneratedAssembly(type, defaultNamespace, out implementation);
                    if (assembly == null)
                    {
                        this.mapping = new XmlReflectionImporter(defaultNamespace).ImportTypeMapping(type, null, defaultNamespace);
                        this.tempAssembly = GenerateTempAssembly(this.mapping, type, defaultNamespace);
                    }
                    else
                    {
                        this.mapping = XmlReflectionImporter.GetTopLevelMapping(type, defaultNamespace);
                        this.tempAssembly = new TempAssembly(new XmlMapping[] { this.mapping }, assembly, implementation);
                    }
                }
                cache.Add(defaultNamespace, type, this.tempAssembly);
            }
        }
        if (this.mapping == null)
        {
            this.mapping = XmlReflectionImporter.GetTopLevelMapping(type, defaultNamespace);
        }
    }
}

And here is the constructor used with XmlAttributeOverrides (always generates serialization assembly):

public XmlSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, string defaultNamespace, string location, Evidence evidence)
{
    this.events = new XmlDeserializationEvents();
    if (type == null)
    {
        throw new ArgumentNullException("type");
    }
    XmlReflectionImporter importer = new XmlReflectionImporter(overrides, defaultNamespace);
    for (int i = 0; i < extraTypes.Length; i++)
    {
        importer.IncludeType(extraTypes[i]);
    }
    this.mapping = importer.ImportTypeMapping(type, root, defaultNamespace);
    if (location != null)
    {
        this.DemandForUserLocation();
    }
    this.tempAssembly = GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);
}
Yacoder