XmlException в десериализации WCF: "Имя не может начинаться с" <"- в автоматических полях поддержки свойств

Я начал испытывать ошибки в десериализации WCF сегодня - в коде, который не изменился и работает в течение нескольких месяцев.

Проблема в том, что я получаю runtime XmlException, говорящий: "Name не может начинаться с" < " персонаж'. Я отлаживался в источнике .NET, и кажется, что ошибка заключается в десериализации возвращаемых объектов из наших вызовов службы WCF. Эти объекты определяются с использованием автоматических свойств, и кажется, что для полей поддержки заданы имена типа <MyProperty>k_BackingField, откуда исходит XmlException.

Я видел пару других ссылок в Интернете, где люди, принимающие решение, приняли: "Я изменил свой код, чтобы не использовать автоматические свойства", что для меня не очень приемлемо, поскольку у меня было бы 100 объектов для изменения, (с тысячами свойств среди них). Кроме того, этот же код работал нормально, когда я запускал его на прошлой неделе и, похоже, не влиял на все сериализованные 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 '<'

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

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

Ответ 1

Я думаю, что я нашел больше информации, чтобы помочь объяснить эту проблему (по крайней мере, в той мере, в какой причина ошибки появляется только для определенных типов).

DTO, которые получают исключения, сообщаемые против них:

  • опубликован как часть моего атрибута [ServiceKnownType]
  • отмечен [Serializable]
  • НЕ, отмеченный [DataContract]

Добавление атрибута [DataContract] к типу разрешает эту проблему. Я понятия не имею, почему, и до сих пор не знаю, почему эта ошибка прерывается, когда это происходит, но непротиворечиво в том, что она затрагивает.

Ответ 2

Я также посмотрел на этот вопрос: Справочник службы WCF - Получение "XmlException: имя не может начинаться с символа" <", шестнадцатеричное значение 0x3C" на стороне клиента.

относительно этого исключения:

System.Xml.XmlException: "Имя не может начинаться с символа" <", шестнадцатеричное значение 0x3C."

  1. Проверьте, загружаете ли вы какие-либо действительные XML файлы (например, не содержат опечаток, например <или>
  2. Если вы используете services + WCF, взгляните на свои интерфейсы Service (интерфейсы с ServiceContract). Это будет хорошей отправной точкой. Теперь проверьте, есть ли у вас какие-либо параметры DTO в методах интерфейса. Перейдите к этим DTO и посмотрите, имеют ли эти классы DTO [Serializable] или [DataContract] или подобные атрибуты. Если эти классы также содержат автоматические свойства, измените их свойства на нотацию с собственным полем поддержки, например:

    частный Foo _Bar; public Foo Bar {get {return _Bar; } set { _Bar = value; } set {_Bar = value; } } }}

Если вам повезет, вы увидите, что ошибки исчезнут! Кажется, существует проблема с автоматическими свойствами, когда автоматически сгенерированное вспомогательное поле имеет имя, похожее, например, на <> нечто, <> d_whothing или тому подобное. Эти имена начинаются с символа "<", что приводит к этой ошибке.

В случае сервисов и WCF ваши сервисные интерфейсы и обратные вызовы (с datacontract) являются хорошим местом для начала замены автоматических свойств. По крайней мере, это дает вам представление, с чего начать вместо замены тысяч автоматических свойств.

Кроме того, попробуйте перехватить FirstChanceExceptions, добавив этот код в начале вашего приложения и записать сообщения в консоль. Это поможет понять, уменьшено или нет количество сообщений "Имя не может начинаться с символа" <".

AppDomain.CurrentDomain.FirstChanceException + = (источник объекта, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e) => {Console.WriteLine("Событие FirstChanceException, возникшее в {0}: {1}", AppDomain.CurrentDomain.FriendlyName,.Сообщение); };

https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-receive-first-chance-exception-notifications

Это то, что я нашел до сих пор. Надеюсь, поможет.

Ответ 3

У меня теперь есть обходной путь, однако я не могу положиться → DTO, вызывающие проблему, были удалены из издателя [ServiceKnownType], что заставляет проблему уйти.

Мне интересно, связана ли проблема с именами членов, на которые я получаю исключения. До сих пор я видел, как он жаловался,

  • Id
  • Address
  • UserName

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

Ответ 4

Я затрагивал эту проблему сегодня (исключение первого шанса, иначе явной проблемы нет). В моем случае 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 сказали ранее, и я проголосовал за их ответы.

Ответ 5

Лучший способ выяснить, в каком поле возникает проблема, - это проверить StackTrace при появлении ошибки:

enter image description here

Ответ, в моем случае, состоял в том, чтобы изменить свойство auto, чтобы явно объявить вспомогательные поля, чтобы избежать возможности этого именования. Так

public string ScreenName { get; set; }

будет выглядеть так:

private string _screenName;
public string ScreenName { get { return _screenName; } set { _screenName = value; } }