Signalr Hub Client trying to call Subscribe method of Hub with object of TopicFilter which doesn't de-serialize properly in hub. The type received in Subsribe is Filter whereas i want TopicFilter object in there. It works if i manual serialize and deserialize using JsonSerializer class but i want signalr jasonSerilizer to do it as there will be lot of places if i have to do manually. Any thoughts on this?
hub = connection.CreateHubProxy("DebugDispatcherHub");
hub.JsonSerializer.TypeNameHandling = TypeNameHandling.Auto;
hub.JsonSerializer.TypeNameAssemblyFormat = FormatterAssemblyStyle.Full;
var topicfilter = new TopicFilter() { Topic = new Guid("5D5B26AD-5E6A-4B96-95C8-06540FC17E53") };
hub.Invoke("Subscribe", filter);
public class Filter { }
public class TopicFilter : Filter
{
public Guid Topic { get; set; }
}
[HubName("DebugDispatcherHub")]
public class DebugDispatcherHub : Hub
{
public void Subscribe(Filter filter)
{
//some code here
}
}
internal class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
var service = (Newtonsoft.Json.JsonSerializer)GlobalHost.DependencyResolver.GetService(typeof(Newtonsoft.Json.JsonSerializer));
service.TypeNameHandling = TypeNameHandling.Auto ;
service.TypeNameAssemblyFormat = FormatterAssemblyStyle.Full;
}
}
Related
I would like to pass multiple objects of the same type from Startup to a Controller in ASP.NET Core 3.
My Controller constructor:
public RenderImageController(CloudQueue startRenderQueue, CloudQueue renderCompletedQueue)
{
_startRenderQueue = startRenderQueue;
_renderCompletedQueue = renderCompletedQueue;
}
I tried this in Startup:
services.AddSingleton(typeof(CloudQueue), startRenderQueue);
services.AddSingleton(typeof(CloudQueue), renderCompletedQueue);
But, it resulted in the same object (second one) being passed to my controller.
What is the best way to pass objects like this to my controller?
you have to create one wrapper class for two cloudqueue and inject as singleton
public interface IMyCloudQueueCollection
{
CloudQueue StartRenderQueue { get; }
CloudQueue RenderCompletedQueue { get; }
}
public class MyCloudQueueCollection : IMyCloudQueueCollection
{
public CloudQueue StartRenderQueue { get; private set; }
public CloudQueue RenderCompletedQueue { get; private set; }
public MyCloudQueueCollection(CloudQueue startRenderQueue, CloudQueue renderCompletedQueue)
{
this.StartRenderQueue = startRenderQueue;
this.RenderCompletedQueue = renderCompletedQueue;
}
}
// in startup
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMyCloudQueueCollection,MyCloudQueueCollection>(s=> new MyCloudQueueCollection(startRenderQueue, renderCompletedQueue))
}
//in controller
public RenderImageController(IMyCloudQueueCollection queueCollection)
{
_startRenderQueue = queueCollection.StartRenderQueue;
_renderCompletedQueue = queueCollection.RenderCompletedQueue;
}
Inject IEnumerable<CloudQueue> into the controller constructor and extract the desired object
//...
public RenderImageController(IEnumerable<CloudQueue> queues) {
_startRenderQueue = queues.FirstOrDefault(queue => queue.Name == "startRenderQueueNameHere");
_renderCompletedQueue = queues.FirstOrDefault(queue => queue.Name == "renderCompletedQueueNameHere");
}
//...
While the above example used the queue's Name to distinguish the desired object, You can use any one or combination of the other properties of the queue to identify it.
I'm currently working on a ASP.NET Core 2 application using SignalR Core. I was wondering if it is possible to receive a complex object within the Hub class instead of a simple string or simple data structure.
Works - This example works fine: string message
public class MyHub : Hub
{
public Task SendMessage(string message)
{
// ... some logic
}
}
Works - This example works fine as well: List<Dictionary<string, object>> message
public class MyHub : Hub
{
public Task SendMessage(List<Dictionary<string, object>> message)
{
// ... some logic
}
}
Doesn't work correctly - It seems I cannot transfer complex objects via SignalR e.g. if I create a custom message class:
public class Message
{
public int MessageId { get; set; }
public List<Dictionary<string, object>> Items { get; set; }
public List<string> TextMessages { get; set; }
}
public class MyHub : Hub
{
public Task SendMessage(Message message)
{
// ... some logic
}
}
Do you know how to transfer complex objects via a SignalR RPC?
Thank you!
You can use the Newtonsoft.Json Nuget.
There you have a JsonConverter that can serializ your object.
So in your example:
public class MyHub : Hub
{
public Task SendMessage(Message message)
{
var messageJsonString = JsonConvert.SerializeObject<Message>(message);
// some logic
}
}
And on your client you can convert it back to an object. It´s have a nativ API so you just call
connection.on("ReceiveMessage", (message) => {
let messageObject = JSON.parse(message);
// Other code here
});
Now message is again the object you send from the server.
And of course you can use JsonConvert.DeserializeObject<T>() to convert a json string you recieve from the client into a Object.
Follow steps below for a working demo which passing Message between signalR Client and Server.
Server
public class TimeHub: Hub
{
public async Task UpdateTime(string message)
{
if (Clients != null)
{
await Clients?.All.SendAsync("ReceiveMessage", message);
}
}
public Task SendMessage(Message message)
{
// ... some logic
return Task.CompletedTask;
}
}
Client
private static async void Connect()
{
var hubConnectionBuilder = new HubConnectionBuilder();
#region Worked
var hubConnection = hubConnectionBuilder.WithUrl("https://localhost:44381/timeHub", options =>
{
}).Build();
#endregion
await hubConnection.StartAsync();
await hubConnection.SendAsync("UpdateTime", $"From Client");
var item1 = new Dictionary<string, object> {
{ "T1", new { Name = "TT1" } },
{ "T2", new { Name = "TT2" } },
{ "T3", new { Name = "TT3" } },
};
var item2 = new Dictionary<string, object> {
{ "T11", new { Name = "TT11" } },
{ "T12", new { Name = "TT12" } },
{ "T13", new { Name = "TT13" } },
};
await hubConnection.SendAsync("SendMessage", new Message {
MessageId = 1,
Items = new List<Dictionary<string, object>> {
item1,
item2
},
TextMessages = new List<string> {
"H1",
"H2"
}
});
var on = hubConnection.On("ReceiveMessage", OnReceivedAction);
Console.WriteLine($"Client is Start");
Console.ReadLine();
on.Dispose();
await hubConnection.StopAsync();
}
If you are considering using JSON parsing just for the sake of passing multiple objects/parameters to client side, there is an alternative.
Server side (C#). You can pass any number of parameters to the anonymous object array.
SendCoreAsync("MethodName", new object[] {someObject, someNumber, someString });
Client side (Typescript)
private alertHandler = (someObject: any, someNumber: number, someString: string) => {
console.log(someObject, someNumber, someString);
};
I have a slightly more detailed answer here
Hello i have a very big problem. I need to take/create connection to one core with single type and make any operations.
For now its looks like:
public class SolrMachine<T> : ISolrMachine<T> where T : ISolrRecord
{
private ISolrOperations<T> actuallyInstance { get; set; }
public SolrMachine(string coreName)
{
string url = String.Format("http://xxxx/solr/{0}", coreName);
ISolrConnection solrConnection = new SolrConnection(url) { HttpWebRequestFactory = new SolrAuthWebRequestFactory()};
Startup.Init<T>(solrConnection);
var myInstance = ServiceLocator.Current.GetInstance<ISolrOperations<T>>();
this.actuallyInstance = myInstance;
}
}
ISolrMachine<T> is a interface with my methods to operate on solr core. ISolrRecord is a interface with properties in my cores.
Now, when I am doing a connection with two other cores all works perfectly.
SolrMachine<SolrTypeOne> firstCoreConnection = new SolrMachine<SolrTypeOne>(firstCoreName);
SolrMachine<SolrTypeTwo> secondCoreConnection = new SolrMachine<SolrTypeTwo>(secondCoreName);
// operation on firstCoreConnection and secondCoreConnection works
But when I'm trying to connect with one type and one coreName i have exception on Startup.Init<T>(solrConnection). I know that Startup container blocks a connection with same Type and coreName but always I am creating a new instance to this SolrMachine. I expect this:
class SomeClass
{
public MyMethod()
{
SolrMachine<SolrTypeOne> myConn = new SolrMachine<SolrTypeOne>(firstCoreName);
// operation
}
}
class SecondSomeClass
{
public MyMethod()
{
SolrMachine<SolrTypeOne> myConn2 = new SolrMachine<SolrTypeOne>(firstCoreName);
// here it's not work
}
}
How to avoid this ?
In my case, problem was that my Solr using a IHttpWebRequestFactory. From SolrNet multicore documentation author doesn't take this problem. Here is my solution (use Windsor):
public class SolrAuth : IHttpWebRequestFactory
{
public IHttpWebRequest Create(Uri url)
{
//... credentials, timeouts, etc.
return new HttpWebRequestAdapter((HttpWebRequest)webrequest);
}
}
public class SolrMachine<T> : ISolrMachine<T> where T : ISolrRecord
{
public WindsorContainer myContainer = new WindsorContainer();
private ISolrOperations<T> actuallyInstance { get; set; }
public SolrMachine(string coreName)
{
var url = string.Format("http://xxx/solr/{0}", coreName);
myContainer.Register(Component.For<IHttpWebRequestFactory>().ImplementedBy<SolrAuth>());
var solrFacility = new SolrNetFacility(string.Format("http://xxx/solr/{0}", "defaultCollection"));
solrFacility.AddCore(coreName, typeof(T), url);
myContainer.AddFacility(solrFacility);
this.actuallyInstance = myContainer.Resolve<ISolrOperations<T>>();
}
}
I'm using WCF and I made my own proxy in the client, and i want to create a method using lambda expression or Action that will excute everything.
Here is my proxy:
public class BooksProxy
{
public ChannelFactory<IBooksService> Channel { get; set; }
public BooksProxy()
{
Channel = new ChannelFactory<IBooksService>("endpoint");
}
public IBooksService CreateChannel()
{
return Channel.CreateChannel();
}
}
Here is how i use the proxy:
IBooksService proxy = BooksProxy.CreateChannel();
IList<string> lst = proxy.GetStrings();
((ICommunicationObject)proxy).Close();
I want to do something like this in the BooksProxy class:
public void Execute(Action<...> action)
{
IBooksService proxy = this.CreateChannel();
/* executing here. */
((ICummunicationObject)proxy).Close();
}
And to call it like this maybe:
IList<string> result = null;
BooksProxy.Execute(proxy => { result = proxy.GetStrings(); });
Not quite sure how to do that...
Ok, so I figured how to do it.
Here is the Proxy, The idea is to make it generic:
public class Proxy<T>
{
public ChannelFactory<T> Channel { get; set; }
public Proxy()
{
Channel = new ChannelFactory<T>("endpoint");
}
public T CreateChannel()
{
return Channel.CreateChannel();
}
}
Now here is the trick :
For void methods :
public void Execute(Action<T> action)
{
T proxy = CreateChannel();
action(proxy);
((ICommunicationObject)proxy).Close();
}
For return:
public TResult Execute<TResult>(Func<T, TResult> function)
{
T proxy = CreateChannel();
var result = function(proxy);
((ICommunicationObject)proxy).Close();
return result;
}
Where the TResult is the returning type.
How to use:
Proxy<IService> proxy = new Proxy();
// for a void method
Proxy.Execute(prxy => prxy.Method());
// for non void method.
var result = Proxy.Execute(prxy => prxy.Method());
So, to sum up, here is how the proxy class should look like:
public class Proxy<T>
{
public ChannelFactory<T> Channel { get; set; }
public Proxy()
{
Channel = new ChannelFactory<T>("endpoint");
}
public T CreateChannel()
{
return Channel.CreateChannel();
}
public void Execute(Action<T> action)
{
T proxy = CreateChannel();
action(proxy);
((ICommunicationObject)proxy).Close();
}
public TResult Execute<TResult>(Func<T, TResult> function)
{
T proxy = CreateChannel();
var result = function(proxy);
((ICommunicationObject)proxy).Close();
return result;
}
}
I recommend this solution for a custom wcf proxy without using any service reference, its really simple and easy.
I'm trying to make my client subscribe to events that happen on my server.
I have an interface that looks like this:
public delegate void RemoteEventHandler(object sender, ClientEventArgs args);
[Serializable]
public class ClientEventArgs : EventArgs
{
public ClientEventArgs()
{ }
public ClientEventArgs(Client _client)
{
MyClient = _client;
}
public Client MyClient { get; set; }
}
public interface IMonitor
{
event RemoteEventHandler RemoteEvent;
}
My Server Class looks like this:
public class ConnectionManager : MarshalByRefObject, IMonitor
{
public event RemoteEventHandler RemoteEvent;
// call the below code when th event should fire.
if (RemoteEvent != null)
RemoteEvent(this, new ClientEventArgs(e.MyClient));
}
Then To set my channels up on the server I do this:
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 5001;
TcpChannel channel = new TcpChannel(props, null, provider);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ConnectionManager),
ConnectionManager",
WellKnownObjectMode.Singleton);
And On the client to set the channels up and subscribe to the event:
TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel, false);
_monitorObject = (IMonitor)Activator.GetObject(
typeof(IMonitor),
"tcp://localhost:5001/ConnectionManager");
_monitorObject.RemoteEvent += _monitorObject_RemoteEvent;
Can anyone explain where this is going wrong please?
Exception:
System.MissingMethodException was unhandled HResult=-2146233069 Message=No parameterless constructor defined for this object.
Source=mscorlib
To answer your last question: when using Serializable you need a constructor without parameters. So this one would definitely fail:
[Serializable]
public class ClientEventArgs : EventArgs
{
public ClientEventArgs(Client _client)
{
MyClient = _client;
}
public Client MyClient { get; set; }
}
You need to add a parameterless constructor:
[Serializable]
public class ClientEventArgs : EventArgs
{
public ClientEventArgs()
{ }
public ClientEventArgs(Client _client)
{
MyClient = _client;
}
public Client MyClient { get; set; }
}
My money is on your ConnectionManager class not having a default / parameterless constructor. The remoting infrastructure needs to be able to create an instance of it on the server end.