Nickolodeon
Nickolodeon

Reputation: 2956

wcf newbie question: arrays as properties

I have DataContract class which has property of type List<AnotherObject>. AnotherObject is also marked with DataContract. For some reason this property comes from wcf service as null, althought I fill it at the server. Is that by design?

Here you go. Class definitions:

[DataContract]
    public class UserInfo
    {
        [DataMember]
        public decimal UserID
        {
            get;
            protected internal set;
        }

        [DataMember]
        public string UserName
        {
            get;
            protected internal set;
        }

        [DataMember]
        public string Pswd
        {
            get;
            protected internal set;
        }       

        [DataMember]
        public List<decimal> RoleID
        {
            get;
            protected internal set;
        }

        List<UserRole> userRolesTable = new List<UserRole>();
        [DataMember]
        public List<UserRole> UserRoles
        {
            get
            {
                return userRolesTable;
            }
            protected internal set { }
        }       
    }



[DataContract]
    public class UserRole
    {
        [DataMember]
        public decimal ROLEID { get; internal set; }

        [DataMember]
        public string ROLE_CODE { get; internal set; }

        [DataMember]
        public string ROLE_DESCRIPTION { get; internal set; }

        [DataMember]
        public decimal FORMID { get; internal set; }

        [DataMember]
        public string FORMCODE { get; internal set; }

        [DataMember]
        public string FORMNAME { get; internal set; }
    }

UserRoles property comes as null.

Upvotes: 4

Views: 828

Answers (5)

Alec
Alec

Reputation: 1706

Basically, its a serialization problem. I had this problem in my code in the past, but it has been a while, so bear with me.

First, we need to find out if the object relations are null before the WCF call, so put a debug before and after.

If the object is being returned as null before the call, you have a few options:

  1. You can explicitly use .Include("AnotherObject") on your DbContext to get the object. I used this by having my Read method take an array of strings which I used to include all the necessary objects. This is more ideal than automatically taking all objects because during serialization, if you take everything, you could fairly easily end up with your entire database being serialized, which introduces performance and security issues, among other things.

  2. Another option is to use a dynamic proxy by adding the keyword virtual in front of your list. The DataContractSerializer, though, has a problem serializing dynamic proxies, so you will need to implement an attribute that uses the ProxyDataContractResolver instead of DataContractResolver. This attribute needs to be applied on all OperationContracts that can pass a dynamic proxy. This will automatically take ALL object references, which is probably bad coding practice, so I do recommend the above method.

    public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior
    {
    
      public ApplyDataContractResolverAttribute() { }
    
      public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { }
    
      public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
      {
        DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
      }
    
      public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
      {
        DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
      }
    
      public void Validate(OperationDescription description) { }
    
    }
    

Edit: Also I think you can have setters in Data Contracts not be public, because I do it and it works fine :). But I would try making your setter public first, then solving the problem, then reverting to a protected setter, just so that you are dealing with as few variables at a time as possible.

Upvotes: 0

James Michael Hare
James Michael Hare

Reputation: 38397

Why are you letting the RoleId property be auto-implemented but not UserRoles? The code as-is won't work because you have an empty setter. You should probably just use an auto-property for it:

[DataMember]
public List<UserRole> UserRoles
{
    get; set;
}      

Or at least provide a meaningful setter. You setter does nothing, hence the de-serializer can't populate the value.

Upvotes: 3

Nathan Tregillus
Nathan Tregillus

Reputation: 6334

Your Setter on the UserRoles property is set to internal. Because the WCF framework will be setting the property, it gives up assigning the value because it is listed as internal.

http://connect.microsoft.com/data/feedback/details/625985/wcf-client-entities-with-internal-setters-and-internalsvisibletoattribute-on-asmbly-fail

You can do what this link suggests, using the InternalsVisibleToAttribute attribute on that property, but I have never used it.

update What I am trying to say is that I bet the Serialization works fine, the WCF framework is unable to insert the deserialized value into the host code because based upon the data contract, the internal Setter section of the property is inaccessible. use the InternalVisibleTo attribute to inform the WCF serialization framework access to the setter of the client version of your data contract object.

Upvotes: 1

DiVan
DiVan

Reputation: 381

List<UserRole> userRolesTable = new List<UserRole>();
[DataMember]
public List<UserRole> UserRoles
{
    get
    {
        return userRolesTable;
    }
    protected internal set { }
} 

Your setter is empty. Put some

userRolesTable = value;

Another thing, your DataContract properties should have public setters.

Upvotes: 1

MattS423
MattS423

Reputation: 311

You need to Implement the setter...

protected internal set { userRolesTable = value; }

Upvotes: 0

Related Questions