XmlSerializer, дающий FileNotFoundException у конструктора - PullRequest
331 голосов
/ 14 июля 2009

Приложение, с которым я работал, не работает, когда я пытаюсь сериализовать типы.

утверждение типа

XmlSerializer lizer = new XmlSerializer(typeof(MyType));

производит:

System.IO.FileNotFoundException occurred
  Message="Could not load file or assembly '[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
  Source="mscorlib"
  FileName="[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
  FusionLog=""
  StackTrace:
       at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
       at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)

Я не определяю никаких специальных сериализаторов для своего класса.

Как я могу решить эту проблему?

Ответы [ 17 ]

367 голосов
/ 24 июля 2009

Верьте или нет, это нормальное поведение. Исключение выдается, но обрабатывается XmlSerializer, поэтому, если вы просто игнорируете его, все должно продолжаться в порядке.

Я нахожу это очень раздражающим, и было много жалоб по этому поводу, если вы немного поищете, но из того, что я прочитал, Microsoft не планирует ничего с этим делать.

Вы можете избежать появления всплывающих окон Исключения во время отладки, если отключите исключения первого шанса для этого конкретного исключения. В Visual Studio перейдите к Отладка -> Исключения (или нажмите Ctrl + Alt + E ), Исключения общего времени выполнения -> System.IO -> System.IO.FileNotFoundException .

Информацию об этом можно найти в сообщении в блоге C # XmlSerializer FileNotFound исключение (инструмент Криса Селлса XmlSerializerPreCompiler ).

100 голосов
/ 03 августа 2009

Как сказал Мартин Шербурн, это нормальное поведение. Сначала конструктор XmlSerializer пытается найти сборку с именем [YourAssembly] .XmlSerializers.dll, которая должна содержать сгенерированный класс для сериализации вашего типа. Поскольку такая DLL еще не была сгенерирована (по умолчанию они не созданы), выдается исключение FileNotFoundException. Когда это происходит, конструктор XmlSerializer ловит это исключение, и DLL автоматически генерируется во время выполнения конструктором XmlSerializer (это делается путем генерации исходных файлов C # в каталоге% temp% вашего компьютера, а затем компиляции их с помощью компилятора C #). Дополнительные конструкции XmlSerializer для того же типа просто будут использовать уже сгенерированную DLL.

ОБНОВЛЕНИЕ: Начиная с .NET 4.5, XmlSerializer больше не выполняет генерацию кода и не выполняет компиляцию с помощью компилятора C #, чтобы создать сборку сериализатора во время выполнения, если это явно не вызвано установкой настройка файла конфигурации ( useLegacySerializerGeneration ). Это изменение устраняет зависимость от csc.exe и повышает производительность при запуске. Источник: .NET Framework 4.5 Readme , раздел 1.3.8.1.

Исключение обрабатывается конструктором XmlSerializer. Не нужно ничего делать самостоятельно, вы можете просто нажать «Продолжить» (F5), чтобы продолжить выполнение вашей программы, и все будет хорошо. Если вас беспокоят исключения, останавливающие выполнение вашей программы и вызывающие помощника исключений, либо у вас отключено «Просто мой код», либо у вас установлено FileNotFoundException, чтобы прервать выполнение при вызове, а не когда «Пользователь- необработанный».

Чтобы включить «Просто мой код», выберите Инструменты >> Параметры >> Отладка >> Общие >> Включить просто Мой код. Чтобы отключить прерывание выполнения при возникновении FileNotFound, перейдите в раздел «Отладка» >> «Исключения» >> «Найти» >> введите «FileNotFoundException» >> снимите флажок «Брошено» в System.IO.FileNotFoundException.

63 голосов
/ 21 июля 2009

В свойствах проекта Visual Studio (на странице «Сборка», если я правильно помню) есть опция «создать сборку сериализации». Попробуйте включить его для проекта, который генерирует [Содержит сборку MyType] .

58 голосов
/ 30 марта 2012

Для этого есть обходной путь. Если вы используете

XmlSerializer lizer = XmlSerializer.FromTypes(new[] { typeof(MyType) })[0];

следует избегать этого исключения. Это сработало для меня.

ПРЕДУПРЕЖДЕНИЕ: Не используйте несколько раз, иначе у вас будет утечка памяти

Вы потеряете память как сумасшедшую, если будете использовать этот метод для создания экземпляров XmlSerializer для одного и того же типа более одного раза!

Это связано с тем, что этот метод обходит встроенное кэширование, предоставленное конструкторами XmlSerializer(type) и XmlSerializer(type, defaultNameSpace) (все другие конструкторы также обходят кэш).

Если вы используете какой-либо метод для создания XmlSerializer, который не использует эти два конструктора, вы должны реализовать собственное кэширование, иначе у вас будет кровоточить память.

21 голосов
/ 15 сентября 2016

Я столкнулся с этой проблемой и не смог обойти ее ни одним из упомянутых решений.

Тогда я наконец нашел решение. Похоже, что сериализатору нужен не только тип, но и вложенные типы. Изменение этого:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

К этому:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T).GetNestedTypes());

Исправлена ​​проблема для меня. Нет больше исключений или что-нибудь.

