Domanda

I am fairly new to messaging and I have decided for a prototype that I am building to use EasyNetQ (RabbitMQ .Net).

I have created the Publisher and Subscriber and all seems to work well. I have the Publisher in my Web Services application catching all commands that are being sent to the DB. I have the Subscriber in a Console App picking up the messages.

However when the Subscriber picks up the Command objects, it throws the error:

A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.

In my prototype I have a Command class like such:

[Serializable]
public class Command
{
    private Type _type;
    private string _method;
    private object[] _params;

    public Command(){
    }

    public Command(Type type, string method, params object[] Params)
    {
        _type       = type;
        _method     = method;
        _params     = Params;
    }

    public Command(Type type, string method, params object[] Params)
    {
        _type           = type;
        _method         = method;
        _params         = Params;
    }

    public Type Type { get { return _type; } }
    public string Method { get { return _method; } }


    public object[] Params { get { return _params; } }
}

I am passing this command object to the Publisher:

public void Publish(Command message){
        using(var publishChannel = RabbitManager.Instance.OpenPublishChannel()){
            publishChannel.Publish(message);
        }
    }

The subscriber picks up the message:

public void SubscribeCommand(){
        RabbitManager.Instance.Subscribe<Command>("my_subscription_id", msg => Console.WriteLine(msg.Method));
    }

When I run this and the Command objects are processed by the Subscriber I get the error:

ERROR: Exception thrown by subscription calback.

Exchange: 'Entities_Command:BL'
Routing Key: ''
Redelivered: 'False'

Message:{"Type":"BL.DataAccess.XXX, BL, Version=1.2.0.0, Culture=neutral, PublicKeyToken=xxx","Method":"GET","Params":[1,true,3]}

BasicProperties:

ContentType=NULL, ContentEncoding=NULL, Headers=[], DeliveryMode=2, Priority=0, CorrelationId=8aa21f39-4020-4904-b90d-4cb123d3cac0, ReplyTo=NULL, Expiration=NULL, MessageId=NULL, Timestamp=0,Type=Entities_Command:BL, UserId=NULL, AppId=NULL, ClusterId=NULL Exception: Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Entities.Command. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Type', line 1, position 8. at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultConstructor) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at EasyNetQ.JsonSerializer.BytesToMessage[T](Byte[] bytes) at EasyNetQ.RabbitAdvancedBus.<>c_DisplayClass5`1.b_4(Byte[] body, MessageProperties properties, MessageReceivedInfo messageRecievedInfo) at EasyNetQ.Consumer.HandlerRunner.InvokeUserMessageHandler(ConsumerExecution Context context)

I have a default constructor in the Command class but I am still not sure why this is happening.

I have tried passing through just a Object type (rather than Command) which works fine.

Can someone explain what I am doing wrong here?

È stato utile?

Soluzione

Internally EasyNetQ uses the Newtonsoft.JSON serializer. It expects message types to be simple DTOs with default constuctors (i.e. no constructor, or one with no parameters) and read-write properties. Your message type, the Command class, has read-only properties, that's why the serialization is failing.

Another limitation of the serializer is that it doesn't handle polymorphic types. If you are expecting to be able to cast your object[] params into something usable, it's not going to work.

The command pattern is not one that EasyNetQ is currently designed to support, although it's worth considering using the Advanced API and doing your own custom serialization. Plans are to release a version that will support polymorphic commands in the near future though.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top