SignalR send object - newbie - signalr

With Signal R, if trying to send an object, what is the syntax to pass the model?
private async void FormLoaded(object sender, RoutedEventArgs e)
{
//Connection stuff...
Proxy.On("sendHello", () => OnSendDataConnection(ModelBuilder()));
}
private void OnSendDataConnection(ConnectionModel model)
{
Dispatcher.Invoke(DispatcherPriority.Normal,model?????)
this.Dispatcher.Invoke((Action)(LoadW));
}

Looking at the question (text, and not the code) I understand you are trying to send Complex object to JS side and use it there? Is that correct?
In that case the solution is supposed to be simple:
From the ASP.NET.SignalR site:
You can specify a return type and parameters, including complex types
and arrays, as you would in any C# method. Any data that you receive
in parameters or return to the caller is communicated between the
client and the server by using JSON, and SignalR handles the binding
of complex objects and arrays of objects automatically.
Example C#:
public void SendMessage(string name, string message)
{
Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}
JS:
var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
console.log(message.UserName + ' ' + message.Message);
});
Where ContosoChatMessage is:
public class ContosoChatMessage
{
public string UserName { get; set; }
public string Message { get; set; }
}
(read on for examples...)
So basically, in JS once 'model' received, you should be able to use 'model.XY', where XY is a member of the model complex object.
I hope it helps.

In my case, I needed to convert my fields to properties, otherwise, it doesn't send the information properly.
From
public string myField;
to
public string myField {get; set;}

Related

MassTransit. Consume equal objects defined in different namespaces

First of all, excuse my English, it's very bad. I am using MassTransit with Azure Service Bus for asynchronous communication between microservices. By their own definition, and to avoid generating dependencies between them, messages sent between different microservices are defined in each of them, that is, they are part of different namespaces. The automatic management of MassTransit causes queues and topics to be managed by the object type, which prevents the microservices that consume a message from receiving the messages sent by the microservice publisher. The same thing happens with two classes with the same properties in the same namespace but with a different class name.
Is there any way to solve this?
The options that have occurred to me are:
Remove the namespace from the endpoint of the destination address, naming it only with the name of the class.
That MassTransit can manage the creation of queues and topics based on the serialization of the object, instead of managing it based on the object type (perhaps through some type of wrapping object?)
I leave an example that I hope can help you in understanding the problem.
//FIRST PROGRAM - MESSAGE CONSUMER
namespace Consumer
{
public class Example
{
public string PropOne { get; set; }
public string PropTwo { get; set; }
}
public class ExampleConsumer :
IConsumer<Example>
{
public List<Example> ConsumedTestObjectList { get; } = new List<Example>();
//THIS METHOD NEVER CALL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public Task Consume(ConsumeContext<ExampleConsumer> context)
{
ConsumedTestObjectList.Add(context.Message);
return Task.CompletedTask;
}
}
public class ConsumerProgram
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
{
var host = sbc.Host("connectionString", h => {});
});
sbc.ReceiveEndpoint(host, e =>
{
e.Consumer<ConsumerProgram.Example>(context =>
{
return Console.Out.WriteLineAsync($"Message Received: {JsonConvert.SerializeObject(context.Message)}");
});
});
bus.Start(); // This is important!
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
//SECOND PROGRAM - MESSAGE PUBLISHER
namespace Publisher
{
public class Example
{
public string PropOne { get; set; }
public string PropTwo { get; set; }
}
public class PublisherProgram
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
{
var host = sbc.Host("connectionString", h => {});
});
bus.Start(); // This is important!
//send new instance of Publisher.Example
var example = new Example() { PropOne = "1", PropTwo = "2" };
bus.Publish(example);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
Thank you very much.
regards
Borja
The message type, and the resulting name, are a key concept within MassTransit. If you want to avoid sharing assemblies between projects, that is fine, but you will need to match the entire interface (or class, in your case) name, including namespace, or it will not route properly.
Yes, you can override the entity name formatter to change how topics are named but it won't change the message type requirement for deserialization of the message (which happens, by type).
So the recommendation here is to use the same namespace for the contracts, even if they're in separate projects.

WebApi: mapping parameter to header value