9 голосов
/ 23 февраля 2012

Мое решение - перейти прямо к размышлению, чтобы создать сериализатор. Это обходит странную загрузку файла, которая вызывает исключение. Я упаковал это в вспомогательную функцию, которая также заботится о кэшировании сериализатора.

private static readonly Dictionary<Type,XmlSerializer> _xmlSerializerCache = new Dictionary<Type, XmlSerializer>();

public static XmlSerializer CreateDefaultXmlSerializer(Type type) 
{
    XmlSerializer serializer;
    if (_xmlSerializerCache.TryGetValue(type, out serializer))
    {
        return serializer;
    }
    else
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(type, null, null);
        serializer = new XmlSerializer(mapping);
        return _xmlSerializerCache[type] = serializer;
    }
}
7 голосов
/ 29 октября 2009

Чтобы избежать исключения, вам нужно сделать две вещи:

  1. Добавить атрибут в сериализованный класс (надеюсь, у вас есть доступ)
  2. Создание файла сериализации с помощью sgen.exe

Добавьте атрибут System.Xml.Serialization.XmlSerializerAssembly в свой класс. Замените «MyAssembly» на имя сборки, в которой находится MyClass.

[Serializable]
[XmlSerializerAssembly("MyAssembly.XmlSerializers")]
public class MyClass
{
…
}

Создайте файл сериализации с помощью утилиты sgen.exe и разверните его вместе со сборкой класса.

gen sgen.exe MyAssembly.dll ’создаст файл MyAssembly.XmlSerializers.dll

Эти два изменения приведут к тому, что .net непосредственно найдет сборку. Я проверил это, и он работает на .NET Framework 3.5 с Visual Studio 2008

6 голосов
/ 07 августа 2009

Это исключение также может быть перехвачено управляемым помощником по отладке (MDA) с именем BindingFailure.

Этот MDA полезен, если ваше приложение предназначено для поставки с предварительно собранными сериализованными сборками. Мы делаем это для повышения производительности нашего приложения. Это позволяет нам убедиться в том, что предварительно созданные сборки сериализации правильно собираются нашим процессом сборки и загружаются приложением без повторной сборки на лету.

Это действительно бесполезно, кроме как в этом сценарии, потому что, как говорили другие авторы, когда ошибка связывания перехватывается конструктором Serializer, сборка сериализации перестраивается во время выполнения. Так что обычно вы можете выключить его.

6 голосов
/ 22 сентября 2016

Функция XmlSerializer.FromTypes не выдает исключение, но она утечка памяти. Вот почему вам необходимо кэшировать такой сериализатор для каждого типа, чтобы избежать утечки памяти для каждого созданного экземпляра.

Создайте свою собственную фабрику XmlSerializer и используйте ее просто:

XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(MyType));

Фабрика выглядит так:

public static class XmlSerializerFactoryNoThrow
{
    public static Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();

    private static object SyncRootCache = new object();        

    /// <summary>
    /// //the constructor XmlSerializer.FromTypes does not throw exception, but it is said that it causes memory leaks
    /// /1080329/xmlserializer-dayschii-filenotfoundexception-u-konstruktora
    /// That is why I use dictionary to cache the serializers my self.
    /// </summary>
    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            //constructor XmlSerializer.FromTypes does not throw the first chance exception           
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            //serializer = XmlSerializerFactoryNoThrow.Create(type);
        }

        lock (SyncRootCache)
        {
            _cache[type] = serializer;
        }
        return serializer;
    }       
}

Более сложная версия без возможности утечки памяти (пожалуйста, просмотрите код):

    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            lock (SyncRootCache)
            {
                if (_cache.TryGetValue(type, out serializer))
                    return serializer;
            }
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            lock (SyncRootCache)
            {
                _cache[type] = serializer;
            }
        }          
        return serializer;
    }       
}
3 голосов
/ 14 июля 2009

Устранение ошибок компиляции, с другой стороны, очень сложно. Эти проблемы проявляются в исключении FileNotFoundException с сообщением:

File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll"
   at System.Reflection.Assembly.nLoad( ... )
   at System.Reflection.Assembly.InternalLoad( ... )
   at System.Reflection.Assembly.Load(...)
   at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly() 

Вы можете задаться вопросом, какое исключение имеет файл, не связанный с созданием объекта сериализатора, но помните: конструктор записывает файлы C # и пытается их скомпилировать. Стек вызовов этого исключения предоставляет некоторую полезную информацию для поддержки этого подозрения. Исключительная ситуация произошла, когда XmlSerializer попытался загрузить сборку, созданную CodeDOM, вызвав метод System.Reflection.Assembly.Load. Исключение не содержит объяснения того, почему не присутствовала сборка, которую должен был создать XmlSerializer. В общем случае сборка отсутствует, поскольку компиляция не удалась, что может произойти из-за того, что в редких случаях атрибуты сериализации создают код, который компилятор C # не может компилировать.

Примечание Эта ошибка также возникает, когда XmlSerializer работает под учетной записью или в среде безопасности, которая не может получить доступ к временному каталогу.

Источник : http://msdn.microsoft.com/en-us/library/aa302290.aspx

...