WCF ... Passing in Custom Objects

I have 2 WCF Services into which I wish to pass a dataobject (the same dataobject).

for examples if I have an object with two properties ; say X (int) and Y (string)

on client side i have

myDataObject objX = new myDataObject();
objX.X = 10
objX.Y = "Hello"

I want to pass this to two seperate WCF Services like :-

myService1 svc1 = new myService1();
myService2 svc2 = new myService2();

svc1.SomeMethod(objX)
svc2.OtherMethod(objX)

if i set up data contracts on the service side i keep getting cannot convert type objX to SVC.DataContract etc etc
even if the data contract matches my local object.

Can this be done ?
any help would be greatly appreciated.

Services :- DataContract class
 
using System;
using System.Data;
using System.ServiceModel;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml.Serialization;
 
[DataContract(Name = "myDataObject")]
public class myDataObject
{
    public int intX;
    public string strX;
}
 
 
 
Service 1 Class
 
 
using System;
using System.ServiceModel;
 
 
[ServiceContract()]
public interface IDataService1
{
    [OperationContract]
    string MyOperation1(myDataObject objX);
}
 
public class DataService1 : IDataService1
{
    public string MyOperation1(myDataObject objX)
    {
        return "Service 1";
    }
}
 
 
Service 2 Class
 
 
using System;
using System.ServiceModel;
 
 
[ServiceContract()]
public interface IDataService2
{
	[OperationContract]
    string MyOperation1(myDataObject objX);
}
 
public class DataService2 : IDataService2
{
    public string MyOperation1(myDataObject objX)
	{
        return "Service 2";
    }
}
 
 
Client Side
 
DataObject class
 
    [DataContract(Name = "myDatCont")]
    [Serializable]
    public class myDataObject
    {
        [DataMember]
        public int Parameter1;
        [DataMember]
        public string Parameter2;
    }
 
 
running the services
 
            myDataObject dat1 = new myDataObject();
            svc1.DataService1Client service1 = new myWeb.svc1.DataService1Client();
            svc2.DataService2Client service2 = new myWeb.svc2.DataService2Client();
 
            TextBox3.Text = service1.MyOperation1(dat1);
            TextBox4.Text = service2.MyOperation1(dat1);
 
 
Error received at compile :-
 
Argument '1': cannot convert from 'myWeb.myDataObject' to 'myWeb.svc1.myDataObject'
 
 
This works (but its not what I want because it requires two seperate data objects):-
 
            svc1.DataService1Client service1 = new myWeb.svc1.DataService1Client();
            svc1.myDataObject dat1 = new myWeb.svc1.myDataObject();
            svc2.DataService2Client service2 = new myWeb.svc2.DataService2Client();
            svc2.myDataObject dat2 = new myWeb.svc2.myDataObject();
 
            TextBox3.Text = service1.MyOperation1(dat1);
            TextBox4.Text = service2.MyOperation1(dat2);

Open in new window

DerekhendersonAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
alaranConnect With a Mentor Commented:
Even though your datacontract is the same in both services, when your client creates a proxy for each service, the resulting client side datacontract classes will not be the same. That is, you get two classes on the client side (one for each service) with the same signature, but they are inherently different classes, in different namespaces, and so you cannot cast one to the other.

I can think of two ways of passing the data to both classes that should work.

1. Copy the property values from an instance of on type to an instance of the other type. This can be done with reflection.

2. (The one I think I would do)
As a type used in web services is inherently serializable you can use XmlSerializer to serialize an instance of namespace1.class1, and deserialize it into an instance of namespace2.class1. It works because the XML resulting from a serialized namespace1.class1 is equal to the serialized result of namespace2.class1.

Example give below. Although the code in your proxies will not be excly as below, the principle should work. Please tell if it doesn't.

On a side note, objects passed to a service method are serialized into SOAP (XML) and sent as text to the service, which in turn de-serializes the data into a new instance of the data contract class. As such passing an object by reference to a service proxy will not result in the exact same instance being sent to the service, but rather a textual representation of the instance's state is sent to the service.

// First proxy namespace
namespace MyServiceProxyNamespace1
{
	public class MyClass1
	{
		public int X;
		public int Y;
	}
}
 
// Second proxy namespace
namespace MyServiceProxyNamespace2
{
	public class MyClass1
	{
		public int X;
		public int Y;
	}
}
 
// A console app showing the principle
static public class Program
{
    [STAThread]
    static void Main()
    {
         // Create an instance of the first class
	Namespace1.MyClass1 mc1 = new Namespace1.MyClass1();
	mc1.X = 100;
	mc1.Y = 200;
 
	// Uncomment the next line to see that this line gives you a compiler error, 
         // which is what you are experiencing, and for the same reason.
	// Namespace2.MyClass1 mc2 = (Namespace1.MyClass2) mc1;
 
	// Create a StringBuilder, and a StringWriter which will write text into the StringBuilder.
	StringBuilder sb = new StringBuilder();
	StringWriter sw = new StringWriter(sb);
 
	// Create an XmlSerializer, passing it the type it will serialize.
	XmlSerializer serializer1 = new XmlSerializer(typeof(Namespace1.MyClass1));
	// Serialize the instance of the first class.
	serializer1.Serialize(sw, mc1);
 
	// Get the result of the serialization, so you can see what it looks like.
	string s = sb.ToString();
 
	// Create a StringReader, passing it the string containg the serialized object.
	StringReader sr = new StringReader(s);
 
	// Create a new XmlSerializer passing it the type it will deserialize. In this case the other class.
	XmlSerializer serializer2 = new XmlSerializer(typeof(Namespace2.MyClass1));
	// Use the serializer to deserialize the string into an object of the second type.
	Namespace2.MyClass1 mc2 = (Namespace2.MyClass1) serializer2.Deserialize(sr);
    }
}

