Client WCF DataContract has empty/null values from service - wcf

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()]

Related

POST null because property type does not match?

I have a asp .net core API with simple REST methods like this:
[Route("api/[controller]")]
public class SomeController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Insert([FromBody] ItemClass newItem)
{
[...]
return Ok();
}
}
The model class is pretty simple as well:
public class ItemClass
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
}
When I POST this JSON to the service
{
"id": null,
"name": "Some name",
"description": "Some description",
"isActive": null
}
the method is called with newItem set to null. No error, no exception, the the object was just null. It took me quite a while to figure out the parameter isActive was the cause of the problem. In the class I defined it as bool, but JSON defined it as null. When I either change the value to true or false or when I leave it out completely or when I change the parameter to bool? it works again. The object is deserialized correctly.
I'm used to asp net core APIs to be very resilient. Usually when a parameter is simply set to its default when it can not be parsed correctly from the message.
But why does the whole object become null here?
Is there at least a way to tell asp net core to throw an exception when the deserialization fails?
When null is mapped to the bool, resulting in a type mismatch exception, it will cause the whole object become null.
If this api is 3.x, it will trigger 400. But in 2.x and 5.0, they have no exception. You can handle the null value through NewtonSoft.json.
A simple way to handel it with this configuration, it can assign a default value to bool, but it doesn't trigger exception.
In 2.x
services.AddMvc()
.AddJsonOptions(options=>
{
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
5.0
services.AddControllers()
.AddNewtonsoftJson(option=>
{
option.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
If must return an error, you have to use a custom model binding.
public class CustomBindClassBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var itemClass = new ItemClass();
using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body))
{
var body = reader.ReadToEndAsync();
var mydata = JsonConvert.DeserializeObject<JObject>(body.Result);
if(mydata["isActive"].ToString()==""|| mydata["isActive"].ToString() == null)
{
bindingContext.Result= ModelBindingResult.Failed();
throw new Exception("isActive is not correct");
}
else
{
itemClass.Id = mydata["id"].ToString();
itemClass.Name = mydata["name"].ToString();
itemClass.Description = mydata["description"].ToString();
itemClass.IsActive = bool.Parse(mydata["isActive"].ToString());
bindingContext.Result = ModelBindingResult.Success(itemClass);
}
}
return Task.CompletedTask;
}
}
In action
[HttpPost]
public async Task<IActionResult> Insert([FromBody][ModelBinder(typeof(CustomBindClassBinder))] ItemClass newItem)
{
return Ok(newItem);
}

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.

Return Entity Framework objects over WCF

We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method

Sending complex type as a parameter in SOAP message

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.

wcf generic datacontract does not get serialized

I am having the following data structure:
[DataContract]
public class OperationResult<T>
{
public OperationResult() { }
[DataMember]
public Int32 OpResult
{
get;set;
}
[DataMember]
public IList<T> OperationResults
{
get;set;
}
public static OperationResult<T> Success(IList<T> results, int numberOfChangedObjects)
{
OperationResult<T> result = new OperationResult<T>();
result.OpResult = 1;
result.OperationResults = results;
return result;
}
}
When I update the service reference, the class does not get serialized. In the service I am using a so-called closed generic type
eg.
[OperationContract]
public OperationResult<Int32> SometTestMethod()
{
return new OperationResult<Int32>
{
OpResult = 1,
OperationResults = new List<Int32> {1, 2, 3}
};
}
The method is exposed, but the return type OperationResult in this case is not accesible.
What am I doing wrog?
Thanks
I just realized. The reason I didn't find the type is because I was looking for an OperationResult. As it got serialized , it was named OperationResultOfInt.