I got a big issue trying to make this work.
I have a WCF scenario, with callbacks, trying to send a Message that is a struct made of a string and an object.
Seems like the object can't be serialized.
I get the following error trying to call a function sending that data:
Type '<>f__AnonymousType01[System.String]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. See the Microsoft .NET Framework documentation for other supported types.
The code for my CLIENT IS:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.IO;
namespace WCFClient
{
[DataContract]
public struct Message
{
private string messageType;
private object param;
public Message(string _messageType, object _param)
{
// TODO: Complete member initialization
this.messageType = _messageType;
this.param = _param;
}
[DataMember]
public string type { get { return messageType; } set { messageType = type; } }
[DataMember]
public object obj { get { return param; } set { param = obj; } }
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(ICallbacks))]
public interface IMessageHandler
{
[OperationContract]
void HandleMessage(string value);
}
public interface ICallbacks
{
[OperationContract(IsOneWay = true)]
void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath);
[OperationContract(IsOneWay = true)]
void MyCallbackFunction(Message msg);
}
public class Callbacks : ICallbacks
{
public void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath)
{
Console.WriteLine("Callback Received: ");
}
public void MyCallbackFunction(Message msg)
{
Console.WriteLine("Callback Received: ");
}
}
class Program
{
static void Main(string[] args)
{
Callbacks myCallbacks = new Callbacks();
DuplexChannelFactory<IMessageHandler> pipeFactory =
new DuplexChannelFactory<IMessageHandler>(
myCallbacks,
new NetNamedPipeBinding(),
new EndpointAddress(
"net.pipe://localhost/PipeReverse"));
IMessageHandler pipeProxy =
pipeFactory.CreateChannel();
while (true)
{
string str = Console.ReadLine();
pipeProxy.HandleMessage(str);//send the type for example
}
}
}
}
And my SERVER code:
using System;
using System.ServiceModel;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Text;
namespace WCFServer
{
[DataContract]
public struct Message
{
private string messageType;
private object param;
public Message(string _messageType, object _param)
{
// TODO: Complete member initialization
this.messageType = _messageType;
this.param = _param;
}
[DataMember]
public string type { get { return messageType; } set { messageType = type; } }
[DataMember]
public object obj { get { return param; } set { param = obj; } }
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(ICallbacks))]
public interface IMessageHandler
{
[OperationContract]
void HandleMessage(string value);
}
public interface ICallbacks
{
[OperationContract(IsOneWay = true)]
void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath);
[OperationContract(IsOneWay = true)]
void MyCallbackFunction(Message msg);
}
public class StringReverser : IMessageHandler
{
public void HandleMessage(string value)//handle the type and do the request
{
ICallbacks callbacks = OperationContext.Current.GetCallbackChannel<ICallbacks>();
if (value.CompareTo("1") == 0)
{
callbacks.QueuePaths_Callback("path1", "path2", "path3", "path4", "path5");
}
else
{
Console.WriteLine("In the else");
//BinaryFormatter bformatter = new BinaryFormatter();
//MemoryStream stream = new MemoryStream();
//bformatter.Serialize(stream, new Message(value, new { PathCompleted = "path" }));
callbacks.MyCallbackFunction(new Message(value, new { PathCompleted = "path" }));
}
}
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(
typeof(StringReverser),
new Uri[]{
new Uri("net.pipe://localhost")
}))
{
host.AddServiceEndpoint(typeof(IMessageHandler),
new NetNamedPipeBinding(),
"PipeReverse");
host.Open();
Console.WriteLine("Service is available. " +
"Press <ENTER> to exit.");
Console.ReadLine();
host.Close();
}
}
}
}
Why do I have this issue with objects? They are annonymous I know but in the Types Supported by the Data Contract Serializer from MSDN!(http://msdn.microsoft.com/en-us/library/ms731923.aspx) they say .NET Framework primitive types. The following types built into the .NET Framework can all be serialized and are considered to be primitive types: Byte, SByte, Int16, Int32, Int64, UInt16, UInt32, UInt64, Single, Double, Boolean, Char, Decimal, Object, and String. so I don't know why I cant.
Thank you, I appreciate your help
Related
I have a message publisher that uses Microsoft.Azure.ServiceBus and need to replace it with Azure.Messaging.ServiceBus as it is now deprecated.
Here is the code:
using Microsoft.Azure.ServiceBus;
using Newtonsoft.Json;
using System;
using System.Text;
using System.Threading.Tasks;
namespace gbx.infra.ware.Services
{
public interface IMessagePublisher
{
public Task Publish<T>(T obj);
public Task Publish(string raw);
public Task<long> PublishScheduled<T>(T obj, DateTimeOffset time);
}
public class MessagePublisher : IMessagePublisher
{
private readonly ITopicClient _topicClient;
public MessagePublisher(ITopicClient topicClient)
{
_topicClient = topicClient;
}
public Task Publish<T>(T obj)
{
string objAsText = JsonConvert.SerializeObject(obj);
Message message = new Message(Encoding.UTF8.GetBytes(objAsText));
message.UserProperties["messageType"] = typeof(T).Name;
return _topicClient.SendAsync(message);
}
public Task Publish(string raw)
{
Message message = new Message(Encoding.UTF8.GetBytes(raw));
message.UserProperties["messageType"] = "Raw";
return _topicClient.SendAsync(message);
}
public Task<long> PublishScheduled<T>(T obj, DateTimeOffset time)
{
string objAsText = JsonConvert.SerializeObject(obj);
Message message = new Message(Encoding.UTF8.GetBytes(objAsText));
message.UserProperties["messageType"] = typeof(T).Name;
return _topicClient.ScheduleMessageAsync(message, time);
}
}
}
Is there a simple way i can make the change? I can't find any info on this.
The publisher is registered like this:
services.AddSingleton<ITopicClient>(x => new TopicClient(Configuration["ServiceBus:ConnectionString"], Configuration["ServiceBus:TopicName"]));
services.AddSingleton<IMessagePublisher, MessagePublisher>();
And injected into as needed.
What i want to do is to change the code in the Message Publisher only so that no changes need to be done in the code where it is used.
If you would simply use the migration guide you would end up with
public interface IMessagePublisher
{
public Task Publish<T>(T obj);
public Task Publish(string raw);
public Task<long> PublishScheduled<T>(T obj, DateTimeOffset time);
}
public class MessagePublisher : IMessagePublisher
{
private readonly ServiceBusSender _serviceBusSender;
public MessagePublisher(ServiceBusSender serviceBusSender)
{
_serviceBusSender = serviceBusSender;
}
public Task Publish<T>(T obj)
{
string objAsText = JsonConvert.SerializeObject(obj);
ServiceBusMessage message = new ServiceBusMessage(Encoding.UTF8.GetBytes(objAsText));
message.ApplicationProperties["messageType"] = typeof(T).Name;
return _serviceBusSender.SendMessageAsync(message);
}
public Task Publish(string raw)
{
ServiceBusMessage message = new ServiceBusMessage(Encoding.UTF8.GetBytes(raw));
message.ApplicationProperties["messageType"] = "Raw";
return _serviceBusSender.SendMessageAsync(message);
}
public Task<long> PublishScheduled<T>(T obj, DateTimeOffset time)
{
string objAsText = JsonConvert.SerializeObject(obj);
ServiceBusMessage message = new ServiceBusMessage(Encoding.UTF8.GetBytes(objAsText));
message.ApplicationProperties["messageType"] = typeof(T).Name;
return _serviceBusSender.ScheduleMessageAsync(message, time);
}
}
To register the MessagePublisher try:
services.AddSingleton<IMessagePublisher>(p =>
new MessagePublisher(
new ServiceBusClient(Configuration["ServiceBus:ConnectionString"]).CreateSender(Configuration["ServiceBus:TopicName"])));
Note: there are other ways to register the message publisher, see How to register ServiceBusClient for dependency injection?. You might need some minor modifications of MessagePublisher though.
I want to be able to catch all unhandled exceptions and return the expected DTO but with some error information filled in. For example
public class CreateFooRequest
{
public string Name { get; set; }
}
public class CreateFooResponse
{
public Foo Created { get; set; }
public string Error { get; set; } // If call was successful then this will be null
public string Detail { get; set; }
}
public interface IFooService
{
CreateFooResponse Create(CreateFooRequest request);
}
public ErrorHandler: IErrorHandler
{
public bool Handle(Exception ex)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// Some how figure out that IFooService.Create was called.
// Inspect the method signature and see that there is an input called CreateFooRequest
// Use reflection to initialize response objects that will replace the "Request" with "Response"
var response = new CreateFooResponse();
response.Error = error.GetType().Name;
// I think i need one of the following overloads
fault = Message.CreateMessage(version, action, response);
}
}
Is doing something like this even possible? We are using NetTCP as our binding if that makes a difference.
IErrorHandler is designed to generate fault contracts. If you don't want to return a fault, you are better off taking an interceptor approach and using a the IOperationInvoker extension point instead.
The operation invoker is the part of the WCF framework that actually calls the service method. When you extend it, you can effectively "intercept" the call to the service. Note the Invoker does have responsibilities. You can't simply replace the WCF invoker implementation. Instead you chain them (see below).
At a high level the steps are:
Create an IOperationInvoker.Invoke() that implements a try/catch block. Catch the exceptions you want to be Response messages instead of FaultExceptions.
Create an IOperationBehavior (that's optionally also an attribute) to apply the behavior to your service.
The approach has a couple of advantages:
The exception is caught before WCF sees it.
In a IOperationBehavior.ApplyDispatchBehavior() you have access to the OperationDescription at the time of service start up. If you save this in the Invoker, you don't need to use reflection to capture the method's return type.
The IOperationBehavior.Validate() allows robust checks to ensure that the return type can actually be processed.
Below is a complete windows console application that demonstrates the approach. Paste it into Visual Studio, add the obvious assembly references, and run it.
My apologizes for the large amount of code and excessive use of base classes. I'm cutting down from actual production code.
If you want a better understanding of the IOperationInvoker extension point and what the example InvokerBase class is doing see Carlos Figueira's blog.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Threading.Tasks;
namespace WcfErrorResponse
{
/// <summary>
/// Provides a base IOperationInvoker implementation that stores and passes through calls to the exisiting (old) invoker
/// </summary>
public abstract class InvokerBase : IOperationInvoker
{
private readonly IOperationInvoker m_OldInvoker;
protected IOperationInvoker OldInvoker
{
get { return m_OldInvoker; }
}
public InvokerBase(IOperationInvoker oldInvoker)
{
m_OldInvoker = oldInvoker;
}
public virtual object[] AllocateInputs()
{
return OldInvoker.AllocateInputs();
}
public virtual object Invoke(object instance, object[] inputs, out object[] outputs)
{
return OldInvoker.Invoke(instance, inputs, out outputs);
}
public virtual IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return OldInvoker.InvokeBegin(instance, inputs, callback, state);
}
public virtual object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return OldInvoker.InvokeEnd(instance, out outputs, result);
}
public virtual bool IsSynchronous
{
get { return OldInvoker.IsSynchronous; }
}
}
/// <summary>
/// Base implementation for a Method level attribte that applies a <see cref="InvokerBase"/> inherited behavior.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public abstract class InvokerOperationBehaviorAttribute : Attribute, IOperationBehavior
{
protected abstract InvokerBase CreateInvoker(IOperationInvoker oldInvoker, OperationDescription operationDescription, DispatchOperation dispatchOperation);
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{ }
public virtual void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
// chain invokers.
IOperationInvoker oldInvoker = dispatchOperation.Invoker;
dispatchOperation.Invoker = CreateInvoker(oldInvoker, operationDescription, dispatchOperation);
}
public virtual void Validate(OperationDescription operationDescription)
{
return;
}
}
public class ResponseExceptionInvoker : InvokerBase
{
private Type returnType;
public ResponseExceptionInvoker(IOperationInvoker oldInvoker, OperationDescription operationDescription)
: base(oldInvoker)
{
// save the return type for creating response messages
this.returnType = operationDescription.GetReturnType();
if (this.returnType == null)
{
throw new InvalidOperationException("The operation '" + operationDescription.SyncMethod.DeclaringType.Name + "' does not define a return type.");
}
}
public override object Invoke(object instance, object[] inputs, out object[] outputs)
{
object returnedValue = null;
object[] outputParams = new object[] { };
outputs = new object[] { };
try
{
returnedValue = OldInvoker.Invoke(instance, inputs, out outputParams);
outputs = outputParams;
return returnedValue;
}
catch (Exception ex)
{
Logger.Debug("ResponseExceptionInvoker() - Caught Exception. A Response Message will be returned. Message='" + ex.Message + "'");
// there was an excpetion. Do not assign output params... their state is undefined.
//outputs = outputParams;
try
{
// assumes the behavior only used for return types that inherit from Response, as verified by ResponseExceptionOperationBehaviorAttribute.Validate()
Response response = (Response)Activator.CreateInstance(this.returnType);
response.Success = false;
response.ErrorMessage = ex.Message;
return response;
}
catch (Exception exCreateResponse)
{
// Log that the Response couldn't be created and throw the original exception.
// Probably preferable to wrap and throw.
Logger.Error("Caught ResponseException, but unable to create the Response object. Likely indicates a bug or misconfiguration. Exception will be rethrown." + exCreateResponse.Message);
}
throw;
}
}
}
public class ResponseExceptionOperationBehaviorAttribute : InvokerOperationBehaviorAttribute
{
protected override InvokerBase CreateInvoker(IOperationInvoker oldInvoker, OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
return new ResponseExceptionInvoker(oldInvoker, operationDescription);
}
public override void Validate(OperationDescription operationDescription)
{
// validate that this attribute can be applied to the service behavior.
Type returnType = operationDescription.GetReturnType();
if (!typeof(Response).IsAssignableFrom(returnType))
{
throw new InvalidOperationException("'" + returnType.FullName + "' does not inherit from '" + typeof(Response).FullName +
"'. ImplicitResponse behavior applied to '" + operationDescription.SyncMethod.DeclaringType.Name + "." + operationDescription.Name +
"' requires the method return type inherit from '" + typeof(Response).FullName);
}
}
}
static class OperationDescriptionExtensions
{
public static Type GetReturnType(this OperationDescription operationDescription)
{
if (operationDescription.SyncMethod == null)
throw new InvalidOperationException("These behaviors have only been tested with Sychronous methods.");
// !! Warning: This does NOT work for Asynch or Task based implementations.
System.Reflection.MethodInfo method = operationDescription.SyncMethod ?? operationDescription.EndMethod;
return method.ReturnType;
}
}
// When not using FaultContracts, return success/fail as a part of all responses via some base class properties.
[DataContract]
public class Response
{
[DataMember]
public bool Success { get; set; }
[DataMember]
public string ErrorMessage { get; set; }
}
public class ChildResponse : Response
{
[DataMember]
public string Foo { get; set; }
}
[DataContract]
public class Request
{
[DataMember]
public string Name { get; set; }
}
[ServiceContract]
public interface ISimple
{
[OperationContract]
ChildResponse Work(Request request);
[OperationContract]
ChildResponse Fail(Request request);
}
public class SimpleService : ISimple
{
public ChildResponse Work(Request request) {
return new ChildResponse() { Success = true };
}
[ResponseExceptionOperationBehavior]
public ChildResponse Fail(Request request)
{
throw new NotImplementedException("This method isn't done");
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
simpleHost.Open();
ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
ISimple proxy = factory.CreateChannel();
Logger.Debug("Calling Work...");
var response1 = proxy.Work(new Request() { Name = "Foo" });
Logger.Debug("Work() returned Success=" + response1.Success + " message='" + response1.ErrorMessage + "'");
Logger.Debug("Calling Fail...");
var response2 = proxy.Fail(new Request() { Name = "FooBar" });
Logger.Debug("Fail() returned Success=" + response2.Success + " message='" + response2.ErrorMessage + "'");
Console.WriteLine("Press ENTER to close the host.");
Console.ReadLine();
((ICommunicationObject)proxy).Shutdown();
simpleHost.Shutdown();
}
}
public static class CommunicationObjectExtensions
{
static public void Shutdown(this ICommunicationObject obj)
{
try
{
obj.Close();
}
catch (Exception ex)
{
Console.WriteLine("Shutdown exception: {0}", ex.Message);
obj.Abort();
}
}
}
public static class Logger
{
public static void Debug(string message) { Console.WriteLine(message); }
public static void Error(string message) { Console.WriteLine(message); }
}
}
In your place, I would implement this as simple as possible by using general try/catch block. Using custom exception handler to observe the invoked service method and create a corresponding response using reflection looks like an overkill for me.
Make your life easier:
public CreateFooResponse Create(CreateFooRequest request)
{
try
{
// Create Foo
var foo = CreateFoo();
// Return successful CreateFooResponse
return new CreateFooResponse
{
Created = foo,
Error = null,
Detail = "Created successfully"
};
}
catch (Exception ex)
{
// Return CreateFooResponse with an error
return new CreateFooResponse
{
Created = null,
Error = CreateError(ex),
Detail = "Unable to create Foo."
};
}
}
A good example of using custom WCF error handler is to log the error, convert it to the FaultContract and return to the caller. You have a different scenario and I would suggest a different approach.
First of all, I would like to say that it is much better to use faultcontracts. Below I will give an example for service GetDataUsingDataContract:
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
[DataContract]
public class CompositeType
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
Then, you create a bodyWriter equivalent to a normal response:
public class MyBodyWriter : BodyWriter
{
public CompositeType CompositeType { get; private set; }
public MyBodyWriter(CompositeType composite)
: base(false)
{
CompositeType = composite;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("GetDataUsingDataContractResponse", "http://tempuri.org/");
writer.WriteStartElement("GetDataUsingDataContractResult");
writer.WriteAttributeString("xmlns", "a", null, "http://schemas.datacontract.org/2004/07/WcfService1");
writer.WriteAttributeString("xmlns", "i", null, "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteStartElement("a:BoolValue");
writer.WriteString(CompositeType.BoolValue.ToString().ToLower());
writer.WriteEndElement();
writer.WriteStartElement("a:StringValue");
writer.WriteString(CompositeType.StringValue);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
And finally, you use it in your IErrorHandler:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// TODO: parse error and gets response
var response = new CompositeType {BoolValue = true, StringValue = "a"};
fault = Message.CreateMessage(version, "http://tempuri.org/", new MyBodyWriter(response));
}
I did a test and I can get a correct answer if the service throws an exception or if the service responds normally:
var response1 = client.GetDataUsingDataContract(null);
var response2 = client.GetDataUsingDataContract(new CompositeType { StringValue = "a", BoolValue = true });
I have a datacontact with many members that has a custom class
I would like to force a new instance if the property is null on deserialization.
is there a way to do that?
If your are using DataContract serialization then you can override its default behaviour using the OnDeserialized attribute.
From MSDN: When applied to a method, specifies that the method is called during deserialization of an object in an object graph. The order of deserialization relative to other objects in the graph is non-deterministic.
Here is my sample code:
namespace MySpace
{
public class MyCustomClass
{
public string MyStrData { get; set; }
}
[DataContract]
public class Data
{
[DataMember]
public int mInt;
[DataMember]
public MyCustomClass MyCustonObj;
[OnDeserialized]
void OnDeserialized(StreamingContext c)
{
if (MyCustonObj == null)
{
MyCustonObj = new MyCustomClass();
MyCustonObj.MyStrData = "Overridden in serialization";
}
}
[OnDeserializing]
void OnDeserializing(StreamingContext c)
{
if (MyCustonObj == null)
{
MyCustonObj = new MyCustomClass();
MyCustonObj.MyStrData = "Overridden in deserializing";
}
}
[OnSerialized]
void OnSerialized(StreamingContext c)
{
// if you wan to do somehing when serialized here or just remove them
}
[OnSerializing]
void OnSerializing(StreamingContext c)
{
// if you wan to do somehing during serializing here or just remove them
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
Data Method(Data dd);
}
public class Service : IService
{
public Data Method(Data dd)
{
return dd;
}
}
class Program
{
static void Main(string[] args)
{
string Url = "http://localhost:8000/";
Binding binding = new BasicHttpBinding();
ServiceHost host = new ServiceHost(typeof(Service));
host.AddServiceEndpoint(typeof(IService), binding, Url);
host.Open();
ChannelFactory<IService> fac = new ChannelFactory<IService>(binding);
fac.Open();
IService proxy = fac.CreateChannel(new EndpointAddress(Url));
Data d = new Data();
d.mInt = 5;
Console.WriteLine("Data before calling service " + d.mInt);
Console.WriteLine("Data before calling service " + (d.MyCustonObj == null ? "null" : d.MyCustonObj.MyStrData));
d = proxy.Method(d);
fac.Close();
host.Close();
Console.WriteLine("Data after calling service " + d.mInt);
Console.WriteLine("Data after calling service " + d.MyCustonObj.MyStrData);
Console.ReadLine();
}
}
I have two apps, app1.cs and app2.cs (codes below). In addition I also have a dll I extracted from refer.cs(code below). When I compile app1.cs(which sends a measurement object) I get the following exception:
Unhandled Exception: RabbitMQ.Client.Exceptions.OperationInterruptioedException
I can't see how the connection is interrupted. Do you see where the problem is caused at?
Regards,
Demi
//refer.cs from which refer.dll is created
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace refer
{
//start alternate serialization
public static class AltSerialization
{
public static byte[] AltSerialize(Measurement m)
{
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
bf.Serialize(ms, m);
return ms.GetBuffer();
}
}
public static Measurement AltDeSerialize(byte[] seriM)
{
using (var stream = new MemoryStream( seriM ))
{
BinaryFormatter bf = new BinaryFormatter();
bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
return (Measurement)bf.Deserialize(stream);
}
}
}
//end alternte serialization
[Serializable] //This attribute sets class to be serialized
public class Measurement : ISerializable
{
[NonSerialized] public int id;
public int time; //timestamp
public double value;
public Measurement()
{
id = 1;
time = 12;
value = 0.01;
}
public Measurement(int _id, int _time, double _value)
{
id = _id;
time = _time;
value = _value;
}
//Deserialization constructor
public Measurement(SerializationInfo info, StreamingContext ctxt)
{
//Assign the values from info to the approporiate properties
Console.WriteLine("DeSerialization construtor called.");
time = (int)info.GetValue("MeasurementTime", typeof(int));
value = (double)info.GetValue("MeasurementValue", typeof(double));
}
//Serialization function
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
// Custom name-value pair
// Values must be read with the same name they're written
info.AddValue("MeasurementTime", time);
info.AddValue("MeasurementValue", value);
}
}
}
//MB1.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using UtilityMeasurement;
public interface IMessageBus
{
string MsgSys // Property 1
{
get;
set;
}
void write (Measurement m1);
Measurement read();
void publish(string queue);
void subscribe(string queue);
}
public class Rabbit : IMessageBus
{
// Implementation of methods for Rabbit class go here
private List<string> publishQ = new List<string>();
private List<string> subscribeQ = new List<string>();
public void write ( Measurement m1 )
{
byte[] body = Measurement.AltSerialize( m1 );
IConnection connection = factory.CreateConnection();
IModel channel = connection.CreateModel();
foreach (string queue in publishQ)
{
channel.BasicPublish("", queue, null, body);
Console.WriteLine("\n [x] Sent to queue {0}.", queue);
}
}
public void publish(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null); //durable=true
publishQ.Add(queueName); //and, add it the list of queue names to publish to
}
public Measurement read()
{
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
foreach (string queue in subscribeQ)
{
channel.BasicConsume(queue, true, consumer);
}
System.Console.WriteLine(" [*] Waiting for messages." +
"To exit press CTRL+C");
BasicDeliverEventArgs ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
return Measurement.AltDeSerialize(ea.Body);
}
public void subscribe(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null);
subscribeQ.Add(queueName);
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
public Rabbit(string _msgSys) //Constructor
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
System.Console.WriteLine("\nMsgSys: RabbitMQ");
MsgSys = _msgSys;
}
}
public class Zmq : IMessageBus
{
public void write ( Measurement m1 )
{
//
}
public Measurement read()
{
//
return null;
}
public void publish(string queue)
{
//
}
public void subscribe(string queue)
{
//
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
// Implementation of methods for Zmq class go here
public Zmq(string _msgSys) //Constructor
{
System.Console.WriteLine("ZMQ");
MsgSys = _msgSys;
}
}
public class MessageBusFactory
{
public static IMessageBus GetMessageBus(string MsgSysName)
{
switch ( MsgSysName )
{
case "Zmq":
return new Zmq(MsgSysName);
case "Rabbit":
return new Rabbit(MsgSysName);
default:
throw new ArgumentException("Messaging type " +
MsgSysName + " not supported." );
}
}
}
public class MainClass
{
public static void Main()
{
//Asks for the message system
System.Console.WriteLine("\nEnter name of messageing system: ");
System.Console.WriteLine("Usage: [Rabbit] [Zmq]");
string MsgSysName = (System.Console.ReadLine()).ToString();
//Create a new Measurement message
Measurement m1 = new Measurement(2, 2345, 23.456);
//Declare an IMessageBus instance:
//Here, an object of the corresponding Message System
// (ex. Rabbit, Zmq, etc) is instantiated
IMessageBus obj1 = MessageBusFactory.GetMessageBus(MsgSysName);
System.Console.WriteLine("\nA {0} object is now created.", MsgSysName);
System.Console.WriteLine("With Test message:\n ID: {0}", m1.id);
System.Console.WriteLine(" Time: {0}", m1.time);
System.Console.WriteLine(" Value: {0}", m1.value);
// Ask queue name and store it
System.Console.WriteLine("Enter a queue name to publish the message to: ");
string QueueName = (System.Console.ReadLine()).ToString();
obj1.publish( QueueName );
System.Console.WriteLine("Enter another queue name: ");
QueueName = (System.Console.ReadLine()).ToString();
obj1.publish( QueueName );
// Write message to the queue
obj1.write( m1 );
}
}
//MB2.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using UtilityMeasurement;
public interface IMessageBus
{
string MsgSys // Property 1
{
get;
set;
}
void write (Measurement m1);
Measurement read();
void publish(string queue);
void subscribe(string queue);
}
public class Rabbit : IMessageBus
{
// Implementation of methods for Rabbit class go here
private List<string> publishQ = new List<string>();
private List<string> subscribeQ = new List<string>();
public void write ( Measurement m1 )
{
byte[] body = Measurement.AltSerialize( m1 );
IConnection connection = factory.CreateConnection();
IModel channel = connection.CreateModel();
foreach (string queue in publishQ)
{
channel.BasicPublish("", queue, null, body);
Console.WriteLine("\n [x] Sent to queue {0}.", queue);
}
}
public void publish(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null); //durable=true
publishQ.Add(queueName); //and, add it the list of queue names to publish to
}
public Measurement read()
{
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
foreach (string queue in subscribeQ)
{
channel.BasicConsume(queue, true, consumer);
}
System.Console.WriteLine(" [*] Waiting for messages." +
"To exit press CTRL+C");
BasicDeliverEventArgs ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
return Measurement.AltDeSerialize(ea.Body);
}
public void subscribe(string queueName)
{
channel.QueueDeclare(queueName, true, false, false, null);
subscribeQ.Add(queueName);
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
public Rabbit(string _msgSys) //Constructor
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
System.Console.WriteLine("\nMsgSys: RabbitMQ");
MsgSys = _msgSys;
}
}
public class Zmq : IMessageBus
{
public void write ( Measurement m1 )
{
//
}
public Measurement read()
{
//
return null;
}
public void publish(string queue)
{
//
}
public void subscribe(string queue)
{
//
}
public static string MsgSysName;
public string MsgSys
{
get
{
return MsgSysName;
}
set
{
MsgSysName = value;
}
}
// Implementation of methods for Zmq class go here
public Zmq(string _msgSys) //Constructor
{
System.Console.WriteLine("ZMQ");
MsgSys = _msgSys;
}
}
public class MessageBusFactory
{
public static IMessageBus GetMessageBus(string MsgSysName)
{
switch ( MsgSysName )
{
case "Zmq":
return new Zmq(MsgSysName);
case "Rabbit":
return new Rabbit(MsgSysName);
default:
throw new ArgumentException("Messaging type " +
MsgSysName + " not supported." );
}
}
}
public class MainClass
{
public static void Main()
{
//Asks for the message system
System.Console.WriteLine("\nEnter name of messageing system: ");
System.Console.WriteLine("Usage: [Rabbit] [Zmq]");
string MsgSysName = (System.Console.ReadLine()).ToString();
//Declare an IMessageBus instance:
//Here, an object of the corresponding Message System
// (ex. Rabbit, Zmq, etc) is instantiated
IMessageBus obj1 = MessageBusFactory.GetMessageBus(MsgSysName);
System.Console.WriteLine("\nA {0} object is now created.", MsgSysName);
System.Console.WriteLine("Enter a queue to subscribe to: ");
string QueueName = (System.Console.ReadLine()).ToString();
obj1.subscribe( QueueName );
//Create a new Measurement object m2
Measurement m2 = new Measurement();
//Read message into m2
m2 = obj1.read();
m2.id = 11;
System.Console.WriteLine("\nMessage received from queue {0}:\n ID: {1}",QueueName, m2.id);
System.Console.WriteLine(" Time: {0}", m2.time);
System.Console.WriteLine(" Value: {0}", m2.value);
}
}
I just created a vanilla C# VS2010 Console application project with the Refer.cs and App1.cs in the same project.
I made the following changes:
Added RabbitMQ.Client.dll
Removed the AssemblyVersion attributes
Added string[] args to the Main method in App1.cs
Also, I changed:
factory.HostName = "localhost";
To this:
factory.HostName = "192.168.56.101";
Which is the ip address to my VirtualBox Ubuntu VM running rabbitmq-server. There was no exception thrown, and the message successfully was received on the server.
All signs point to server configuration with what is given. My guess is either your rabbitmq-server is not running at all, it's not running on localhost, or there is some kind of connectivity issue with port 5672.
I have a value object class (UserData) that is marked as Serializable and contains another class (StringType) that is marked as Serializable and implements ISerializable so that it can return singleton instances. The StringType class serializes and deserializes fine on it's own, but when it is used as a property on another object that is marked as Serializable, I get an exception truing to convert from the helper class deserialize into a singleton.
Object of type 'Spring2.Core.Test.Serialization.StringType_DEFAULT' cannot be converted to type 'Spring2.Core.Test.Serialization.StringType'.
I am using a BinaryFormatter and need to use that so that I can store this UserData object in an ASP.NET session using SQL server storage.
Here is a very stripped down version of the StringType class as well as some tests that show that serialization/deserialization work for StringType by itself, but not when as a field on UserData.
StringType:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace Spring2.Core.Test.Serialization {
public enum TypeState : short {
DEFAULT,
VALID,
UNSET
}
[Serializable]
public struct StringType : ISerializable {
private string myValue;
private TypeState myState;
public static readonly StringType DEFAULT = new StringType(TypeState.DEFAULT);
public static readonly StringType UNSET = new StringType(TypeState.UNSET);
private StringType(TypeState state) {
myState = state;
myValue = "";
}
public StringType(String s) {
myValue = s;
myState = TypeState.VALID;
}
public bool IsValid {
get { return myState == TypeState.VALID; }
}
public bool IsDefault {
get { return myState == TypeState.DEFAULT; }
}
public bool IsUnset {
get { return myState == TypeState.UNSET; }
}
public override string ToString() {
return IsValid ? this.myValue : myState.ToString();
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
StringType(SerializationInfo info, StreamingContext context) {
myValue = (System.String)info.GetValue("myValue", typeof(System.String));
myState = (TypeState)info.GetValue("myState", typeof(TypeState));
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
if (this.Equals(DEFAULT)) {
info.SetType(typeof(StringType_DEFAULT));
} else if (this.Equals(UNSET)) {
info.SetType(typeof(StringType_UNSET));
} else {
info.SetType(typeof(StringType));
info.AddValue("myValue", myValue);
info.AddValue("myState", myState);
}
}
}
[Serializable]
public class StringType_DEFAULT : IObjectReference {
public object GetRealObject(StreamingContext context) {
return StringType.DEFAULT;
}
}
[Serializable]
public class StringType_UNSET : IObjectReference {
public object GetRealObject(StreamingContext context) {
return StringType.UNSET;
}
}
}
Tests:
using System;
using NUnit.Framework;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace Spring2.Core.Test.Serialization {
/// <summary>
/// Tests for BooleanType
/// </summary>
[TestFixture]
public class DataTypeSerializationTest {
[Test]
public void ShouldBinarySerializeStringTypeWithValue() {
BinaryFormatter binaryFmt = new BinaryFormatter();
StringType s = new StringType("foo");
FileStream fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
binaryFmt.Serialize(fs, s);
fs.Close();
Console.WriteLine("Original value: {0}", s.ToString());
// Deserialize.
fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
StringType s2 = (StringType)binaryFmt.Deserialize(fs);
Console.WriteLine("New value: {0}", s2.ToString());
fs.Close();
Assert.AreEqual(s.ToString(), s2.ToString());
}
[Test]
public void ShouldBinarySerializeStringTypeUnset() {
BinaryFormatter binaryFmt = new BinaryFormatter();
StringType s = StringType.UNSET;
FileStream fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
binaryFmt.Serialize(fs, s);
fs.Close();
Console.WriteLine("Original value is UNSET: {0}", s.IsUnset);
// Deserialize.
fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
StringType s2 = (StringType)binaryFmt.Deserialize(fs);
Console.WriteLine("new value is UNSET: {0}", s2.IsUnset);
fs.Close();
Assert.IsTrue(s2.IsUnset);
}
[Test]
public void ShouldDeserializeDataObject() {
BinaryFormatter binaryFmt = new BinaryFormatter();
UserData u = new UserData();
FileStream fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
binaryFmt.Serialize(fs, u);
fs.Close();
Console.WriteLine("Original value is UNSET: {0}", u.Name.IsUnset);
// Deserialize.
fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
Object o = binaryFmt.Deserialize(fs);
UserData u2 = (UserData)o;
Console.WriteLine("new value is UNSET: {0}", u2.Name.IsUnset);
fs.Close();
Assert.IsTrue(Object.Equals(u, u2));
}
}
[Serializable]
public class UserData {
private StringType name = StringType.DEFAULT;
public StringType Name {
get { return name; }
set { name = value; }
}
}
}
Any help would be MUCH appreciated!
Cort
Cort you should change your IObjectReference implementations to return a struct. The third unit test will fail, but it fails on the assertion of the Objects being equal, rather than throwing the type mismatch exception.
[Serializable]
public struct StringType_DEFAULT : IObjectReference {
public object GetRealObject(StreamingContext context) {
return StringType.DEFAULT;
}
}
[Serializable]
public struct StringType_UNSET : IObjectReference {
public object GetRealObject(StreamingContext context) {
return StringType.UNSET;
}
}
You need to change StringType to be a class instead of a struct.
Since your DEFAULT and UNSET StringTypes are static readonly and constructed with the class, you shouldn't need to serialize them at all. Attribute them as [field:nonserialized].
Additionally, unless you have specific logic that needs to happen, you don't need to implement ISerializable for this class. Attributing it as [Serializable] should be enough.