if I send a request, and I expect the response to come trough SignalR, is it possible to test this using a LoadTest or PerformanceTest in Visual Studio?
Short answer: Yes
I've done this several times in CodedWebTests but it would also be possible to do in a declarative WebTest. You can use a custom PreWebTest Event Handler to create your signalR client and connect to your SignalR hub. What you choose to do with the signalR notification is up to you but I like to save it to the WebTestContext as well as display it on the test results screen using the AddCommentToResult method.
The method below creates a hubConnection invokes the "addToGroup" function on the hub and then tells the client what to do when it receives a message.
using Microsoft.AspNet.SignalR.Client;
public class SignalRPlugin : WebtTestPlugin
{
public override void PreWebTest(object sender, PreWebTestEventArgs e)
{
var hubConnection = new HubConnection("yourSignalRUrl");
var hubProxy = hubConnection.CreateHubProxy("notifications");
hubConnection.Start().Wait();
hubProxy.Invoke("addToGroup", "me");
hubProxy.On<string>("message", s =>
{
e.Webtest.AddCommentToResult(s);
e.Webtest.Context.Add("signalRMessages", s);
});
}
}
Use it by attaching the event handler in your test constructor.
public MyWebTest()
{
PreWebTest += new SignalRPlugin().PreWebTest;
}
Then once you have the signalR messages you can use a custom validation rule to validate that the response was received. Just have a while loop checking the WebTestContext for the "signalRMessages" key. I strongly suggest making sure you add a timeout feature so you are not waiting forever if the messages never come in.
The other option if you are writing CodedWebTests is to create a WaitForNotifications method that basically does the same thing as the validation rule. The advantage with this is that you can use an extraction rule to get data out of the last response and then use that data in validating your signalR messages. If you still need to fail a test in your WaitForNotification method use WebTest.InternalSetOutcome(Outcome.Fail);
The best way to load test a SignalR application is by building on the crank project included in the source.
This is a simple ramp up solution built with the .Net client but it is relatively easy to modify to call whatever hub methods you require and to analyse the responses.
You can always attach the Visual Studio profiler to your iis express instance to get detailed profiling data if required.
Related
This article shows a well-known problem with HttpClient that can lead to socket exhaustion.
I have an ASP.NET Core 3.1 web application. In a .NET Standard 2.0 class library I've added a WCF web service reference in Visual Studio 2019 following this instructions.
In a service I'm using the WCF client the way it's described in the documentation. Creating an instance of the WCF client and then closing the client for every request.
public class TestService
{
public async Task<int> Add(int a, int b)
{
CalculatorSoapClient client = new CalculatorSoapClient();
var resultat = await client.AddAsync(a, b);
//this is a bad way to close the client I should also check
//if I need to call Abort()
await client.CloseAsync();
return resultat;
}
}
I know it's bad practice to close the client without any checks but for the purpose of this example it does not matter.
When I start the application and make five requests to an action method that uses the WCF client and then take a look at the result from netstat I discover open connections with status TIME_WAIT, much like the problems in the article above about HttpClient.
It looks to me like using the WCF client out-of-the-box like this can lead to socket exhaustion or am I missing something?
The WCF client inherits from ClientBase<TChannel>. Reading this article it looks to me like the WCF client uses HttpClient. If that is the case then I probably shouldn't create a new client for every request, right?
I've found several articles (this and this) talking about using a singleton or reusing the WCF client in some way. Is this the way to go?
###UPDATE
Debugging the appropriate parts of the WCF source code I discovered that a new HttpClient and HttpClientHandler were created each time I created a new WCF client which I do for every request.
You can inspect the code here
internal virtual HttpClientHandler GetHttpClientHandler(EndpointAddress to, SecurityTokenContainer clientCertificateToken)
{
return new HttpClientHandler();
}
This handler is used in to create a new HttpClient in the GetHttpClientAsync method:
httpClient = new HttpClient(handler);
This explains why the WCF client in my case behaves just like a HttpClient that is created and disposed for every request.
Matt Connew writes in an issue in the WCF repo that he has made it possible to inject your own HttpMessage factory into the WCF client.
He writes:
I implemented the ability to provide a Func<HttpClientHandler,
HttpMessageHandler> to enable modifying or replacing the
HttpMessageHandler. You provide a method which takes an
HttpClientHandler and returns an HttpMessageHandler.
Using this information I injected my own factory to be able to control the generation of HttpClientHandlers in HttpClient.
I created my own implementation of IEndpointBehavior that injects IHttpMessageHandlerFactory to get a pooled HttpMessageHandler.
public class MyEndpoint : IEndpointBehavior
{
private readonly IHttpMessageHandlerFactory messageHandlerFactory;
public MyEndpoint(IHttpMessageHandlerFactory messageHandlerFactory)
{
this.messageHandlerFactory = messageHandlerFactory;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
Func<HttpClientHandler, HttpMessageHandler> myHandlerFactory = (HttpClientHandler clientHandler) =>
{
return messageHandlerFactory.CreateHandler();
};
bindingParameters.Add(myHandlerFactory);
}
<other empty methods needed for implementation of IEndpointBehavior>
}
As you can see in AddBindingParameters I add a very simple factory that returns a pooled HttpMessageHandler.
I add this behavior to my WCF client like this.
public class TestService
{
private readonly MyEndpoint endpoint;
public TestService(MyEndpoint endpoint)
{
this.endpoint = endpoint;
}
public async Task<int> Add(int a, int b)
{
CalculatorSoapClient client = new CalculatorSoapClient();
client.Endpoint.EndpointBehaviors.Add(endpoint);
var resultat = await client.AddAsync(a, b);
//this is a bad way to close the client I should also check
//if I need to call Abort()
await client.CloseAsync();
return resultat;
}
}
Be sure to update any package references to System.ServiceModel.* to at least version 4.5.0 for this to work. If you're using Visual Studio's 'Add service reference' feature, VS will pull in the 4.4.4 versions of these packages (tested with Visual Studio 16.8.4).
When I run the applications with these changes I no longer have an open connection for every request I make.
You should consider disposing your CalculatorSoapClient. Be aware that a simple Dispose() is usually not enough, becaue of the implementation of the ClientBase.
Have a look at https://learn.microsoft.com/en-us/dotnet/framework/wcf/samples/use-close-abort-release-wcf-client-resources?redirectedfrom=MSDN, there the problem is explained.
Also consider that the underlying code is managing your connections, sometimes it will keep them alive for later use. Try calling the server a lot of times to see, if there is a new connection for each call, or if the connections are being reused.
The meaning TIME_WAIT is also discussed here:
https://superuser.com/questions/173535/what-are-close-wait-and-time-wait-states
https://serverfault.com/questions/450055/lot-of-fin-wait2-close-wait-last-ack-and-time-wait-in-haproxy
It looks like your client has done everything required to close the connection and is just waiting for the confirmation of the server.
You should not have to use a singleton since the framework is (usually) taking good care of the connections.
I created an issue in the WCF repository in Github and got some great answers.
According to Matt Connew and Stephen Bonikowsky who are authorities in this area the best solution is to reuse the client or the ChannelFactory.
Bonikowsky writes:
Create a single client and re-use it.
var client = new ImportSoapClient();
And Connew adds:
Another possibility is you could create a channel proxy instance from
the underlying channelfactory. You would do this with code similar to
this:
public void Init()
{
_client?.Close();
_factory?.Close();
_client = new ImportSoapClient();
_factory = client.ChannelFactory;
}
public void DoWork()
{
var proxy = _factory.CreateChannel();
proxy.MyOperation();
((IClientChannel)proxy).Close();
}
According to Connew there is no problem reusing the client in my ASP.NET Core web application with potentially concurrent requests.
Concurrent requests all using the same client is not a problem as long
as you explicitly open the channel before any requests are made. If
using a channel created from the channel factory, you can do this with
((IClientChannel)proxy).Open();. I believe the generated client also
adds an OpenAsync method that you can use.
UPDATE
Since reusing the WCF Client also means reusing the HttpClient instance and that could lead to the known DNS problem I decided to go with my original solution using my own implementation of IEndpointBehavior as described in the question.
As we know that Rebus provides Topic Based routing in addition to the familiar TypeBased routing although we are told that the TypeBased routing follows the same principle.
On my side however unfortunately I have not seen a good example on how to create a handler that processes messages published to a particular topic.
Suppose I publish my message as follows
var message=new Student { StudentID=90008,FirstName="Chilipo",LastName="Mjengo" };
await bus.Advanced.Topics.Publish("rebus_example_topic", message);
In another endpoint I have subscribed to the topic as follows
await bus.Advanced.Topics.Subscribe("rebus_example_topic");
My interest is to know how do I then implement the Handler that will process the messages published to the rebus_example_topic.
Regards
It's quite simple, actually 🙂 the preferred way of writing message handlers is to implement IHandleMessage<TMessage>, where TMessage is your message type.
In your case that would be
public class StudentHandler : IHandleMessages<Student>
{
public async Task Handle(Student message)
{
// handle your message in here
}
}
How you then activate your message handler depends on which handler activator, you're using. The "handler activator" is what you use to instantiate message handlers, so you just need to register your handler in that to make it available to Rebus.
Some container integrations even come with additional help in the form of registration extensions, so e.g. if you're using Autofac, you can register your handler like this:
containerBuilder.RegisterHandler<StudentHandler>();
I have a web application which has few charts on dashboard. The data for charts is fetched on document.ready function at client side invoking a WCF service method.
What i want is now to use SignalR in my application. I am really new to SignalR. How can i call WCF methods from SignalR Hub or what you can say is that instead of pulling data from server i want the WCF service to push data to client every one minute.
Is there a way of communication between signalR and WCF service.
Also another approach can be to force client to ask for data from WCF Service every minute.
Any help will be really appreciated.
I have done following as of yet.
Client Side Function on my Dashboard page
<script src="Scripts/jquery.signalR-2.0.3.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
<a id="refresh">Refresh</a>
$(function() {
var dashboardHubProxy = $.connection.dashboardHub;
$.connection.hub.start().done(function() {
// dashboardHubProxy.server.refreshClient(parameters);
$("#refresh").click(function() {
dashboardHubProxy.server.refreshClient(parameters);
});
});
dashboardHubProxy.client.refreshChart = function (chartData) {
debugger;
DrawChart(chartData, 'Hourly Call Count For Last ' + Duration + ' Days', '#chartHourly', 'StackedAreaChart');
};
});
and my Dashboard Hub class is as follows
public class DashboardHub : Hub
{
private readonly ReportService ReportService = new ReportService();
public void RefreshClient(string parameters)
{
var chartData = ReportService.GenerateHourlyCallsTrendGraphicalReport(parameters);
Clients.All.refreshChart(chartData);
}
}
My SignalR startup class is as follows
[assembly: OwinStartup(typeof(CallsPortalWeb.Startup), "Configuration")]
namespace CallsPortalWeb
{
public static class Startup
{
public static void Configuration(IAppBuilder app)
{
ConfigureSignalR(app);
}
public static void ConfigureSignalR(IAppBuilder app)
{
app.MapSignalR();
}
}
}
When i click on refresh button and a debugger on RefreshClient method on hub the debugger doesn't get to the method which means i am unable to call server side method of SignalR.
Is there anything needs to be done in web.config?
I agree with AD.Net's comment. To elaborate slightly more though, the SignalR hubs can be hosted directly in your web project kinda the same way controllers are used. There is also a package out there so you can host the SignalR library on its own so it can act as a service all on its own. Either way you will need to hit the SignalR hub first as that is how it communicates then you would call your WCF service methods from within the hubs.
Brief explanation
Your HUB will have methods used by both your USER Client and your WCF Client. You may use something like UserConnected() for the user to call in and setup your logging of the connection. Then the WCF service may call your HUB with an UpdateUserStats(Guid connnectionId, UserStats stats) which would in turn call the USER client directly and provide the stats passed in like so Clients.Client(connectionId).updateStats(stats) which in turn would have a method on the USERS client named updateStats() that would handle the received information.
Initial page landing
What AD.Net provided is basic code that will be called when the user lands on the page. At this point you would want to log the ConnectionId related to that user so you can directly contact them back.
First contact with your hub touching WCF
From your Hub, you could call your WCF service as you normally would inside any normal C# code to fetch your data or perform action and return it to your user.
Method of updating the user periodically
SignalR removes the need for your client code to have to continually poll the server for updates. It is meant to allow you to push data out to the client with out them asking for it directly. This is where the persistence of the connections come into play.
You will probably want to create a wrapper to easily send messages to the hub from your application, since you are using WCF I would assume you have your business logic behind this layer so you will want the WCF service reaching out to your Hub whenever action X happens. You can do that by utilizing the Client side C# code as in this case your client is actually the user and the WCF service. With a chat application the other user is basically doing what you want your WCF service to do, which is send a message to the other client.
Usage example
You are running an online store. The dashboard displays how many orders there have been for the day. So you would wire up a call to the hub to send a message out to update the products ordered when a user places a new order. You can do this by sending it to the admin group you have configured and any admins on the dashboard would get the message. Though if these stats are very user specific, you will more then likely instead reach into the database, find the ConnectionId that the user has connected with and send the update message directly to that connectionid.
WCF Client Code Example
Just incase you want some code, this is directly from MS site on connecting with a .net client. You would use this in your WCF service, or wherever in your code you plan on connecting and then sending an update to your user.
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));
await hubConnection.Start();
Here is a link directly to the .Net Client section: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-net-client
I am sure you have seen this link but it really holds all the good information you need to get started. http://www.asp.net/signalr
Here is a more direct link that goes into usages with code for you. http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server
ADDED: Here is a blog specific to Dashboards with SignalR and their polling.
http://solomon-t.blogspot.com/2012/12/signalr-and-interval-polling-for.html
ADDED: Here is a page on managing users signalR connections.
http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections
Update for your code update
The .Net Client library (in NuGet) gives your .net code access to the hub. Since you are a client you will need to connect to the hub just like the User who is also a client. Your hub would act as the server for this. So with the .Net Client I am assuming you would setup a windows service that would internally poll, or something event based that would call the .Net Client code portion of it which would reach out to your hub. Your hub would take the information provided, more than likely a ConnectionId or GroupId and broad cast the User (which is perhaps on a website so it would be the JS client) a method that would update the front end for the user client. Basically what I mention under "Brief Explanation".
Now, to directly respond to the code you posted. That is Javascript, I would expect a connect like you have done. Updating the chart on initial connection is fine as well. If this is all the code signalR wise though you are missing a client side method to handle the refresh. Technically, instead of calling Clients.Caller.RefreshChart() you could just return that data and use it, which is what your javascript is doing right now. You are returning void but it is expecting a your date.
Now, I would actually say correct your javascript instead of correcting the hub code. Why? Because having a method in JS on your client that is called "refreshChart()" can be reused for when you are having your server reach out and update the client.
So I would recommend, dropping anything that is related to updating the dashboard in your JS done statement. If you want to do a notification or something to the user that is fine but dont update the grid.
Now create a JS client function called "refreshChart", note the lower case R, you can call it with a big R in c# but the js library will lowercase it so when you make the function have it will receive your dashboard information.
Now, on the server polling, or executing on some action, your WCF would call a method on the hub that would be say "UpdateDashboar(connectionId,dashInfo)" and that method would then inside of it call the "refreshChart" just like you are doing in your RefreshClient method, accept instead of doing Clients.Caller you would use Clients.Client(connectionId).refreshChart(chartInfo).
Directly the reason your code is not working is because you need to turn that Void into the type you expect to be returned. If the rest is coded right you will have it update once. You will need to implement the other logic I mentioned if you want it constantly updating. Which is again why I asked about how you are persisting your connections. I added a link to help you with that if you are not sure what I am talking about.
You should use the SignalR Hub to push data to the client. Your hub can consume a WCF service (the same way your client can) to get the data.
from client:
hub.VisitingDashBoard();
on the hub in the VisitingDashBoard method:
var data = wcfClient.GetDashboardData()//may be pass the user id from the context
Clients.Caller.UpdateDashboard(data)
Of course your client will have a handler for UpdateDashboard call
Ive got a WCF service which has multiple web methods in it. I want to be able to intercept the request on all methods and look at the Ip address. Id rather not put the logic into a method call at the top of each called web method is there a way to intercept all calls to these methods from one place?
If it was a page I would write a base page object but im nout sure if there are events raised on a wcf call?
WCF allows you to implement interceptors that are added to the stack. See this link for an example. I´m not sure whether this allows you the extract the senders IP but I think it´s worth a try.
You can implement IDispatchMessageInspector and do something like this.
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
{
RemoteEndpointMessageProperty remoteEndpoint = request.Properties
[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
//remoteEndpoint.Address will give you the address.
return null;
}
You can use Custom Behaviors, they are part of WCF Extensibility features. Here's more information: Extending WCF with Custom Behaviors
There's a clever way to do this with the ServiceAuthorizationManager, and it's far easier than all the seriously hard work of the IDispatchMessageInspector.
Create a class in your WCF Service project like so:
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
string classMethod = operationContext.RequestContext.RequestMessage.Headers.Action;
if (classMethod.Contains("/transfer/Get"))
{
return true; // because someone is simply updating a client service reference
}
Console.WriteLine("Class Method Call: {0}",classMethod);
// do something with operationContext here as you need to inspect stuff
// return true if you want this class method call to succeed and go through
// return false if you want this class method to fail on the client
return true;
}
}
Then, in your service, right before your host.Open() call, add the link to MyServiceAuthorizationManager.
ServiceHost host = new ServiceHost(typeof(MyProject.Service1));
host.Authorization.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
host.Open();
Now when you test your client connections, you'll notice that the console outputs what class method was called. You can also work against all the stuff in the operationContext object.
One way I use this is for a security header check. In my client, I add a header. Then, in the service, in this CheckAccessCore() call, I verify that this custom header exists. If it doesn't, then I return false. This is one more layer of protection that keeps the hackers out, and is great for the limited security in Named Pipes configurations too. If you're wanting to also do that, then click here for more information on how to add custom headers that automatically get sent on every client's method call on the service.
And note, among all this, I didn't have to mess with behaviors, claims, listeners, or message dispatches. I also didn't need to edit my WCF Configuration.
Note the string check for /transfer/Get above. This is important if you're doing header checks as a security mechanism like I was. If you don't have that condition and return true, then your WCF client IDE can't update its ServiceReference because the IDE doesn't know about that extra header (if you're adding a custom header and not specifying that header in the WCF client's app.config). Otherwise, you'll get an error The URI prefix is not recognized.
I want my asp.net mvc framework system to send an e-mail everytime a certain action (inside a certain controller) is fired off. Are there any third party libraries or .net standard ways to accomplish this?
A more up to date method would be to use System.Net.Mail - this is the 2.0 replacement for System.Web.Mail.
Something like this, called from either a BaseController (if there are other controllers that need this) the actual controller in question.
I have the following code inside a static class to handle mailing simple plain text items from the server:
internal static void SendEmail(MailAddress fromAddress, MailAddress toAddress, string subject, string body)
{
var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
};
var client = new SmtpClient("smtpServerName");
client.Send(message);
}
Obviously, you'd probably want some error handling etc in there - Send can throw an exception for example if the server is refusing connections.
Create a BaseController from which all your other controllers inherits.
In the BaseController override the OnActionExecuted Method and insert your code for sending the email.
public class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
// Send mail here
base.OnActionExecuted(filterContext);
}
}
The SmtpClient Class with the other System.Net.Mail classes are easily utilized from any .NET program to send mail. You just need to point it to an available and willing SMTP server.
Well its not really hard to send a Email using .NET. You can just send the mail from inside your action.
But, I think we talk little about logging here, and for logging there is a range of 3th party libraries. I know there is one called Log4Net.
Most of these logging frameworks makes it possible to config how logs are stored, and porsibly also a setting to send a email, when it logs something.
But in your scenario, it would just write a plain simple mail function, that sends the mail, when the user enters the action. You can make look at: http://www.developer.com/net/asp/article.php/3096831 - its a demo of sending a mail using .NET - webforms though, but the basic things still apply to MVC.