EasyNetQ Publish method not found - c#

I have been going through the set up for EasyNetQ https://github.com/EasyNetQ/EasyNetQ/wiki/Quick-Start
The following code
using System;
using EasyNetQ;
static void Main(string[] args)
{
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
var input = "";
Console.WriteLine("Enter a message. 'Quit' to quit.");
while ((input = Console.ReadLine()) != "Quit")
{
bus.Publish(new TextMessage
{
Text = input
});
}
Throws this error, on the bus.Publish line
Error CS1929 'IBus' does not contain a definition for 'Publish' and the best extension method overload 'PubSubExtensions.Publish(IPubSub, TextMessage, CancellationToken)' requires a receiver of type 'IPubSub' PetStore.StockDelivery.Publish
I have EasyNetQ installed
<ItemGroup>
<PackageReference Include="EasyNetQ" Version="5.3.0-alpha0074" />
</ItemGroup>
I added this through nuget and got RabbitMQ and other dependancys
When I look at what is available on IBus
IPubSub PubSub { get; }
IRpc Rpc { get; }
ISendReceive SendReceive { get; }
IScheduler Scheduler { get; }
IAdvancedBus Advanced { get; }
Is there something I am missing?
I aslo have RabbitMQ server running okay on my mashine on localhost and I have tested that works

They moved it under PubSub property. See code example below:
public class Publisher<T>
{
IBus bus;
public Publisher()
{
bus = RabbitHutch.CreateBus("host=localhost");
}
public async void Send(T message)
{
await bus.PubSub.PublishAsync(message).ConfigureAwait(false);
}
}

Related

Impossible to read queue message with Masstransit

I have a queue with some messages in (created with masstransit).
I tried this piece of code the get the messages (see below).
I expected to get the messages on the Console.Out line but I never hit this line and the messages are still in the queue. I didn't get any error.
Any idea ?
class Program
{
static void Main(string[] args)
{
var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ReceiveEndpoint("myQueue", e =>
{
e.Handler<ProcessingQueue>(context =>
{
return Console.Out.WriteLineAsync($"{context.Message.Id}");
});
});
});
}
}
public class ProcessingQueue
{
public int Id { get; set; }
public string Name { get; set; }
}
Thanks,
I tried to add :
bus.Start();
Console.WriteLine("Receive listening for messages");
Console.ReadLine();
bus.Stop();
but when I do this a new queue is created myQueue_skipped is created with my messages in.
If messages are moved to the _skipped queue, it indicates that those messages are not consumed by any of the consumers configured on that receive endpoint. The most common mistake, as highlighted at the top of the message documentation, is a mismatched namespace.
Similar answer: here
Try with this code for the ReceiveEndpoint
cfg.ReceiveEndpoint("myQueue", e =>
{
e.Consumer<MessagesConsumer>();
});
"MessagesConsumer" must inherit from IConsumer
public class MessagesConsumer: IConsumer<ProcessingQueue>
{ public async Task Consume(ConsumeContext<ProcessingQueue> context)
{
//access to the properties
var name=context.Message.Name;
var id=context.Message.Id;
}
}
In the Consume method, you will receive messages of the type "ProcessingQueue". You can access the properties here..

Publish to Azure Service Bus using MassTransit is causing errors