I've done a few searches but haven't seem to find anything...
Using WebApi, I would like to map an input parameter to a header value: e.g.
E.g. in controller:
public User GetUser(int id){
...
return user;
}
I want WebApi to map the id parameter to a header value (e.g. X-Auth: 1234)... rather than an URL parameter.
Is this supported?
I don't think this is supported out of the box, like for example with the [FromBody] attribute.
It seems you should be able to achieve this functionality by using Model Binders, as described here. In the model binder you have access to the request and its headers, so you should be able to read the header and set its value to the bindingContext.Model property.
Edit: Reading the article further, it seems a custom HttpParameterBinding and a ParameterBindingAttribute is a more appropriate solution, or at least I would go this way. You could implement a generic [FromHeader] attribute, which does the job. I am also fighting the same problem, so I will post my solution once I have it in place.
Edit 2: Here is my implementation:
public class FromHeaderBinding : HttpParameterBinding
{
private string name;
public FromHeaderBinding(HttpParameterDescriptor parameter, string headerName)
: base(parameter)
{
if (string.IsNullOrEmpty(headerName))
{
throw new ArgumentNullException("headerName");
}
this.name = headerName;
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues(this.name, out values))
{
actionContext.ActionArguments[this.Descriptor.ParameterName] = values.FirstOrDefault();
}
var taskSource = new TaskCompletionSource<object>();
taskSource.SetResult(null);
return taskSource.Task;
}
}
public abstract class FromHeaderAttribute : ParameterBindingAttribute
{
private string name;
public FromHeaderAttribute(string headerName)
{
this.name = headerName;
}
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return new FromHeaderBinding(parameter, this.name);
}
}
public class MyHeaderAttribute : FromHeaderAttribute
{
public MyHeaderAttribute()
: base("MyHeaderName")
{
}
}
Then you can use it like this:
[HttpGet]
public IHttpActionResult GetItem([MyHeader] string headerValue)
{
...
}
Hope that helps.
WebApi on DotNet Core has a has some additional attributes for extracting data from the request. Microsoft.AspNetCore.Mvc.FromHeaderAttribute will read from the request head.
public ActionResult ReadFromHeader([FromHeader(Name = "your-header-property-name")] string data){
//Do something
}
Thank you filipov for the answer.. I took your code and modified it a bit to suit my needs. I am posting my changes here in case anyone can make use of this.
I made 2 changes.
I liked the idea of the FromHeaderAttribute, but without subclassing. I made this class public, and require the user to set the param name.
I needed to support other data types besides string. So I attempt to convert the string value to the descriptor's parameterType.
Use it like this:
[HttpGet]
public void DeleteWidget(long widgetId, [FromHeader("widgetVersion")] int version)
{
...
}
And this is my FromHeaderBinding
public class FromHeaderBinding : HttpParameterBinding
{
private readonly string _name;
public FromHeaderBinding(HttpParameterDescriptor parameter, string headerName)
: base(parameter)
{
if (string.IsNullOrEmpty(headerName)) throw new ArgumentNullException("headerName");
_name = headerName;
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues(_name, out values))
{
var tempVal = values.FirstOrDefault();
if (tempVal != null)
{
var actionValue = Convert.ChangeType(tempVal, Descriptor.ParameterType);
actionContext.ActionArguments[Descriptor.ParameterName] = actionValue;
}
}
var taskSource = new TaskCompletionSource<object>();
taskSource.SetResult(null);
return taskSource.Task;
}
}

Error sending object to .net signalr client

