Sending complex type as a parameter in SOAP message - wcf

I have a WCF Service like following:
public class Service1 : IService1
{
public string GetData(Person person)
{
if (person != null)
{
return "OK";
}
return "Not OK!";
}
Here is my Person class:
[DataContract]
public class Person
{
[DataMember]
public int Age { get; set; }
[DataMember]
public string Name { get; set; }
}
And I'm calling service like that:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(new BindingParameterCollection());
factory.Open();
EndpointAddress address = new EndpointAddress(url);
IRequestChannel irc = factory.CreateChannel(address);
using (irc as IDisposable)
{
irc.Open();
string soapMessage = "<GetData><person><Age>24</Age><Name>John</Name></person></GetData>";
XmlReader reader = XmlReader.Create(new StringReader(soapMessage));
Message m = Message.CreateMessage(MessageVersion.Soap11,"http://tempuri.org/IService1/GetData", reader);
Message ret = irc.Request(m);
reader.Close();
return ret.ToString();
}
When I try to send complex type like Person as a parameter to GetData method, person object is coming null. But I have no problem when I send known type like integer, string etc. as a parameter.
How can I manage to send complex type as a parameter to the service method?

I ran into a similar situation, and we ended up changing the interface of the service to be the equivalent of:
public string GetData(string person)
And we did our own object serialization before calling the web service. Immediately within the web service method we would deserialize it, and proceed as normal.

Related

WCF Serialization Error Using NetTCP

Using VS2012 and NetTcpBinding. I am getting the following error when I call the serviceContract from the client - The service is hosted in IIS:
There was an error while trying to serialize parameter CS.ServiceContracts.Zzzzzz.Common:GetZipCodesResult. The InnerException message was 'Type 'System.DelegateSerializationHolder+DelegateEntry' with data contract name 'DelegateSerializationHolder.DelegateEntry:http://schemas.datacontract.org/2004/07/System' is not expected. Consider using a DataContractResolver or 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.'. Please see InnerException for more details.
Here is my ServiceContract:
[ServiceContract(SessionMode = SessionMode.Allowed, Namespace = "CS.ServiceContracts.Zzzzzz.Common",
Name = "IZzzzzzCommonService")]
public interface IZzzzzzCommonService
{
[OperationContract]
GetZipCodesResponse GetZipCodes(GetZipCodesRequest request);
}
Here is my DataContract:
[DataContract]
[Serializable]
public class GetZipCodesResponse : ResponseBase
{
[DataMember(IsRequired = true)]
public List<ZipCodes> ZipCodes { get; set; }
}
And here is the ResponseBase:
[DataContract]
[Serializable]
[KnownType(typeof(GetZipCodesResponse)), XmlInclude(typeof(GetZipCodesResponse))]
public class ResponseBase
{
[DataMember(IsRequired = true)]
public int ResponseCode { get; set; }
[DataMember(IsRequired = false)]
public int ReasonCode { get; set; }
[DataMember(IsRequired = false)]
public string ReasonText { get; set; }
}
Here is the Implementation of the serviceContract Interface:
public class ZzzzzzCommonService : IZzzzzzCommonService
{
public GetZipCodesResponse GetZipCodes(GetZipCodesRequest request)
{
var response = new GetZipCodesResponse();
try
{
response.ZipCodes = ZipCodes.GetCustom(request.ZipCode, request.City, request.State);
}
catch (Exception ex)
{
this.BuildExceptionResponse(response, ex);
}
return response; // I get to this line ok, but here is where the error occurs
}
}
And here is the client code where I am calling the Service:
public void ZipCodes()
{
var endPoint = new EndpointAddress(
"net.tcp://localhost/CS.WebService.Zzzzzz.Common/ZzzzzzCommonService.svc");
var binding = new NetTcpBinding { TransferMode = TransferMode.Buffered, SendTimeout = TimeSpan.MaxValue, ReceiveTimeout = TimeSpan.MaxValue, MaxReceivedMessageSize = 100000000, MaxBufferSize = 100000000 };
using (var channel = new ChannelFactory<IZzzzzzCommonService>(binding, endPoint))
{
try
{
channel.Endpoint.Contract.SessionMode = SessionMode.Allowed;
var proxy = channel.CreateChannel();
var request = new GetZipCodesRequest();
request = new GetZipCodesRequest { ZipCode = "32701" };
response = proxy.GetZipCodes(request);
}
}
}
I have debugged the code and I am getting into the entity and the dataSet is populated with the appropriate rows but when it sends the dataset back is when I get the errors.
Not sure what I am missing. I believe I have the serviceContract and dataContract members decorated correctly so not sure why it is having problems serializing the List.
The problem was the ZipCode class. It was an Entity class and it was not able to be serialized. So I deconstructed the class and it was able to send it back to the client.

Passing List<T> as parameter to WCF service operation

I have a WCF operation contract which looks like this:
public void SavePersons(List<Person> list, bool IsSelected)
{
}
I am passing it a strongly typed list of Person objects (List<Person>) in my client. However, I am getting a bad request 400 message when calling the service. What am I doing wrong?
May I suggest you create you create a contract to encapsulate the parameters like so:
public void SavePersons(PersonCollectionContract Request)
{
...
}
[DataContract]
public class PersonCollectionContract
{
[DataContract]
public List<Person> People { get; set; }
[DataContract]
public bool IsSelected { get; set; }
}
[DataContract]
public class Person
{
...
}
I was facing a similar problem in passing a List<Health> of class Health type as a parameter to a wcf service method. I created a data contract in wcf service as below:
[DataContract]
public class Health
{
...
}
Defined a method in wcf service class such as:
public string GetData(List<Health> healthValues)
In my client application, while configuring/updating the service, I followed these steps:
Add/Update URL
Under Data Type (in Advanced), selected option, Collection type: System.Collection.Generic.List
And finally, I created a list and added the code in client as follows:
List<WcfService.Health> listHealth = new List<WcfService.Health>();
WcfService.Health h = new WcfService.Health();
.
.
listHealth.Add(h);
WcfService.Service1Client s = new WcfService.Service1Client();
string str = s.GetData(listHealth);
This solved my purpose and I was able to send the data as a list through wcf service.

Calling a WCF Service without a Service reference

I have a project A which has a Service reference to a WCF Service. I want to invoke the service in project B without a Service reference.
From what I understand, the Service reference is just a way to generate the proxy and config and is not used at run-time.
I copied the proxy class and the node from project A to project B.
Can I just create an instance of the proxy class in project B and expect it to directly use the config. items and connect to the service without anything else?
(I cannot try this right now)
Short answer yes.
As long as you have the interface, a way to connect to the service, you can create a channel and talk to service without having the reference.
The reference simply makes it easier for you to develop against.
Look into creating channels from your service contracts with ChannelFactory.
Here is a working copy. For me it works fine and returns List
private List<MyClass> GetAllSiteDetailsJSON(string language)
{
Uri address =
new Uri(#"http://weburlpath/MyService/MyService.svc/GetAllList/"
+ language);
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string jSon = reader.ReadToEnd();
reader.Close();
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
List<MyClass> result = jsSerializer.Deserialize<List<MyClass>>(jSon);
return result;
}
}
And Here is the class
public class MyClass
{
public string ID { get; set; }
public string Name { get; set; }
public List<Location> Locations { get; set; }
}
public class Location
{
public string Region { get; set; }
public string Country { get; set; }
}

