O WCF bloqueia em propriedades sem “conjunto”. Alguma solução alternativa?

97

Tenho uma classe que estou passando como resultado de um método de serviço e essa classe tem uma propriedade get-only:

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

Estou recebendo uma exceção no lado do serviço:

System.Runtime.Serialization.InvalidDataContractException: Nenhum método definido para a propriedade 'Message' no tipo 'MyNamespace.ErrorBase'.

Preciso ter essa propriedade como apenas getter, não posso permitir que os usuários atribuam um valor a ela. Alguma solução alternativa que eu possa usar? Ou estou faltando algum atributo adicional?

Andrey
fonte

Respostas:

106

Dê a Message um getter público, mas um setter protegido, de modo que apenas as subclasses (e o DataContractSerializer, porque ele engana :) possam modificar o valor.

rh.
fonte
Essa é uma solução bacana!
Russell
Obrigado, que bom que foi útil! Na verdade, este é apenas um dos vários usos para esse truque. Como getters e setters são tecnicamente funções, você também pode usar essa mesma técnica para fornecer serialização personalizada de tipos primitivos (talvez um formato de hora personalizado em XML) sem a necessidade de usar o intimidante IDataContractSurrogate.
rh.
28
Você pode até torná-lo privado. O Serializer não se importa se é privado, público, protegido, interno ou interno protegido.
Abel
8
e faça o setterthrow new NotSupportedException()
Simon_Weaver
2
Tendo um private set;funciona se você usar [DataContract]e [DataMember]. Se você omiti-los, você precisa public set;.
user276648
12

Mesmo se você não precisar atualizar o valor, o setter é usado pelo WCFSerializer para desserializar o objeto (e redefinir o valor).

Este SO é o que você procura : WCF DataContracts

Russell
fonte
1
Então, a única maneira de superar o problema é torná-lo um método em vez de propriedade? Mais uma vez, não posso permitir "set" nesta propriedade
Andrey
Você poderia torná-lo um método (por exemplo, GetMessage () {return "";}) alternativamente, tenho certeza de que você poderia dizer ao serializador WCF para ignorá-lo. Vou ver o que posso encontrar e informá-lo.,
Russell
1
Esta questão stackoverflow acerta em cheio: stackoverflow.com/questions/172681/wcf-datacontracts
Russell
11
[DataMember(Name = "PropertyName")]
public string PropertyName
{
    get
    {
        return "";
    }
    private set
    { }
}
Rikin Patel
fonte
5

Se você só tem um getter, por que precisa serializar a propriedade? Parece que você poderia remover o atributo DataMember da propriedade somente leitura e o serializador simplesmente ignoraria a propriedade.

Rob Lambert
fonte
3
De fato, não faz sentido serializar uma propriedade derivada (por exemplo, uma propriedade de URL que é calculada a partir de uma propriedade de ID) para um armazenamento persistente (por exemplo, um banco de dados) - voto positivo para isso - mas faz sentido serializá-lo para um representação (por exemplo, JSON ou XML) que é retornada por uma solicitação de API.
Florian Winter
3

Você não poderia simplesmente ter um setter "não fazer nada" ??

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

Ou o serializador DataContract barf também ??

marc_s
fonte
14
Não vomita, eu só não quero deixar os desenvolvedores que usam a API do cliente pensarem que podem atribuir coisas à propriedade.
Andrey
2

Propriedades com atributo DataMember sempre requerem conjunto. Você deve reescrever o objeto semelhante no aplicativo cliente, pois os membros DataContract sempre podem receber valores.

Jojo Sardez
fonte
2

Eu tive esse problema com a ASP.NET MVC e eu queria usar DataContractSerializer para poder controlar os nomes dos itens na saída JSON. Por fim, mudei o serializador para JSON.NET, que oferece suporte a propriedades sem setters (que DataContractSerializer não oferece) e controle de nome de propriedade (que o serializador JSON integrado na ASP.NET MVC não oferece) [JsonProperty(PropertyName = "myName")].

Thomas Lundström
fonte
2

Se for uma opção viável, em vez de ter ErrorBasecomo classe base, defina-a da seguinte maneira:

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

            // leave unattributed
            set;
        }
    }

Agora, mesmo que exista um setter, ele está inacessível para o cliente via canal WCF, então é como se fosse privado.

Gilad
fonte