I get this error from the SendObject method when I try to serialize an object just as the NetworkComms example says to do it. This seems like something silly, but I can't figure out what I need to do, and I don't think implementing the IExplicitlySerialize interface is the answer. Here is my calling method and the serialized class:
public static void SendTestPacket()
{
var message = "This is a test packet";
NetworkComms.SendObject("PacketPrintToConsole", "192.168.1.105", 5614, new PacketPrintToConsole(message));
}
[ProtoContract]
public class PacketPrintToConsole
{
[ProtoMember(1)]
public string Message { get; set; }
public PacketPrintToConsole() { }
public PacketPrintToConsole(string message)
{
this.Message = message;
}
}
Maybe you already figured it out, but for everyone else finding this here (like me). Here´s the answer.
That error message tells you, that you have to define a serializer.
You have an object you want to send, but you didn´t tell NetworkComms what serializer to use.
So you can either implement IExplicitlySerialize in your object or use a serializer that has already been created for protobuf-net.
You need to use the NetworkCommsDotNet.DPSBase.ProtobufSerializer.
You have to reference the ProtobufSerializer.dll compiled from the NetworkComms source you can get on github and then define the SendReceiveOptions.
Example:
SendReceiveOptions customSendReceiveOptions = new SendReceiveOptions<ProtobufSerializer>();
ConnectionInfo connectionInfo = new ConnectionInfo("192.168.1.105", 5614);
TCPConnection serverConnection = TCPConnection.GetConnection(connectionInfo, customSendReceiveOptions);
var message = "This is a test packet";
serverConnection.SendObject("PacketPrintToConsole", new PacketPrintToConsole(message));
This has only to be done where the object will be serialized to be sent.
A receiving client just need´s to have protobuf-net with the same object class to deserialize into.
Example with SendReceiveObject to request a message.
SendReceiveOptions customSendReceiveOptions = new SendReceiveOptions<ProtobufSerializer>();
NetworkComms.AppendGlobalIncomingPacketHandler<int>("GetMessage", GetMessageRequest, customSendReceiveOptions);
The method to send the result:
private static void GetMessageRequest(PacketHeader packetheader, Connection connection, int incomingobject)
{
connection.SendObject("MessageReply", new MessageObject(message));
}
And on the client side then:
ConnectionInfo connectionInfo = new ConnectionInfo("192.168.2.105", 5614);
TCPConnection serverConnection = TCPConnection.GetConnection(connectionInfo);
MessageObject myMessageObject = serverConnection.SendReceiveObject<ImageWrap>("GetMessage", "MessageReply", 1000);
if (myMessageObject != null)
{
Console.WriteLine(myMessageObject.Message);
}
Related
I've seen several other posts here with the same problem, but none offer a solution. And what's really odd is that this works in dev, but not in prod.
I am submitting a message to a queue as follows:
public void QueueMessage(LeadSubmissionMessage message)
{
using (var queue = new MessageQueue(MessageQueuePath))
{
queue.DefaultPropertiesToSend.Recoverable = true; // always send as recoverable
queue.Send(message);
}
}
This is the LeadSubmissionMessage class:
[Serializable]
public class LeadSubmissionMessage
{
public long LeadId { get; set; }
public long UserId { get; set; }
public DateTime DateTime { get; set; }
}
This is the message, in raw text:
<?xml version="1.0"?>
<LeadSubmissionMessage xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LeadId>194018</LeadId>
<UserId>300</UserId>
<DateTime>2016-05-17T14:52:30.1484784Z</DateTime>
</LeadSubmissionMessage>
That all works fine. But on the receiving end, and only in production, when we do this:
body = message.Body;
It throws this error:
System.InvalidOperationException: Cannot deserialize the message passed as an argument. Cannot recognize the serialization format.
at System.Messaging.XmlMessageFormatter.Read(Message message)
at System.Messaging.Message.get_Body()
It works find in Dev and Staging. I'm trying to minimize and eliminate the points where things could be different, but I've run out of things to check. They are all running the same build version (release). Any MSMQ related config keys match (except for the obvious queue names and locations). One possible variation is the version of MSMQ installed on the machine? But I'm not sure how to check that. Would the OS make a difference? Can't imagine it would.
I'm using it in the following way:
private static MyMessage RecieveMessage()
{
if (!MessageQueue.Exists(QueueName))
{
return null;
}
using (var msmq = new MessageQueue(QueueName))
{
msmq.Formatter = new XmlMessageFormatter(new Type[] { typeof(MyMessage) });
var message = msmq.Receive();
return message != null && message.Body is MyMessage ? (MyMessage)message.Body : null;
}
}
Not sure what's the problem in your case, but you could try to make it similar to my approach and see if it's working for you.
EDIT : the moment I asked the question I thougt of trying something..
I've set XmlNamespace property on the request and that did the trick..
request.XmlNamespace = "http://musicbrainz.org/ns/mmd-2.0#";
But I don't really understand as to why...
Next problem is getting RestSharp to recognize xml atributes as object properties
I've been going over this for the most of the weekend and I just don't get it to work.
I'm trying to write a wrapper round a RestFul webservice (MusicBrainz). I'm testing with a simple example : get details of one artist and put it in a custom Artist object.
When I do a Execute on the RestClient it ends ok but my object properties are null..
But when I test the deserialization with the XmlDeserializer the objectproperties are filled (But not for properties that correspond to an attribute, but I'll tackle that later)
What happens between deserialization of the response and putting the object in response.data ?
Quite possible it is a "newbie" error I'm making as this are my first steps with RestSharp..
Help would be much appreciated..
Returnded xml :
<metadata>
<artist type="Group" id="f1548c5b-329e-4036-921c-02213a04b525">
<name>Uriah Heep</name>
<sort-name>Uriah Heep</sort-name>
<country>GB</country>
<life-span>
<begin>1970</begin>
</life-span>
</artist>
</metadata>
My class :
public class Artist
{
public int Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public string SortName { get; set; }
public string Country { get; set; }
}
In the following code output properties are filled
var output = xml.Deserialize<Artist>(response);
But the same response does not fill properties when calling
var response = client.Execute<T>(request);
Complete code (I've put the test code in the generic method for sake of simplicity) :
public T Execute<T>(RestRequest request) where T : new()
{
var client = new RestClient();
client.BaseUrl = BaseUrl;
client.Authenticator = null;
//does not fill properties
var response = client.Execute<T>(request);
if (response.ErrorException != null)
{
throw response.ErrorException;
}
var xml = new XmlDeserializer();
//fills properties
var output = xml.Deserialize<Artist>(response);
return response.Data;
}
This happens because Execute method, after receiving response, tries to negotiate it based on the request's RootElement and XmlNamespace properties and copies them to the XmlDeserializer.
Here's a code from RestClient:
handler.RootElement = request.RootElement;
handler.DateFormat = request.DateFormat;
handler.Namespace = request.XmlNamespace;
response.Data = handler.Deserialize<T>(raw);
If you pass a RestRequest with a mismatching XmlNamespace, RestSharp's XmlDeserializer (that uses XDocument behind the scenes) won't be able to map response XML to an object properties and you will get default/null values instead.
Now for default implementation (when you create XmlDeserializer manually), if you don't set a XmlNamespace, deserializer will do an auto-detection that basically ignores all namespaces in the response and maps all properties only by their names.
See source code from XmlDeserializer:
// autodetect xml namespace
if (!Namespace.HasValue())
{
RemoveNamespace(doc);
}
Taking all above into account, it's clear why it started working after you explicitly set XmlNamespace property to a matching namespace in your request object with this line:
request.XmlNamespace = "http://musicbrainz.org/ns/mmd-2.0#";
Execute method copied namespace into deserializer and it mapped XML to object appropriately.
I wanted to send one custom object in system.servicemodel.Channels.Message. Like
public class person
{
string Id;
string Name;
}
MessageVersion mv = MessageVersion.Create(Soap12);
String action = "Msg";
Message msg = Message.Create(mv, action, new person());
serviceref.ProcessMsg(msg) // this is my service reference in client
//when i tried to access this in Service like
person p = msg.GetBody<person>()
//I am getting an serialization exception
//I have the Person class on both client and service side
Can some one please help me in figure out my error?
Looks like you're looking for a DataContract:
using System.Runtime.Serialization;
[DataContract]
public class person
{
[DataMember]
string Id;
[DataMember]
string Name;
}
Check out Using Data Contracts for more information on DataContracts and WCF.
EDIT
Not sure if this will do the trick or not, but as I noted in my response to your comment, there's an overload of the CreateMessage method that takes an XmlObjectSerializer. MSDN docs on it are rather thin, but I think something like this might do it:
Message msg = Message.Create(mv, action, new person(), new DataContractSerializer(typeof(person)));
I haven't tested this, but at the least it may get you pointed in the right direction.
The DataContractSerializer will need to be supplied a DataContract (person in the first part of my answer).
It looks like serverside cannot receive the passed values, requestVersion.Ping is empty.
namespace Communication
{
public class DataForRequestVersion
{
public string Ping = "";
}
public class DataForResponseVersion
{
public string Pong = "";
public string Version = "";
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
Communication.DataForResponseVersion Version(Communication.DataForRequestVersion requestVersion);
}
//Server
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceImplementation : WCFSimple.Contract.IService
{
public Communication.DataForResponseVersion Version(Communication.DataForRequestVersion requestVersion)
{
//Here requestVersion.Ping is EMPTY
Communication.DataForResponseVersion responseVersion = new Communication.DataForResponseVersion();
responseVersion.Pong = requestVersion.Ping;
responseVersion.Version = "MyApp v" + Settings.version;
return responseVersion;
}
}
//Client makes a request here
Communication.DataForRequestVersion requestVersion = new Communication.DataForRequestVersion();
requestVersion.Ping = DateTime.Now.ToString(Settings.DayTimePreciseFormat);
//Here requestVersion.Ping has a value
Communication.DataForResponseVersion responseVersion =
Service.Version(requestVersion);
What am I missing?
UPDATE
My application works very well, both sides communicate by passing custom data classes without any problem. However I tried to modify test client which one only sends-receives current time as string and made its parameter a bit complex; from string to custom data class. Main solution's application can send Version request and receive the answer. So I think my little test client got a problem.
Here is the pastebin code:
2nd UPDATE:
Some moderator doesn't allow me to answer my own question, I don't know why, I found a very similar question and the guy answered his own too. To help others next time I'm explaining the reason; I used namespace instead of creating class...And I fixed:
//namespace Communication
public class Communication
You need to label your request (and response) classes with the [DataContract] attribute, and their properties with the [DataMember] attribute:
[DataContract]
public class DataForRequestVersion
{
[DataMember]
public string Ping = "";
}
try using [DataContract] on your Data...classes... and [DataMember] on their fields...
Change the fields in DataForRequestVersion DataForResponseVersion classes to properties. By Default DatacontractSerializer will serialize public properties.
I'm attempting to test a [MessageContract] class against an existing sample message, and I'm looking for a way to simplify development by reading the sample message file into an instance of my [MessageContract] class and seeing how it worked out (I'm dealing with a particularly complex contract here, of non-WCF origin).
My [MessageContract] class looks something like this:
[MessageContract(IsWrapped = true, WrapperName = "wrapper", WrapperNamespace = "somens")]
public class RequestMessage
{
[MessageHeader(Name = "HeaderElem", Namespace = "otherns")]
public XElement CorrelationTimeToLive { get; set; }
[MessageBodyMember(Name = "id", Namespace = "somens")]
public XElement id { get; set; }
}
I can read the file into an instance of the Message class, using code such as the following:
var xr = XmlReader.Create("sample_message.xml");
var msg = Message.CreateMessage(xr, int.MaxValue, MessageVersion.Soap12);
That's not particulary helpful, however, because it doesn't allow me to test my [MessageContract] class at all.
Somewhere in the guts of WCF is a system for turning this Message instance into an instance of a particular [MessageContract] class, but what is it?
I just learned how to do this the other day following a talk with a collegue. I think this is what you were asking to do.
namespace MessageContractTest
{
class Program
{
static void Main(string[] args)
{
string action = null;
XmlReader bodyReader = XmlReader.Create(new StringReader("<Example xmlns=\"http://tempuri.org/\"><Gold>109</Gold><Message>StackOverflow</Message></Example>"));
Message msg = Message.CreateMessage(MessageVersion.Default, action, bodyReader);
TypedMessageConverter converter = TypedMessageConverter.Create(typeof(Example), "http://tempuri.org/IFoo/BarOperation");
Example example = (Example)converter.FromMessage(msg);
}
}
[MessageContract]
public class Example
{
[MessageHeader]
public string Hello;
[MessageHeader]
public double Value;
[MessageBodyMember]
public int Gold;
[MessageBodyMember]
public string Message;
}
}
You will need to deserialize the XML into an instance of your data contract. This is what WCF is doing for you under the covers.
Here is a quick tutorial that will show you how to invoke the DataContractSerializer manually for your XML.