I feel like I'm either missing the point of the SignalR service or I've got the architecture of it wrong.
Using an Azure SignalR service I've got the front-end to front-end communication working as such;
Startup.cs
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.MapAzureSignalR(this.GetType().FullName);
}
Hub.cs
private void BroadcastMessage(string message)
{
Clients.All.broadcastMessage(message);
}
Index.html
hubName.client.broadcastMessage = function (message) {
alert(message);
};
$('#btn').click(function () {
hubName.server.broadcastMessage("Hello World");
});
This is fine, and when I need the back-end to trigger a message I am using the following;
Functions.cs
HubConnection signalr = new HubConnection("http://localhost:57690/");
IHubProxy hub = null;
if (signalr.State == ConnectionState.Disconnected)
{
hub = signalr.CreateHubProxy(hubName);
signalr.Start().Wait();
}
hub.Invoke(functionName, args);
signalr.Stop();
While this works it leaves me wondering if I have implemented it wrong as this leaves http://localhost:57690/signalr/hubs open to posts from any source.
In the front-end, I have to provide the Endpoint=https://appName.service.signalr.net;AccessKey=xxxxx setting but not from the back-end.
As well as a security concern, this leaves me questioning what purpose the SignalR service has in this.
Surely there must be a better way to invoke signalr from the back-end.
Many Thanks,
Tom
In the Startup.cs you gotta have a line of code like this
services.AddSignalR().AddAzureSignalR();
By not passing a parameter to AddAzureSignalR(), this code uses the default configuration key, Azure:SignalR:ConnectionString, for the SignalR Service resource connection string.
You can find more info in this article
Related
I am creating an azure function using a service bus to make my API more resilient to high traffic spikes. However I am uncertain on how I should handle an offline database as the function still continues to fetch messages, eventually dead lettering messages.
Therefore I were thinking about implementing a check during the startup class of the function to automatically shut it down before fetching any events.
It would look something like this:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var settings = new MongoClientSettings();
var mongoClient = new MongoClient(settings);
mongoClient.StartSession();
if(mongoCLient.Cluser.Description.State != MongoDB.Driver.Core.Clusters.CluseterState.Connected)
throw new Exception("Db connection failed during startup")
}
}
However as I am not certain on how the trigger really works i am wondering if there might be any issues i cant foresee. Its also quite hard to test properly.
Any thoughts, knowledge or potentially other ideas would be much appreciated. Thanks!
I'm new to .NET and WCF framework. Currently, I'm building a simple windows application to consume WCF service. The windows client need to pass username parameter to WCF Service to save to database. How can I do that without passing parameter in every service requests ?
One thing to note is this service will call a method in another project to save to database , so actually I want to access the username parameter at this project layer.
Form1.cs
public async void Save()
{
using(var client = new MyChannelFactory<WCFService>("WCFService"))
{
await Task.Run(() => client.Proxy.SaveData());
}
}
WCFService.cs
public void SaveData()
{
var _dataBL = new DataBL();
_dataBL.SaveData();
}
DataBL.cs
public void SaveData();
{
//need to get username from client
string user = GetUserName();
//save to database
}
Something to note:
WCFService.cs and DataBL.cs are in different projects.
I'm not adding Service Reference to client project, instead I'm using a ChannelFactory to create service proxy.
I'm using wsHttpBinding
I'm not using Windows Authentication
This should be a common request but after read many articles from internet, I still couldn't make it work.
Thanks for your help.
I am working on an web based application in which I want to display a twitter stream on a specific query. User does need to refresh the webpage view and it will load the tweets automatically.
So far, I have created a simple console application using tweetinvi which read tweeets and performs all the custom logic on the tweets.
Next I need to know that how do I create project layout/infrastructure that my web app gets the constant feeds without client interaction.
As Nathan Cooper notes, SignalR is the best way of achieving this. As I've literally just built what you've described, I'll give you a detailed rundown of what you need to do..
Create a new ASP.NET MVC project, and install ASP.NET SignalR using NuGet as well as Tweetinvi
Right click on the App_Start folder and add a new OWIN Startup class (this should be listed in the contextual menu if you have installed SignalR using NuGet).
Your OWIN startup class should look like this:
[assembly: OwinStartup(typeof(TwitterClient.Web.App_Start.Startup))]
namespace TwitterClient.Web.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
Add a new Folder to the project called "Hubs" and add a new SignalR Hub class (this should also be available as a template in the new file dialog in Visual Studio).
Create a new class called "TwitterConnection" wherever you feel like in the project. In the constructor for this class, do all of the stuff you did in your console application to connect to the Twitter API with Tweetinvi. Usually when you broadcast data from server to client in SignalR, you do it from within the Hub class, but you can obtain a reference to the SignalR hub outside of the hub class itself by using GlobalHost.ConnectionManager.GetHubContext<HUBNAME>(); where HUBNAME is the name of your hub. So your TwitterConnection class should look something like this:
public class TwitterConnection {
private string _consumerKey = ConfigurationManager.AppSettings.Get("consumerKey");
private string _consumerSecret = ConfigurationManager.AppSettings.Get("consumerSecret");
private string _accessKey = ConfigurationManager.AppSettings.Get("accessToken");
private string _accessToken = ConfigurationManager.AppSettings.Get("accessTokenSecret");
private IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<TwitterHub>();
public TwitterConnection()
{
// Access the filtered stream
var filteredStream = Stream.CreateFilteredStream();
filteredStream.MatchingTweetReceived += (sender, args) =>
{
_context.Clients.All.broadcast(args.Tweet.Text);
};
filteredStream.StartStreamMatchingAllConditions();
}
}
In terms of the server-side stuff, you need to find a way of ensuring that there is only one instance of a stream open to Twitter at any one time. My quick and dirty way of doing this was to use Task.Factory.StartNew to create a new Task to manage to stream in the Global.asax file:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Start a new instance of the TwitterConnection class
Task.Factory.StartNew(() => new TwitterConnection());
}
Finally, you need to hook up the client-side element of SignalR. In your MVC Layout view (i.e. Views/Shared/_Layout.cshtml), add in a reference at the bottom of the HTML markup to the SignalR JavaScript library, the generated Hub proxy and to your external JavaScript file where your boilerplate SignalR JavaScript will go:
<!--Reference the SignalR library. -->
<script src="../../Scripts/jquery.signalR-2.2.0.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
<script src="../../Scripts/application.js"></script>
Finally, your boilerplate code in application.js (or whatever you want to call it), will look something like this:
// document ready shorthand
$(function () {
// obtain reference to the hub proxy and hub itself
var theHub = $.connection.twitterHub;
// this is the function that the server will call to broadcast new tweets
theHub.client.broadcast = function (tweet) {
var item = '<li>' + tweet.text + '</li>';
$('ul.tweets').prepend(item);
};
// this is a function that indicates that connection to the hub has been successful
$.connection.hub.start().done(function () {
console.log("connected");
});
});
Your Index.cshtml file will simply have an empty <ul> in it, where new tweets will be prepended to as and when they are received:
#{
ViewBag.Title = "Home Page";
}
<div class="row">
<div class="col-md-12">
<ul class="tweets"></ul>
</div>
</div>
You need to push live data to the browser. Use SignalR.
SignalR is great library for ASP.NET that allows you to write real time web applications. It uses Websockets under the covers, but has a number of fallback positions for older browsers. tutorial link
I always knew SignalR as fitting perfectly with browser based applications in real time.
In general to pushing server side processing messages to the client, that is a "listening" web page.
It is possible to do the same with client that is not a web application, but a desktop application, pushing it in real-time?
In short, yes. The samples on github show you how, for instance the console client sample, and the documentation in the wiki shows you how you can build a .NET client. To quote that documentation (warning, version dependent, it works right now, but may be different in the future):
var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
stockTickerHub.On("notify", () =>
// Context is a reference to SynchronizationContext.Current
Context.Post(delegate
{
textBox.Text += "Notified!\n";
}, null)
);
await hubConnection.Start();
// or do the following for a synchronous method:
// connection.Start().Wait();
See ASP.NET: ASP.NET SignalR Hubs API Guide for the above code.
I have made a wrapper around the .NET client that makes it really easy to implement listeners on the local client
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki/.NET-Client
Once set up you just add a listener like
public class MyViewModel : IHandle<MyEvent>
{
public MyViewModel(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(MyEvent message)
{
//Act on MyEvent
}
}
My infrastructure:
Main - ServiceStack self hosted console app. 'Main' sends messages to MQ.
Background - ServiceStack self hosted console app. 'Background' receives messages from MQ.
Locally installed Redis
In 'Main' AppHost I configure Redis manager:
container.Register<IRedisClientsManager>(
new PooledRedisClientManager("localhost:6379"));
Then I run this code somewhere in service:
using (var client = new RedisMessageQueueClient(TryResolve<IRedisClientsManager>()))
{
client.Publish(new TestMessage { Value = "From ping" });
}
Everything works great and I can get message in my 'Background'. But problem comes when I wrap this code in class:
public class MessageQueuePublisher : IMessageQueuePublisher
{
public void Publish(object message)
{
using (var client = new RedisMessageQueueClient(
EndpointHost.AppHost.TryResolve<IRedisClientsManager>()))
{
client.Publish(message);
}
}
}
When I call MessageQueuePublisher.Publish method from the exactly same place where previous code was executed, it seems like it works correctly (no exceptions are thrown), but my message doesn't reach 'Background'.
Is this OK?
I found a solution. On my 'Background' I expect message with type TestMessage
mqService.RegisterHandler<TestMessage>(ServiceController.ExecuteMessage);
But when using MessageQueuePublisher.Publish message was of type object and went to the object queue and wasn't handled.
So to solve this problem Publish method should be generic:
public void Publish<T>(T message)
It doesn't change how method is called but code is not so good because if you look at it, it's not clear why generic is used. But at least it works.