Open in new window

0
 
p_davisCommented:
if you want the object to be the exact same instance then pass by ref. now about the
message "cannot convert type objX to SVC.DataContract" you will have to cast the object as that datacontract type to be able to use it

(SVC.DataContract)objX
0
 
DerekhendersonAuthor Commented:
even using by ref I still get the compile error

using the cast you suggest ...

   myDataObject dat1 = new myDataObject();  // define on client machine as class:
    service1.MyOperation1((svc1.myDataObject)dat1)
    service2.MyOperation1((svc2.myDataObject)dat1)

Cannot convert type 'myDataObject' to 'svc1.myDataObject'
Cannot convert type 'myDataObject' to 'svc2.myDataObject'

0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
p_davisCommented:
why do you have the client side with a myDataObjectClass when you have that on the service side as well?

actually i guess it wouldn't matter but it doesn't appear that either of your datacontracts are doing anything. i believe that you need to setup your datamemebers with gets and sets.
0
 
DerekhendersonAuthor Commented:
Even when the datacontracts are done using gets and set as in

    [DataContract]
    [Serializable]
    public class myDataObject
    {
        private int param1;
        private string param2;

        [DataMember]
        public int Parameter1
        {
            get { return param1; }
            set { param1 = value; }
        }

        [DataMember]
        public string Parameter2
        {
            get { return param2; }
            set { param2 = value; }
        }
      
    }

i get exactly the same error.

Cannot convert type 'myDataObject' to 'svc1.myDataObject'
Cannot convert type 'myDataObject' to 'svc2.myDataObject'
0
 
p_davisCommented:
what if, on your service you set the parameter to just be of type Object and then if you need to actually use the object (which i am assuming you do) try casting it after the service receives it to the dataobject that you need.
0
 
p_davisCommented:
also, the ref suggestion was just if you wanted to work with one instance of the object and not copies.-- not as a solution to your errors.
0
 
DerekhendersonAuthor Commented:
As an object it compiles but now I get :

service is defined as

public class DataService1 : IDataService1
{
    public string MyOperation1(object objX)
    {
        myDataObject dat1 = (myDataObject)objX;   // dataobject defined service side
        return dat1.Parameter2;
    }
}

client side

            svc1.DataService1Client service1 = new myWeb.svc1.DataService1Client();

            myDataObject dat1 = new myDataObject();   // myDataObject defined client side
            dat1.Parameter2 = "Hello";

            TextBox3.Text = service1.MyOperation1(dat1);   // line causes error


{"Type 'myDataObject' with data contract name 'myDataObject' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer."}
0
 
p_davisCommented:
try casting dat1 as an object as well when passing in as a parameter.
0
 
DerekhendersonAuthor Commented:
As far as I can workout (and i could be wrong)... The only thing we've acheived now is to move the error from compile time ... to run time.  It appears to still have the same issue, in that it can't match a local object to a remote one.  

Am I really trying something that no one else has ever wanted to do, surely in OO programming someone will have wanted to pass an object to a number of web services!!!!

0
 
p_davisCommented:
we use wcf and datacontracts for winforms here and when we want to create an object of that datacontract schema we insantiate the services datacontract type. not usually creating one on the client side to send back of the clients type.

what did it do at runtime, specifically?
0
 
DerekhendersonAuthor Commented:
it gave me the runtime error :

{"Type 'myDataObject' with data contract name 'myDataObject' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer."}

Even if I insantiate the service datacontract, it works for that service but I then have no way of passing the object to another service, for the same reason it states the datacontracts cannot be converted.

Prehaps if I explain my thought process ... you may tell me I'm an idiot and should be doing it a different way.  I have an object ... lets say an purchase order object.  Lets say I have a number of WCF services, one that calculates total VAT, one that calculates total postage, and one that saves the Order to the database. I will need to pass the same populated object to each of the 3 services at different points of time. But each service should receive the same set of data. Thats the kind of thing I want to do, only not with purchase orders.
0
 
p_davisCommented:
i would never call you an idiot.-- the documentation on this stuff is weak at best.
i would still say to have one datacontract on your service side and pass that by reference after instantiating on the client side. that way you are only working with one datacontract 'type' and one instance of it.

--does that make sense?
0
 
p_davisConnect With a Mentor Commented:
<<On a side note, objects passed to a service method are serialized into SOAP (XML) and sent as text to the service, which in turn de-serializes the data into a new instance of the data contract class. As such passing an object by reference to a service proxy will not result in the exact same instance being sent to the service, but rather a textual representation of the instance's state is sent to the service.>>

that may be but it will act as if it were the same instance because what ever you do to it on the service side the client side object will maintain those differences.-- my wording may be off but the effect is the same.
0
 
alaranCommented:
Of course... if you donæt neen any of the more "generic" ways of converting one to the other, you could alway make a function like the one below, which will be a bit more effective. But then again since you're doing this on the client side, resource usage shouldn't be much of a problem, and if you have these needs for several contracts it's kind of boring to have to code a converter method for each of them...

 
Namespace1.MyClass1 mc1 = new Namespace1.MyClass1();
mc1.X = 100;
mc1.Y = 200;
Namespace2.MyClass1 mc2 = Convert(mc1);
 
public method Namespace2.MyClass1 Convert(Namespace1.MyClass1 in)
{
    Namespace2.MyClass1 out = new Namespace2.MyClass1();
    out.X = in.X;
    out.Y = in.Y;
    return out;
}

Open in new window

0
All Courses

From novice to tech pro — start learning today.