I am new to both MassTransit and Azure Service Bus. I am attempting to use an architecture where either RabbitMq or Azure Service Bus is used in a .NET Core 3.1 API. I have the RabbitMq portion working and just started on the Azure Service Bus. I have an API that will take an incoming payload and publish it to a queue. When I attempt to publish via the Azure Service Bus approach, I get an error "SubCode=40000. Cannot operate on type Topic because the namespace 'servicehubqa' is using 'Basic' tier.
I am attempting to use a queue approach and am hoping to create the queue as messages are published. Currently, the service bus is using a Basic pricing tier as the documentation says that I can play with queues at that level. I am not sure if I need to manually create the queue (I had to do this approach using RabbitMq since no queue would be created if no consumer exists). Is topic the default approach if nothing is specified? How do I specify queue vs topic?
My code is as follows below.
Startup - ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
services.AddScoped<IMassTransitRabbitMqTransport, MassTransitRabbitMqTransport>();
services.AddScoped<IMassTransitAzureServiceBusTransport, MassTransitAzureServiceBusTransport>();
var messageProvider = ConfigProvider.GetConfig("MessageService", "Messaging_Service");
switch (messageProvider)
{
case "AzureServiceBus":
services.AddScoped<IMessagingService, MassTransitAzureServiceBusMessagingService>();
break;
case "RabbitMq":
services.AddScoped<IMessagingService, MassTransitRabbitMqMessagingService>();
break;
default:
throw new ArgumentException("Invalid message service");
};
services.AddControllers();
}
Controller
public class ListenerController : ControllerBase
{
readonly ILogger<ListenerController> logger;
readonly IMessagingService messenger;
public ListenerController(
ILogger<ListenerController> logger,
IMessagingService messenger)
{
this.logger = logger;
this.messenger = messenger;
}
[HttpPost]
public async Task<IActionResult> Post()
{
var payload = new
{
...
};
await messenger.Publish(payload);
return Ok();
}
}
IMessagingService
public interface IMessagingService
{
Task Publish(object payload);
}
IMassTransitTransport
public interface IMassTransitTransport
{
IBusControl BusControl { get; }
}
public interface IMassTransitRabbitMqTransport : IMassTransitTransport { }
public interface IMassTransitAzureServiceBusTransport : IMassTransitTransport { }
MassTransitAzureServiceBusTransport
public sealed class MassTransitAzureServiceBusTransport : IMassTransitAzureServiceBusTransport
{
public IBusControl BusControl { get; }
public MassTransitAzureServiceBusTransport()
{
BusControl = ConfigureBus();
BusControl.StartAsync();
}
IBusControl ConfigureBus()
{
return Bus.Factory.CreateUsingAzureServiceBus(config => {
var host = config.Host(ConfigProvider.GetConfig("AzureServiceBus", "AzureServiceBus_ConnStr"), host => { });
});
}
}
MassTransitAzureServiceBusMessagingService
public class MassTransitAzureServiceBusMessagingService : IMessagingService
{
readonly IMassTransitAzureServiceBusTransport massTransitTransport;
public MassTransitAzureServiceBusMessagingService(IMassTransitAzureServiceBusTransport massTransitTransport)
{
//transport bus config already happens in massTransitTransport constructor
this.massTransitTransport = massTransitTransport;
}
public async Task Publish(object payload)
{
var jsn = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
var cmd = JObject.Parse(jsn)["Command"];
switch (cmd.ToString())
{
case "UPDATESTATUS":
//IRegisterCommandUpdateStatus is an interface specifying the properties needed
await massTransitTransport.BusControl.Publish<IRegisterCommandUpdateStatus>(payload);
break;
default: break;
}
}
}
The Azure Service Bus basic tier does not allow the use of topics. So you would not be able to use publish. That said, MassTransit doesn't really work with the basic tier, despite attempts in the past that may have been successful.
The MassTransit documentation does state that if you want to use a Topic (i.e. the ability to publish to multiple subscriptions at the same time), you use the publish.
If you want to send a message to a queue (the message is routed to a specific location), you use the send and provide the correct information.
Topics require standard pricing and Queues can use basic pricing.
With this information, the MassTransitAzureServiceBusMessagingService would be modified as follows:
Basic Pricing - Queues
public async Task Publish(object payload)
{
var jsn = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
var cmd = JObject.Parse(jsn)["Command"];
switch (cmd.ToString())
{
case "UPDATESTATUS":
var queueUri = new Uri(massTransitTransport.BusControl.Address, "registration.updatestatus");
var endpoint = await massTransitTransport.BusControl.GetSendEndpoint(queueUri);
await endpoint.Send<IRegisterCommandUpdateStatus>(payload);
break;
default: break;
}
}
Standard Pricing - Topics/Subscriptions
public async Task Publish(object payload)
{
var jsn = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
var cmd = JObject.Parse(jsn)["Command"];
switch (cmd.ToString())
{
case "UPDATESTATUS":
await massTransitTransport.BusControl.Publish<IRegisterCommandUpdateStatus>(payload);
break;
default: break;
}
}

