Веб-сервисы .NET asmx: сериализовать свойство объекта как строковое свойство для поддержки контроля версий - PullRequest
1 голос
/ 03 апреля 2010

Я нахожусь в процессе обновления наших веб-сервисов для поддержки управления версиями. Мы будем публиковать наши версионные веб-сервисы так:

http://localhost/project/services/1.0/service.asmx
http://localhost/project/services/1.1/service.asmx

Одно из требований этой версии заключается в том, что мне не разрешено ломать исходный wsdl (1.0 wsdl). Задача состоит в том, как настроить новые версии классов с помощью логики, лежащей в основе веб-сервисов (эта логика включает в себя несколько классов команд и адаптеров). Обратите внимание, что в настоящее время обновление до WCF не является опцией.

Чтобы проиллюстрировать это, давайте рассмотрим пример с блогами и постами. До появления версий мы передавали конкретные объекты вместо интерфейсов. Таким образом, команда AddPostToBlog будет принимать объект Post вместо IPost.

// Old AddPostToBlog constructor.
public AddPostToBlog(Blog blog, Post post) {
    // constructor body
}

С введением версий я хотел бы сохранить исходный Post при добавлении PostOnePointOne. И Post, и PostOnePointOne будут реализовывать интерфейс IPost (они не расширяют абстрактный класс, потому что это наследование нарушает wsdl, хотя я предполагаю, что может быть способ обойти это с помощью некоторых хитрых приемов сериализации xml).

// New AddPostToBlog constructor.
public AddPostToBlog(Blog blog, IPost post) {
    // constructor body
}

Это подводит нас к моему вопросу о сериализации. Исходный класс Post имеет свойство enum с именем Type. Из-за различных кросс-платформенных проблем совместимости мы меняем наши перечисления в наших веб-сервисах на строки. Поэтому я хотел бы сделать следующее:

// New IPost interface.
public interface IPost
{
    object Type { get; set; }
}

// Original Post object.
public Post
{
    // The purpose of this attribute would be to maintain how
    // the enum currently is serialized even though now the
    // type is an object instead of an enum (internally the
    // object actually is an enum here, but it is exposed as
    // an object to implement the interface).
    [XmlMagic(SerializeAsEnum)]
    object Type { get; set; }
}

// New version of Post object
public PostOnePointOne
{
    // The purpose of this attribute would be to force
    // serialization as a string even though it is an object.
    [XmlMagic(SerializeAsString)]
    object Type { get; set; }
}

XmlMagic относится к XmlAttribute или некоторой другой части пространства имен System.Xml, которая позволила бы мне контролировать тип сериализуемого свойства объекта (в зависимости от того, какую версию объекта я сериализирую).

Кто-нибудь знает, как этого добиться?

1 Ответ

1 голос
/ 08 апреля 2010

Мне не ясно, что вы хотите, с IPost и Post и так далее.

Кажется, вы хотите обновить свой веб-сервис. Вы используете слово «версия». Я не совсем понимаю, что меняется во внешнем интерфейсе. Вы не описали это.

Мы все можем согласиться с тем, что внутренняя реализация услуги должна быть непрозрачной для потребителей этой услуги, верно? Таким образом, независимо от того, обновили ли вы свою реализацию или нет, клиенты службы не должны знать об этом. Независимо от того, используете ли вы IPost, Post или IW, клиенты не должны обращать на это внимания, они не должны знать об этом. Единственное, что имеет значение для потребителя услуг, - это подпись на проводе - открытый интерфейс. WSDL.

Вы уже сказали, что не разрешено нарушать исходный WSDL . Я не знаю, что это значит. Означает ли это

  • новый сервис должен использовать тот же WSDL
  • новая служба может использовать другой WSDL, но старые клиенты должны продолжать работать?
  • что-то еще?

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

Версия 1 может выглядеть так:

<Post xmlns="http://www.example.com/webservices/2010/04">
  <AuthorId>217</AuthorId>
  <Posted>2010-04-07T22:02:23.2214747Z</Posted>
  <Title>Hello, I must be going.</Title>
  <Content>...</Content>
  <Type>Rant</Type>
</Post>

Для обеспечения совместимости версия 2 может только добавлять элементы. Например, добавьте элемент LastEdited к сообщению:

<Post xmlns="http://www.example.com/webservices/2010/04">
  <AuthorId>217</AuthorId>
  <Posted>2010-04-07T22:02:23.2214747Z</Posted>
  <LastEdited>2010-04-07T22:02:23.2214747Z</LastEdited>
  <Title>This is Getting Very Interesting</Title>
  <Content>...</Content>
  <Type>Rant</Type>
</Post>

Когда я говорю «совместимый», я имею в виду, что документы версии 1, подобные вышеприведенным, будут де-сериализуемы сервисами версии 2.

Вы делаете что-то подобное? Если так, уточните.

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


Если вам нужен способ сериализации свойства в строку, а тип свойства не имеет соответствующей реализации ToString (), вы можете использовать суррогатное свойство. Шаблон работает так: отметьте фактическое свойство [XmlIgnore]. Создайте дополнительное «суррогатное» свойство типа String с помощью метода получения и установки, который считывает или обновляет фактическое свойство. Как это:

[XmlRoot("Post", Namespace="http://www.example.com/webservices/2010/04")]
public class Post1_1
{
    public Int32 AuthorId { get; set; }
    public DateTime Posted { get; set; }
    public String Title { get; set; }
    public PostType @Type { get; set; }

    [XmlIgnore]
    public DateTime LastEdited { get; set; }

    [XmlElement("LastEdited")]
    public String LastEdited_Surrogate
    {
        get
        {
            if (LastEdited > (DateTime.UtcNow - new TimeSpan(24,0,0)))
                return "Today";
            else if (LastEdited > (DateTime.UtcNow - new TimeSpan(48,0,0)))
                return "Yesterday";
            else
                return LastEdited.ToString();
        }

        set
        {
            if (value == "Today")
                LastEdited = DateTime.UtcNow;
            else if (value == "Yesterday")
                LastEdited = DateTime.UtcNow - new TimeSpan(24,0,0);
            else
                LastEdited = DateTime.Parse(value);
        }
    }
}
...