Client WCF DataContract has empty/null values from service

I have a simple WCF service that returns the time from the server. I've confirmed that data is being sent by checking with Fiddler. Here's the result object xml that my service sends.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetTimeResponse xmlns="http://tempuri.org/">
<GetTimeResult xmlns:a="http://schemas.datacontract.org/2004/07/TestService.DataObjects" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:theTime>2010-03-26T09:14:38.066372-06:00</a:theTime>
</GetTimeResult>
</GetTimeResponse>
</s:Body>
</s:Envelope>
So, as far as I can tell, there's nothing wrong on the server end. It's receiving requests and returning results.
But on my silverlight client, all the members of the returned object are either null, blank or a default vaule. As you can see the server returns the current date and time. But in silverlight, theTime property on my object is set to 1/1/0001 12:00 AM (default value).
Sooo methinks that the DataContracts do not match up between the server and the silverlight client. Here's the DataContract for the server
[DataContract]
public class Time
{
[DataMember]
public DateTime theTime { get; set; }
}
Incredibly simple. And here's the datacontract on my silverlight client.
[DataContract]
public class Time
{
[DataMember]
public DateTime theTime { get; set; }
}
Literally the only difference is the namespaces within the application. But still the values being returned are null, empty or a .NET default.
Thanks for you help!
UPDATE
Here is the ClientBase that all my services run through. I read an excellent article here to construct it.
public class ClientBase<T> where T :class
{
private T Channel { get; set; }
private Type ContractType { get; set; }
private ClientBase()
{
ContractType = typeof( T );
}
public ClientBase(string endPointConfiguration) :this()
{
Channel = new ChannelFactory<T>( endPointConfiguration ).CreateChannel();
}
public ClientBase( EndpointAddress address, Binding binding ):this()
{
Channel = new ChannelFactory<T>( binding, address ).CreateChannel();
}
public void Begin(string methodName, object state, params object[] parameterArray)
{
Begin( methodName, null, state, parameterArray );
}
public void Begin(string methodName, EventHandler<ClientEventArgs> callBack, object state, params object[] parameterArray)
{
if(parameterArray != null)
{
Array.Resize(ref parameterArray, parameterArray.Length + 2);
}
else
{
parameterArray = new object[2];
}
parameterArray[ parameterArray.Length - 1 ] = new ObjectClientState {CallBack = callBack, MethodName = methodName, UserState = state};
parameterArray[ parameterArray.Length - 2 ] = new AsyncCallback( OnCallBack );
ContractType.InvokeMember( "Begin" + methodName,
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod |
System.Reflection.BindingFlags.Public, null, Channel, parameterArray );
}
private void OnCallBack(IAsyncResult result)
{
ObjectClientState state = result.AsyncState as ObjectClientState;
if(state == null)
return;
Object obj = ContractType.InvokeMember( "End" + state.MethodName,
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod |
System.Reflection.BindingFlags.Public, null, Channel, new object[] {result} );
if(state.CallBack != null)
{
state.CallBack( this, new ClientEventArgs {Object = obj, UserState = state.UserState} );
}
}
public class ClientEventArgs : EventArgs
{
public object Object { get; set; }
public object UserState { get; set; }
public T LoadResult<T>()
{
if( Object is T )
return ( T ) Object;
return default( T );
}
}
private class ObjectClientState
{
public EventHandler<ClientEventArgs> CallBack { get; set; }
public string MethodName { get; set; }
public object UserState { get; set; }
}
}
Here is my interface
[ServiceContract]
public interface ITestService
{
[OperationContract( AsyncPattern = true )]
IAsyncResult BeginGetTime( AsyncCallback callback, object state );
Time EndGetTime( IAsyncResult result );
}
Now I have my service class that makes calls through my BaseService class using this interface.
public class TestSiteService : ClientBase<ITestService>
{
public TestSiteService (string endPointConfiguration):base(endPointConfiguration) { }
public TestSiteService ( EndpointAddress address, Binding binding ) : base( address, binding ) { }
public void GetTime( EventHandler<ClientEventArgs> callBack )
{
Begin( "GetTime", callBack, null, null );
}
}
Finally here is the code that actually calls everything and does the work.
TestSiteService client = new TestSiteService ( new EndpointAddress( "http://localhost:3483/wcf/Service.svc" ), new BasicHttpBinding() );
client.GetTime( delegate( object res, ClientBase<ITestService>.ClientEventArgs e )
{
Dispatcher.BeginInvoke( () => lblDisplay.Text = "Welcome " + e.LoadResult<Time>().theTime );
} );
Whew....I hope no one is lost from all this code I posted :P
Because you don't set the Namespace property on your DataContractAttribute, the namespace will be sythesized from the .NET class/namespace. You can see this in the SOAP message example you posted:
http://schemas.datacontract.org/2004/07/TestService.DataObjects
In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides. That might look a little something like this:
[DataContract(Namespace="urn:my-test-namespace")]
Extending on Drew Marsh's correct answer (+1 - thx) I had a generated Service Reference which was working, but when I tried to use the Wcf Client Factory one implementing the correct interface (but the namespace was different) then I was experiencing the problem described.
I had no easy way to work out what the "correct" namespace should have been but simply copying the following attributes from the service reference's DataContract entity to the one in the Wcf Client Factory implementation solved the issue;
[System.Runtime.Serialization.DataContractAttribute(Name = "BOSPrice", Namespace = "http://schemas.datacontract.org/2004/07/BOSDataService")]
[System.SerializableAttribute()]

