I'm attempting to build a framework for real-time delivery of messages (using MSMQ at the moment) to be displayed on a webpage that will serve as an informational display. As soon as a message lands in the queue, I would like the message to display on the page.
Controller:
public class MessageController : AsyncController
{
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment(1);
Task<string>.Factory.StartNew(() =>
{
var queue = new MessageQueue(@".\private$\path-to-queue");
while (true)
{
var message = string.Empty;
var result = queue.BeginReceive();
queue.Formatter = new XmlMessageFormatter(new[] {typeof (string)});
queue.ReceiveCompleted += delegate(object sender, ReceiveCompletedEventArgs args) { message = args.Message.Body.ToString(); };
queue.EndReceive(result);
return message;
}
}).ContinueWith(t =>
{
AsyncManager.Parameters["message"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
}
);
}
public JsonResult IndexCompleted(string message)
{
return Json(message, JsonRequestBehavior.AllowGet);
}
}
When I call the action from a browser, the browser waits for a message to show up on the queue. When I add a message onto the queue, the browser continues with the request, but I end up with a blank string instead of the contents of the message.
Alternately, I tried substituting
message = args.Message.Body.ToString();
within the delegate with
return args.Message.Body.ToString();
but I ended up with a compiler error telling me that the return type is void.
Admittedly, I am still very new to delegates, but I'd greatly appreciate a push in the right direction.
Thanks--
I arrived at a working solution... not sure it's the best method, which is why I'm leaving this open for some peer review.
public class MessageController : AsyncController
{
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment(1);
Task<Message>.Factory.StartNew(() =>
{
var queue = new MessageQueue(@".\private$\path-to-queue");
while (true)
{
var result = queue.BeginReceive();
queue.Formatter = new XmlMessageFormatter(new[] {typeof (string)});
var message = queue.EndReceive(result);
return message;
}
}).ContinueWith(t =>
{
AsyncManager.Parameters["message"] = t.Result.Body.ToString();
AsyncManager.OutstandingOperations.Decrement();
}
);
}
public JsonResult IndexCompleted(string message)
{
return Json(message, JsonRequestBehavior.AllowGet);
}
}
As stated in edit to original question, I discovered I didn't need the delegate after all:
public class MessageController : AsyncController
{
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment(1);
Task<Message>.Factory.StartNew(() =>
{
var queue = new MessageQueue(@".\private$\path-to-queue");
while (true)
{
var result = queue.BeginReceive();
queue.Formatter = new XmlMessageFormatter(new[] {typeof (string)});
var message = queue.EndReceive(result);
return message;
}
}).ContinueWith(t =>
{
AsyncManager.Parameters["message"] = t.Result.Body.ToString();
AsyncManager.OutstandingOperations.Decrement();
}
);
}
public JsonResult IndexCompleted(string message)
{
return Json(message, JsonRequestBehavior.AllowGet);
}
}
One thing I will note, though... I didn't consider that there would be multiple instances of the controller polling the queue for messages, so you end up with a situation where the browser sessions are competing for messages. One browser will get the first message, other browser will get the second, etc. I ended up switching this implementation over to SignalR-- just waiting for the .NET client to move into release phase.