Constructor in WCF DataContract not reflected on Client
Asked Answered
S

3

11

I need to have some data members get some values when I create an instance of the DataContract on the client. This is not happening using constructors. I have searched through different forums and found we have to use [OnDeserializing] and [OnDeserialized] attributes. This is also not working. Can somebody suggest something here. The other alternative is creating constructors in the partial classes at the client side. I want to avoid that.

Please find the code below:

Server-side: Datacontract

[DataContract]
public class Account
{

    private int mAccountId;
    private string mAccountName;

    public Account()
    {
        mAccountId = 5;
        mAccountName = "ABC";
    }

    [OnDeserializing]
    public void OnDeserializing(StreamingContext context)
    {
        mAccountId = 5;
        mAccountName = "ABC"; 
    }

    [OnDeserialized]
    public void OnDeserialized(StreamingContext context) 
    {

    } 

    [DataMember]
    public int AccountId
    {
        get
        {
            return mAccountId;
        }
        set
        {
            mAccountId = value;
        }
    }

    [DataMember]
    public string AccountName
    {
        get
        {
            return mAccountName;
        }
        set
        {
            mAccountName = value;
        }
    }


}

Client side - initialization

namespace TestClient
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Account acc = new Account();

        }
    }
}
Sidney answered 11/6, 2011 at 13:18 Comment(1)
The WCF client-server connection will only mirror the data aspects of your data contracts - not any code you might have in your data classes. After all: only data can be serialized into a format represented by an XSD (XML schema) and sent across the wire - no code.Hockenberry
C
15

The code generator used to create WCF proxy classes creates compatible contract types, and doesn't used the exact same type as used by the WCF service. The easiest way to achieve what you want, is to create the constructor yourself on your client, as the code generated is partial:

partial class Account
{
    public Account()
    {
        AcountId = 5;
        AccountName = "ABC";
    }
}

If you don't want to do this, you can get WCF to reuse types that are already referenced by your client project. So if your data contract classes are in a separate library (as is recommended), you can reference that library and then reconfigure your WCF client project to reuse the shared types from the referenced assembly.

Cauchy answered 11/6, 2011 at 13:24 Comment(3)
any other workaround? Why is [OnDeserializing]/[OnDeserialized] attributes not working.....Thanks...Sidney
Because those attributes are for deserialization = when you get Account from the service not when you call the constructor!Ecuador
+1 for putting the datacontract classes in another library. We have put them in the library with the service interface definition, and in reality the communication contracts are a service concern, not the concern of the domain model.Hosiery
H
17

The properties attributed with the DataMember attributes only define what will be included in the generated WSDL/XSD. The client will generate its own classes based on the wsdl/xsd to use for communication with the service. It does not use the same classes which are used on the server.

This is why you will not get:

  • any constructors defined in the DataContract class
  • any private [DataMember] properties/fields (the client will always generate public properties/fields)
  • any behaviour defined in the DataContract class

Imagine the scenario where a java client wants to connect to your service. Do you expect the java classes to be generated with the same constructor? What about with the [OnDeserialized] attributes? What about a java script client, or python client?

When you start to think about it in this way you start to see why you cannot have what you want (at least not without sharing libraries between the client and server).

The reality is that you cannot force a client to have classes which always have default values and you cannot for a client to always send back valid data, the client can always just send a message which contains rubbish if it wants. You have a little control over some aspects of the message with the IsRequired and 'EmitDefaultValue` which will add checks into the xsd to ensure that something is present in the message, but you will have to do validation on the server, you can't assume that the objects you get back will be validated.

My suggestion would be to create DTOs from your domain objects to send across the wire, which contain no checking of any sort, they are just simple bags for holding the data. Then create factories to turn your domain objects into DTOs and DTOs into client objects. The factory simply takes the DTO and passes the members into the constructor of the domain object. Then your validation logic can live in the constructor of the domain object where it belongs. With the approach you currently have you will probably end up twisting the validation slightly so it can be done from both the constructor and in the [OnDeserialized] method.

Hosiery answered 11/6, 2011 at 13:44 Comment(0)
C
15

The code generator used to create WCF proxy classes creates compatible contract types, and doesn't used the exact same type as used by the WCF service. The easiest way to achieve what you want, is to create the constructor yourself on your client, as the code generated is partial:

partial class Account
{
    public Account()
    {
        AcountId = 5;
        AccountName = "ABC";
    }
}

If you don't want to do this, you can get WCF to reuse types that are already referenced by your client project. So if your data contract classes are in a separate library (as is recommended), you can reference that library and then reconfigure your WCF client project to reuse the shared types from the referenced assembly.

Cauchy answered 11/6, 2011 at 13:24 Comment(3)
any other workaround? Why is [OnDeserializing]/[OnDeserialized] attributes not working.....Thanks...Sidney
Because those attributes are for deserialization = when you get Account from the service not when you call the constructor!Ecuador
+1 for putting the datacontract classes in another library. We have put them in the library with the service interface definition, and in reality the communication contracts are a service concern, not the concern of the domain model.Hosiery
G
1

make Proxy class in your client side:

public class AccountProxy: Account
{
   public AccountProxy()
   {
         mAccountId = 5;
         mAccountName = "ABC";
   }
}

and use your proxy class, not generated client class:

namespace TestClient
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            AccountProxy acc = new AccountProxy();

        }
    }
}
Gawk answered 4/5, 2018 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.