WCF Error 109: here was an error reading from the pipe. The pipe is closed on the client side

I am getting the following error on my client app
There was an error reading from the pipe: De pipe is beƫindigd. (109,0x6d).
when using a specific implementation of my OperationContract. The following is a sample cut down to the point.
My DataContracts as like this:
[DataContract]
public class Person
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
[DataContract]
public class Employee : Person
{
[DataMember]
public string Function { get; set; }
}
And my ServiceContract looks like this:
[ServiceContract]
public interface IAuthentication
{
[OperationContract]
[WebGet]
Person GetDeveloper();
[OperationContract]
[WebGet]
Person GetArchitect();
}
I implement this service like the following class:
public class Authentication : IAuthentication
{
public Person GetDeveloper()
{
Person architect = new Person()
{
FirstName = "Asghar",
LastName = "Panahy"
};
return architect;
}
public Person GetArchitect()
{
Employee architect = new Employee()
{
FirstName = "Asghar",
LastName = "Panahy",
Function = "Architect"
};
return architect;
}
}
Note: Both methods return the same type, only one instanciates a Person and returns it while the second method instanciates an Employee which is a Person too.
When I call it from the client I do not get any error on the server but on the client side:
Console.WriteLine(" Connecting to Authenticate service... ");
NetNamedPipeBinding myBinding = new NetNamedPipeBinding("Authentication.Endpoint"); ;
EndpointAddress myEndpoint = new EndpointAddress("net.pipe://localhost/authentication"); ;
var myChannelFactory = new ChannelFactory<IAuthentication>(myBinding, myEndpoint);
IAuthentication proxy = myChannelFactory.CreateChannel();
Person person = proxy.GetDeveloper();
Console.WriteLine(String.Format("GetDeveloper OK : {0} {1} ", person.FirstName, person.LastName));
person = proxy.GetArchitect();
Console.WriteLine(String.Format("GetArchitect OK : {0} {1} ", person.FirstName, person.LastName));
and the output is:
Connecting to Authenticate service...
GetDeveloper OK : Asghar Panahy
There was an error reading from the pipe: De pipe is beƫindigd. (109, 0x6d).
Could anyone please help me on this?
Asghar
I know the question is a bit old but I might still help someone. I have the same problem now with the named-pipe binding(getting the same error).
The problem here is the return of the Employee derived class.
There is a good explanation here
So it should work fine if you apply the KnownTypeAttribute:
[DataContract]
[KnownType(Employee)]
public class Person
...