WCF DataContract with readonly properties
Asked Answered
B

8

20

I'm trying to return a complex type from a service method in WCF. I'm using C# and .NET 4. This complex type is meant to be invariant (the same way .net strings are). Furthermore, the service only returns it and never receives it as an argument.

If I try to define only getters on properties I get a run time error. I guess this is because no setters causes serialization to fail. Still, I think this type should be invariant.

Example:

[DataContract]
class A 
{
   [DataMember]
   int ReadOnlyProperty {get; private set;}
}

The service fails to load due to a problem with serialization.

Is there a way to make readonly properties on a WCF DataContract? Perhaps by replacing the serializer? If so, how? If not, what would you suggest for this problem?

Thanks,
Asaf

Boredom answered 22/3, 2010 at 18:54 Comment(2)
You might be able to use readonly fields (not properties) and initialize them in the construct of your class. Also..., possible duplicate: https://mcmap.net/q/319560/-wcf-exposing-readonly-datamember-properties-without-set/945456Reexamine
This is even more important today with the move to immutable classes and C#-6.0’s support for auto-implemented get-only properties.Neogothic
A
5

DataMember Field can't be readonly, because wcf dont serialize object as-is and every time befor deserialization starts creates new object instance, using default constructor. Dispatchers use setters to set field values after deserialization.

But all upper text can be a big mistake :)

To make them realy readonly, make the server logic, validating thiss field values.

Arella answered 24/3, 2010 at 15:45 Comment(2)
It actually does not call the default constructor during deserialization. learn.microsoft.com/en-us/dotnet/api/…Volt
It also has no problem setting private members.Fume
G
16

put [DataMember] on backing field, you won't need a setter.

Goggleeyed answered 22/3, 2010 at 19:2 Comment(4)
But it won't be readonly, and won't express that it should be readonly.Boredom
it will never be readonly. Datacontract when transferred over the wire is just an XML. you can't make piece of XML readonly. You can't make whoever is on the other end of the wire not to change its value. It just does not work like that.Goggleeyed
You could simply ignore the value if it is passed to the service? By read only do you mean that the service only returns it?Calculus
@Bryan: yep. The service only returns it and I want to express exactly that.Boredom
F
11

Make your setter public to satisfy the serializer, but just don't do anything on the setter. Not 'purist' but gets it done

public string MyProperty 
{
    get {
        return this._myProperty
    }
    set {}
}
Flog answered 27/1, 2014 at 21:46 Comment(0)
A
5

DataMember Field can't be readonly, because wcf dont serialize object as-is and every time befor deserialization starts creates new object instance, using default constructor. Dispatchers use setters to set field values after deserialization.

But all upper text can be a big mistake :)

To make them realy readonly, make the server logic, validating thiss field values.

Arella answered 24/3, 2010 at 15:45 Comment(2)
It actually does not call the default constructor during deserialization. learn.microsoft.com/en-us/dotnet/api/…Volt
It also has no problem setting private members.Fume
S
4

You can't make the properties readonly, however you can get close to readonly by making the fields part of your contract instead of the properties:

[DataContract]
public class A 
{
   public class A(){}
   public class A(int readonlyproperty){ _readonlyproperty = readonlyproperty}

   [DataMember(Name = "ReadOnlyProperty")]
   internal int _readonlyproperty;

   public int ReadOnlyProperty {
      get {return _readonlyproperty;}
      private set {_readonlyproperty = value;}
}

next make your internals accesable in wcf:

[assembly: InternalsVisibleTo("System.Runtime.Serialization")]

Some samples on how to use this to your advantage: wcf-and-datacontract-serialization-internals-and-tips

Systematism answered 2/10, 2014 at 12:12 Comment(0)
P
1

Actually, you can make the read-only fields serializable. You need to set the 'SerializeReadOnlyTypes' property of the DataContractSerializerSettings to 'True', when constructing the DataContractSerializer, as below:

var xmlSerializer = new DataContractSerializer(typeof(yourTypeHere),  new DataContractSerializerSettings {SerializeReadOnlyTypes=true});

See the MSDN description and details, here: https://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializersettings(v=vs.110).aspx

Pullman answered 20/4, 2015 at 14:33 Comment(0)
T
0

Like others have said, WCF needs getters and setters. That being said, I came here with the same question and the answers made me ask why I needed a read-only property. If you use the MVVM pattern, the class returned by the service is the Model and so should not be exposed directly by the UI. You can easily make the ViewModel read-only. Alternatively you could use a non UI-targetted facade class.

Trichomonad answered 24/8, 2014 at 22:15 Comment(0)
P
0

WCF needs to able to serialize / deserialize the property, which is not possible with a private property. To have a readonly property (within a complex type):

  • Decorate class as [DataContract(IsReference = true)]
  • Decorate each / the property as [DataMember]
  • Public Get, Internal Set
    [DataContract(IsReference = true)]
    public class Format : ValueObject<Format>
    {
        [DataMember]
        public int Height { get; internal set; }
    }
Palestine answered 30/4, 2015 at 13:14 Comment(0)
C
-2

Have you tried making the setter private?

Something like:

public string MyProperty
{
get;
private set;
}
Calculus answered 22/3, 2010 at 19:1 Comment(5)
Yep. That's exactly my problem.Boredom
So this fixed it? Or this is what you were trying?Calculus
It doesn't work. If I make MyProperty a [DataMember] I get a runtime error caused, to my best understanding, from the serialization. It happens as soon as the service loads - not even during a client operation.Boredom
@AsafR: Correct, it seems that you must have a get/set for all fields for the serialization/deserialization to work. I don't think there is a way around this from everything I've read :(Calculus
If you want to put your conclusion in your above answer, I'll be happy to make it the accepted answer.Boredom

© 2022 - 2024 — McMap. All rights reserved.