Twilio Rest API Helper Library, v 5.0.1, C# - MessageResource.Create function call not returning properly

I am using the Twilio REST API helper library, v 5.0.1 in my C# ASP.NET MVC Web Application. I created the following helper class and function to send out text messages:
using MyApplication.Web.Helpers;
using System;
using System.Configuration;
using Twilio;
using Twilio.Exceptions;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
namespace MyApplication.Web.Services
{
public class TwilioSmsSender : ISmsSender
{
public string AccountSid { get; set; }
public string AuthToken { get; set; }
public string FromPhoneNumber { get; set; }
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public string SmsPrefix { get; set; }
public string SmsSuffix { get; set; }
public TwilioSmsSender()
{
//get our Twilio account info from the config file
AccountSid = ConfigurationManager.AppSettings["TwilioAccountSid"];
AuthToken = ConfigurationManager.AppSettings["TwilioAuthToken"];
FromPhoneNumber = ConfigurationManager.AppSettings["SmsService.FromPhoneNumber"];
SmsPrefix = ConfigurationManager.AppSettings["SmsPrefix"];
SmsSuffix = ConfigurationManager.AppSettings["SmsSuffix"];
if (FromPhoneNumber.Length == 10)
{
FromPhoneNumber = $"+1{FromPhoneNumber}";
}
TwilioClient.Init(AccountSid, AuthToken);
}
public INotificationResponse SendTextMessage(string phoneNumber, string message, bool useFormatting = true)
{
var resp = new TwilioSmsSenderResponse();
resp.Succeeded = false;
resp.AttemptDateTimeUtc = DateTime.UtcNow;
if (useFormatting)
{
message = $"{SmsPrefix}{message}{SmsSuffix}";
}
try
{
var msgResponse = MessageResource.Create(
to: new PhoneNumber($"+1{phoneNumber}"),
from: new PhoneNumber($"{FromPhoneNumber}"),
body: message);
//Previous line works (i.e, I get the text message that I'm sending out successfully).
//However, none of the following lines are running...
//As you see, this is in a try/catch block... and it doesn't go to the catch block either!
if (msgResponse.ErrorCode == null)
{
//successfully queued
resp.Succeeded = true;
resp.ReferenceId = msgResponse.Sid;
resp.AttemptDateTimeUtc = DateTime.UtcNow;
}
else
{
//Twilio sent an error back
log.Info($"Twilio sent an error back: {msgResponse}");
resp.Succeeded = false;
resp.Notes = $"ErrorCode: {msgResponse.ErrorCode}, ErrorMessage: {msgResponse.ErrorMessage}";
resp.AttemptDateTimeUtc = DateTime.UtcNow;
}
}
catch (Exception e)
{
resp.Succeeded = false;
resp.Notes = ExceptionsHelper.GetExceptionDetailsAsString(e);
resp.AttemptDateTimeUtc = DateTime.UtcNow;
log.Error($"Twilio Error: {resp.Notes}, to: {phoneNumber}, message: {message}");
}
return resp;
}
}
}
Unfortunately, my code is not behaving as I expected it would after the MessageResource.Create() call. That is, the text-message is sent out correctly and I receive the SMS on my phone. However, I expect the call to return control to my msgResponse variable and I expect the
if (msgResponse.ErrorCode == null) ...
line and subsequent lines to run but that is not happening. I can put a breakpoint on the var msgResponse line and it will run that just fine but it does not run any code lines after that. You’ll see that I have the call in a try/catch. I suspected there was an exception that was occurring but it doesn’t seem so because it doesn’t go to my catch block either! The text message is being sent successfully! All I want to do is to get an acknowledgement back so that I can properly log it and send that information back to the routines that are calling this function.
Any help would be greatly appreciated!
version 5.0.2 fixed this for me just update twilio to 5.0.2. they just added .ConfigureAwait(false); with CreateAsync

MassTransit - PublishFault stops working when Message Q is down

