WCF задыхается от свойств без «набора». Есть обходной путь?

97

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

[DataContract]
public class ErrorBase
{
  [DataMember]
  public virtual string Message { get { return ""; } }
}

Я получаю исключение на стороне сервиса:

System.Runtime.Serialization.InvalidDataContractException: не установлен метод для свойства «Сообщение» в типе «MyNamespace.ErrorBase».

У меня должно быть это свойство только как получатель, я не могу разрешить пользователям присваивать ему значение. Какое решение я мог бы использовать? Или мне не хватает какого-то дополнительного атрибута?

Андрей
источник

Ответы:

107

Дайте Message общедоступный получатель, но защищенный установщик, чтобы только подклассы (и DataContractSerializer, потому что он читер :) могли изменять значение.

rh.
источник
Отличное решение!
Рассел
Спасибо, рад, что это было полезно! На самом деле это лишь одно из нескольких применений этого трюка. Поскольку методы получения и установки являются технически функциями, вы также можете использовать тот же метод для обеспечения настраиваемой сериализации примитивных типов (возможно, настраиваемого формата времени в XML) без необходимости использования устрашающего IDataContractSurrogate.
рх.
28
Вы даже можете сделать это приватным. Сериализатору не важно, является ли он частным, общедоступным, защищенным, внутренним или защищенным внутренним.
Авель
8
и сделайте сеттерthrow new NotSupportedException()
Simon_Weaver
2
Имея private set;работает , если вы используете [DataContract]и [DataMember]. Если вы их опустите, вам понадобится public set;.
user276648
12

Даже если вам не нужно обновлять значение, установщик используется WCFSerializer для десериализации объекта (и повторной установки значения).

Это то, что вам нужно: WCF DataContracts

Рассел
источник
1
Значит, единственный способ решить эту проблему - сделать это методом, а не свойством? Опять же, я не могу позволить "установить" на это свойство
Андрей
Вы можете сделать это методом (например, GetMessage () {return "";}) в качестве альтернативы, я почти уверен, что вы можете указать сериализатору WCF игнорировать его. Я посмотрю, что смогу найти, и дам вам знать.
Рассел,
1
Этот вопрос о stackoverflow попадает в самую точку
Рассел,
11
[DataMember(Name = "PropertyName")]
public string PropertyName
{
    get
    {
        return "";
    }
    private set
    { }
}
Рикин Патель
источник
5

Если у вас есть только геттер, зачем вам вообще сериализовать свойство. Похоже, вы могли бы удалить атрибут DataMember для свойства только для чтения, и сериализатор просто проигнорирует это свойство.

Роб Ламберт
источник
3
Действительно, не имеет смысла сериализовать производное свойство (например, свойство URL, которое вычисляется из свойства ID) в постоянное хранилище (например, базу данных) - за это проголосуйте за - но имеет смысл сериализовать его в представление (например, JSON или XML), возвращаемое запросом API.
Флориан Винтер
4

Не могли бы вы просто установить "ничего не делать" ??

[DataContract]
public class ErrorBase
{
  [DataMember]
  public virtual string Message 
  {
      get { return ""; } 
      set { }
  }
}

Или сериализатор DataContract тоже не работает?

marc_s
источник
14
Это не мешает, я просто не хотел, чтобы разработчики, использующие клиентский API, думали, что они могут назначать что-то свойству.
Андрей
2

Свойства с атрибутом DataMember всегда необходимо указывать. Вы должны переписать аналогичный объект в клиентском приложении, поскольку членам DataContract всегда могут быть присвоены значения.

Jojo Sardez
источник
2

У меня была эта проблема с ASP.NET MVC, и я хотел использовать DataContractSerializer, чтобы иметь возможность контролировать имена элементов в выводе JSON. В конце концов я переключил сериализатор на JSON.NET, который поддерживает свойства без сеттеров (чего нет в DataContractSerializer) и управление именем свойства (чего не делает встроенный сериализатор JSON в ASP.NET MVC) через [JsonProperty(PropertyName = "myName")].

Томас Лундстрём
источник
2

Если это приемлемый вариант, тогда вместо ErrorBaseбазового класса определите его следующим образом:

    public interface IError
    {
        string Message
        {
            [OperationContract]
            get;

            // leave unattributed
            set;
        }
    }

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

Гилад
источник