Исключение XmlEx в десериализации WCF: "Имя не может начинаться с" <"- в полях автоматической поддержки свойств
Сегодня я начал испытывать ошибки в десериализации WCF - в коде, который не менялся и работал месяцами.
Проблема в том, что я получаю время выполнения XmlException
s сказать "Имя не может начинаться с символа"<". Я сделал отладку в.NET-источнике, и, похоже, ошибка в десериализации возвращаемых объектов из наших вызовов службы WCF. Эти объекты определяются с использованием автоматических свойств, и кажется, что вспомогательные поля имеют имена, такие как <MyProperty>k_BackingField
, откуда исходит исключение XmlException.
Я видел несколько других ссылок в Интернете, где люди принимают решение: "Я изменил свой код, чтобы не использовать автоматические свойства", что на самом деле не приемлемо для меня, так как мне нужно изменить сотни объектов (с тысячами свойств среди них). Кроме того, этот же код работал нормально, когда я запускал его на прошлой неделе, и, похоже, не влияет на все сериализованные DTO, только некоторые.
Чтобы сделать это еще более расстраивающим, это кажется слегка прерывистым. В этот день не было исключений...!
Вопросы;
- Почему эта проблема внезапно появилась в неизменном коде и неизменном исходном коде?
- Как исправить это, не изменяя все DTO для использования полностью реализованных свойств?
ОБНОВЛЕНИЕ: После дня или около того работы нормально, эта проблема появилась снова - нет никакой причины, я могу найти, почему это работало бы / не работает / работает снова, но мы здесь.
Я отследил проблему дальше, чтобы связать ее с кодом, который есть в моих ServiceContracts, используя ServiceKnownType
атрибут, который используется для определения известных типов для сериализации. Кажется, что, хотя типы, о которых сообщается с ошибками, даже не являются частью вызова службы, который я выполняю в то время, эта ошибка возникает в типах, которые являются частью поведения этих известных типов "публикации".
Проблема возникает, когда я использую некоторый код создания прокси, чтобы применить некоторые сервисные поведения;
IOperationBehavior innerBehavior = new PreserveReferencesOperationBehavior(
description, this.preserveReferences, this.maxItemsInObjectGraph);
innerBehavior.ApplyClientBehavior(description, proxy);
Я не могу отладить ApplyClientBehavior
код как часть System.ServiceModel
(или я могу?), но что-то в этом методе пытается проверить все типы, которые я опубликовал, используя мой ServiceKnownType
атрибут, и ломая на некоторых из них с этим XmlException
, У меня нет ИДЕИ, почему некоторые типы не работают - и только для некоторых из их свойств.
Это пример типов, которые получают сообщения об ошибках против них;
[Serializable]
public class MyDataObject
{
public ActivitySession(string id)
{
this.Id = id;
this.IsOpen = true;
}
public string Id { get; set; }
public bool IsValid { get; set; }
}
Исключение сообщило об ошибке в отношении Id
-> <Id>k_BackingField cannot start with '<'
Так что ничего спорно в этом классе, и нет наследования рассмотреть. Это даже не часть сервисного контракта, только он был ранее опубликован как известный тип для сериализации.
Сейчас это становится довольно эзотерическим, поэтому я не жду ответа, а просто уточняю, в чем проблема.
3 ответа
Я думаю, что нашел больше информации, чтобы помочь объяснить эту проблему, (по крайней мере, в той степени, в которой почему ошибка появляется только на определенных типах).
DTO, которые получают исключения против них:
- опубликовано как часть моего
[ServiceKnownType]
атрибут - отмечен
[Serializable]
- НЕ помечены
[DataContract]
Добавление [DataContract]
Атрибут типа решает эту проблему. Я понятия не имею, почему, и до сих пор не знаю, почему эта ошибка периодически возникает, когда она возникает, но соответствует тому, что она влияет.
Я также посмотрел на этот вопрос: Справочник службы WCF - Получение "XmlException: имя не может начинаться с символа"<", шестнадцатеричное значение 0x3C" на стороне клиента.
относительно этого исключения:
System.Xml.XmlException: "Имя не может начинаться с символа"<", шестнадцатеричное значение 0x3C."
- Проверьте, загружаете ли вы какие-либо действительные XML-файлы (например, не содержат опечаток, например <или>
Если вы используете services + WCF, взгляните на свои интерфейсы Service (интерфейсы с ServiceContract). Это будет хорошей отправной точкой. Теперь проверьте, есть ли у вас какие-либо параметры DTO в методах интерфейса. Перейдите к этим DTO и посмотрите, имеют ли эти классы DTO [Serializable] или [DataContract] или подобные атрибуты. Если эти классы также содержат автоматические свойства, измените их свойства на нотацию с собственным полем поддержки, например:
частный Foo _Bar; public Foo Bar { get { return _Bar; } set { _Bar = value; } }
Если вам повезет, вы увидите, что ошибки исчезнут! Кажется, существует проблема с автоматическими свойствами, когда автоматически сгенерированное вспомогательное поле имеет имя, похожее, например, на <> нечто, <> d_whothing или тому подобное. Эти имена начинаются с символа "<", что приводит к этой ошибке.
В случае сервисов и WCF ваши сервисные интерфейсы и обратные вызовы (с datacontract) являются хорошим местом для начала замены автоматических свойств. По крайней мере, это дает вам представление, с чего начать вместо замены тысяч автоматических свойств.
Кроме того, попробуйте перехватить FirstChanceExceptions, добавив этот код в начале вашего приложения и записать сообщения в консоль. Это поможет понять, уменьшено или нет количество сообщений "Имя не может начинаться с символа"<".
AppDomain.CurrentDomain.FirstChanceException += (источник объекта, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e) => { Console.WriteLine("Событие FirstChanceException, возникшее в {0}: {1}", AppDomain.CurrentDomain.FriendlyName,.Сообщение); };
Это то, что я нашел до сих пор. Надеюсь, поможет.
Для всех, у кого есть эта проблема: если у вас есть XmlException, отмеченный в настройках исключений Visual Studio, он выдаст, даже если исключение будет обработано в System.Runtime.Serialization. Я потратил около 20 часов, пытаясь понять, почему мой код внезапно перестал работать, когда я включил все исключения - на самом деле это не было фатальным исключением, это было всего ~1200 перехваченных исключений XmlExceptions.
Лучший способ выяснить, в каком поле возникает проблема, - проверять StackTrace по мере появления ошибки:
В моем случае ответ заключался в том, чтобы изменить свойство auto, чтобы явно объявить поддерживающие поля, чтобы избежать возможности этого именования. Так
public string ScreenName { get; set; }
становится:
private string _screenName;
public string ScreenName { get { return _screenName; } set { _screenName = value; } }
Сейчас у меня есть обходной путь, но я не могу на это положиться -> DTO, которые вызывают проблему, были удалены из [ServiceKnownType]
издатель, который исправляет ошибку.
Мне интересно, связана ли проблема с именами участников, для которых я получаю исключения. До сих пор я видел, как на это жалуются;
Id
Address
UserName
Было бы разумно ожидать, что эти конкретные имена свойств используются где-то еще в сериализации или сервисной модели, что заставляет их компилироваться по-другому, я полагаю.
Я затрагивал эту проблему сегодня (исключение первого шанса, никакой очевидной проблемы иначе). В моем случае NetDataContractSerializer (NDCS) был сериализацией IFieldData[]
(из библиотеки CSLA.NET). NDCS может сериализовать массивы, он также может сериализовать объекты, которые не имеют [DataContract]
Атрибут применяется к нему. В этом случае сериализатор выводит контракт - все общедоступные свойства чтения / записи и поля типа сериализуются. Это задокументировано здесь: https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts
Так что в моем случае один из объектов в массиве имел ссылку на Fraction
(мой собственный класс) определяется следующим образом:
public sealed class Fraction
{
public int Numerator { get; private set; }
public int Denominator { get; private set; }
public double Value { get; private set; }
}
Это заставляет WCF генерировать исключение "Имя не может начинаться...", что вызвано тем фактом, что автоматические свойства используют сгенерированные закрытые поля, названные как <Numerator>k__BackingField
, Если вы добавите [DataContract]
атрибута к классу, то вы должны явно отметить, что должно быть сериализовано [DataMember]
приписывать. Это делает исключение уйти. Сериализатор больше не касается приватных полей.
На мой взгляд, это ошибка в WCF. Предполагаемый контракт должен использовать только открытую поверхность класса, у которой нет проблем с именами. Это не должно быть отслеживание частных полей (созданных компилятором или нет).
Мой ответ поддерживает / дополняет то, что RJ Lohan и juFo сказали ранее, и я проголосовал за их ответы.