My understanding is that with signalr I can just send objects back and forth. I am trying to set up a .net client to receive notifications that orders have been placed on a web site. I am trying to set up a very simple example so that I understand the concepts. It works great when I am sending a string notification back to the client, but when I try to send an object I get an error:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled by user code
HResult=-2146233088
Message=The best overloaded method match for 'ConsoleHub.Program.DisplayOrder(ConsoleHub.Order)' has some invalid arguments
Source=Anonymously Hosted DynamicMethods Assembly
StackTrace:
at CallSite.Target(Closure , CallSite , Type , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)
at ConsoleHub.Program.<Main>b__6(Object o) in c:\Working\OrderNotifier\ConsoleHub\Program.cs:line 23
at Microsoft.AspNet.SignalR.Client.Hubs.HubProxyExtensions.<>c__DisplayClass6`1.<On>b__4(JToken[] args)
at Microsoft.AspNet.SignalR.Client.Hubs.Subscription.OnData(JToken[] data)
at Microsoft.AspNet.SignalR.Client.Hubs.HubProxy.InvokeEvent(String eventName, JToken[] args)
at Microsoft.AspNet.SignalR.Client.Hubs.HubConnection.OnReceived(JToken message)
at Microsoft.AspNet.SignalR.Client.Connection.Microsoft.AspNet.SignalR.Client.IConnection.OnReceived(JToken message)
at Microsoft.AspNet.SignalR.Client.Transports.HttpBasedTransport.ProcessResponse(IConnection connection, String response, Boolean& timedOut, Boolean& disconnected)
InnerException:
My class:
public class Order
{
public int OrderId { get; set; }
public string Name { get; set; }
public string OrderItem { get; set; }
}
My hub:
using Microsoft.AspNet.SignalR.Hubs;
using OrderNotifier.Models;
using System.Linq;
using System.Threading.Tasks;
namespace OrderNotifier.Hubs
{
public class NotifierHub : Hub
{
OrderContext db = new OrderContext();
public void Hello()
{
Clients.Caller.Welcome("hello");
}
}
}
My controller action:
[HttpPost]
public ActionResult Create(Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.SaveChanges();
SendNotifier.SendOrderNotification(String.Format("{0} ordered {1}", order.Name, order.OrderItem), order);
return RedirectToAction("Index");
}
return View(order);
}
SendNotifier - which is a little weird because I am having it send both a string version and an object version for testing:
public class SendNotifier
{
public static void SendOrderNotification(string message, Order order)
{
var context = GlobalHost.ConnectionManager.GetHubContext<NotifierHub>();
context.Clients.All.Notify(message);
context.Clients.All.Order(order);
}
}
And my console application:
using Microsoft.AspNet.SignalR.Client.Hubs;
using OrderNotifier.Models;
using System;
namespace ConsoleHub
{
class Program
{
static void Main(string[] args)
{
var hubConnection = new HubConnection("http://localhost:60692");
var order = hubConnection.CreateHubProxy("NotifierHub");
//
// Set up action handlers
//
order.On("Welcome", message => Console.WriteLine(message));
order.On("Notify", message => Console.WriteLine(message));
order.On("Order", o => DisplayOrder(o));
hubConnection.Start().Wait();
order.Invoke("Hello").Wait();
Console.WriteLine("Initialized...");
Console.ReadLine();
}
public void DisplayOrder(Order o)
{
Console.WriteLine(String.Format("Order object received.../r/nOrderId: {0}/r/nName: {1}/r/nOrderItem: {2}", o.OrderId, o.Name, o.OrderItem));
//Console.WriteLine(o);
}
}
}
If I change the DisplayOrder parameter to be a string it works. I know I could probably manually deserialize it using Json.Net, but my understanding is that I should just be able to work with it as an object and let signalr deserialize. What am I missing?
You're using the dynamic object overload of On. You need to specify the type:
order.On<Order>("Order", DisplayOrder);

ASP.NET: Is it possible to keep complex object such as list<> is the session?

ASP.NET: Is it possible to keep complex object such as List<object> is the session? If yes, how to do so? I'm trying to keep a list in the session, but I'm being told that I cannot convert that List to a string.
EDIT
[Serializable]
public class Client
{
public string ClientType { get; set; }
public string ClientName { get; set; }
public string SubClientName { get; set; }
public string Project { get; set; }
public string Service { get; set; }
public string Activity { get; set; }
}
List<Client> ListOfClients;
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
ListOfClients = new List<Client> { new Client()};
Session["ListOfClients"] = ListOfClients;//This is where the error occurs
//Cannot convert List<Client> to string...
}
}
There are many operations to execute, but the idea is to keep whatever is in the list of clients in the session.
Thanks for helping.
Yes, you can store any serializable object into a session variable. For example:
List<string> list;
Session["list"] = list;
Then to return the value:
List<string> list = (List<string>)Session["list"];
You can store anything in the session object as long as session state is in InProc mode.
Otherwise what you store has to be serialisable.
Note that the type of what you store is object, so you cast the reference that you get back:
ListOfClients = Session["ListOfClients"] as List<Client>;
Since you wrote in a comment that the error happens when compiling as opposed to at runtime, I suspect that you have some other object called Session that masks the Page.Session.
Try to hover your mouse over the Session text in Visual Studio. The tooltip should show you that Session is of type
System.Web.SessionState.HttpSessionState
If it's showing something else, you need to search both your markup (.aspx file) and code behind file to see if you have declared something else with the name/id Session and then change that name/id.

Conditional Required Attribute for validation

We're trying to get a conditional attribute to work, case in point, there's a boolean (checkbox) that if checked, its related text is required. So, ideally we'd have something like ...
public bool Provision { get; set; }
[ConditionalRequirement(IsNeededWhenTrue = Provision)]
public string ProvisionText { get; set; }
Is this even possible?
Alternate idea (not as elegant?)
public bool Provision2 { get; set; }
[PropertyRequired(RequiredBooleanPropertyName = "Provision2")]
public string Provision2Text { get; set; }
I'd hate to use the magic string method ... but any other ideas?
Ended up rolling my own. Basically you create a valiation method that does your normal check of yes, no, whatever and collects them in some kind of error collection. The rub with this is sending it BACK to the Model itself. So I got lazy and strongly typed it as such ...
public static void AddError<T>(this ErrorCollection errorCollection, Expression<Func<T, object>> expression, string friendlyUiName)
{
var propertyName = GetPropertyName(expression.ToString(), expression.Parameters[0].Name);
var propertyInfo = typeof (T).GetProperty(propertyName);
var resultError = DetermineOutput(friendlyUiName, propertyInfo.PropertyType);
errorCollection.Errors.Add(new ValidationError(propertyName, resultError));
}
so then you're validation statements have something like this in them ...
if (FirstName.IsEmpty())
EntityErrorCollection.AddError<SomeClass>(x => x.FirstName, "First Name");
Then within the controller, a simple check and port it BACK to the model if it (isn't valid of course) ...
foreach (var error in someObject.EntityErrorCollection.Errors)
ModelState.AddModelError(error.Property, error.Message);
There's probably a more cleaner way of doing this but so far, this has been working just fine.

Resources