I am connecting a publish observer, using the code below (and RabbitMQ). When RabbitMQ is running, it works well - I can see the console messages on PostPublish and PrePublish.
However, when i stop RabbitMQ, and publish, the PublishFault works once, but never again whilst RabbitMQ remains stopped.
I am attempting to persist a message to another datastore (and log and error), in the event of a publish failing - I thought that the PublishFault method would be the best place to do this. This doesn't really work if only the first failure is detected.
Is this behaviour expected? Is there a better way to achieve failed message persistance.
PS...as soon as I start RabbitMQ again, I then see all my PrePublish and PostPublish debug messages , for my failed messages. Which, I assume, is to be expected.
using MassTransit;
using MassTransit.Pipeline;
using System;
using System.Threading.Tasks;
namespace Mtt.Publisher
{
class Program
{
static void Main(string[] args)
{
IBusControl busControl = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri("rabbitmq://localhost"), h =>
{
h.Username("user");
h.Password("pass");
});
sbc.UseRetry(Retry.Immediate(5));
});
busControl.Start();
busControl.ConnectPublishObserver(new PublishObserver());
var input = "";
while (input != "exit")
{
input = Console.ReadLine();
busControl.Publish<Test>(new TestMessage());
}
busControl.Stop();
}
}
public interface Test { }
public class TestMessage : Test { }
public class PublishObserver : IPublishObserver
{
public async Task PostPublish<T>(MassTransit.PublishContext<T> context) where T : class
{
Console.WriteLine("--- POST PUBLISH ----");
}
public async Task PrePublish<T>(MassTransit.PublishContext<T> context) where T : class
{
Console.WriteLine("**** PRE PUBLISH ****");
}
public async Task PublishFault<T>(MassTransit.PublishContext<T> context, Exception exception) where T : class
{
Console.WriteLine("%%%%%% EXCEPTION %%%%%%%");
}
}
}

How to split Akka.NET remoting solution to two solutions?

I have made 2 Akka.NET solutions in the hope of testing out Remoting on a simple hello world example, however, I keep getting a Disassociated exception when the communication attempt is made. I have reason to believe that this is because of the shared class Greet which should be a message that both systems should understand. Unfortunately, they don't. How can I fix this?
This is the code of the "Server" application:
namespace Shared
{
public class Greet
{
public string Who { get; set; }
public Greet(string who)
{
Who = who;
}
}
}
namespace AkkaTest
{
using Shared;
class GreeterActor : ReceiveActor
{
public GreeterActor()
{
Receive<Greet>(x => Console.WriteLine("Hello {0}", x.Who));
}
}
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor.provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
remote {
helios.tcp {
port = 9099
hostname = 127.0.0.1
}
}
}
");
using (ActorSystem system = ActorSystem.Create("MyServer", config))
{
system.ActorOf<GreeterActor>("greeter");
Console.ReadLine();
system.Shutdown();
}
}
}
}
Here is the code for the client:
namespace Shared
{
public class Greet
{
public string Who { get; set; }
public Greet(string who)
{
Who = who;
}
}
}
namespace AkkaTest
{
using Shared;
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor.provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
remote {
helios.tcp {
port = 9090
hostname = 127.0.0.1
}
}
}
");
using (var system = ActorSystem.Create("MyClient", config))
{
//get a reference to the remote actor
var greeter = system
.ActorSelection("akka.tcp://MyServer#127.0.0.1:9099/user/greeter");
//send a message to the remote actor
greeter.Tell(new Greet("Roger"));
Console.ReadLine();
}
}
}
}
EDIT: Putting both client and server in the same solution but different projects, and the GreetingActor and Greet in a shared projects fixes the issues. However, I would like to have completely separate solutions.
If you are using Greet messages on both sides, you need to provide some way do share this message schema between them. Usually this is done as a separate project shared between other projects or solutions.
While default Akka.NET serializer uses fully qualified type name with assembly to serialize/deserialize messages, it's also version tolerant - you can modify message schema and gradually update it's assembly node by node.
Other option is to use custom serializer. This way you'll be able to determine by yourself, how message will be serialized/deserialized on both ends. You can read more about this topic here.

Categories