How can I store and persist multiple remoting sessions in an MVC application? - asp.net-mvc

Imagine a monitoring website that connects to 50 services via .net remoting and polls them to check they are working and find out what they're up to, and display it.
The page uses ajax to hit the backend and the backend then connects to each one.
Ideally, I don't want it to open a connection, do the request, close the connection, for 50 connections, on a 5 second interval (unless this is the best practice way?). So I'd like MVC to persist the connections.
I can make a ConnectionManager static singleton to handle this (not sure where?), opening and closing connections as required/idling.
You could even have a ConnectionManager handle the server status, so if multiple people load the status webpage, the results are cached.
But is there a better way, especially one that is scaleable? You could have multiple ConnectionManager classes in a webfarm scenario as they won't clash afaik (you can have multiple connections open).
ATM I am doing this in my ajax methods:
TcpChannel tcpChannel = new TcpChannel();
ChannelServices.RegisterChannel(tcpChannel, false);
Type requiredType = typeof(ICatServerMarshaller);
ICatServerMarshaller remoteObject = (ICatServerMarshaller)Activator.GetObject(requiredType,
"tcp://localhost:4567/CatServerMarshaller");
string catStatus;
try
{
catStatus = remoteObject.GetCatStatus().ToString();
}
catch (SocketException ex)
{
catStatus = "Offline";
}
finally
{
ChannelServices.UnregisterChannel(tcpChannel);
}
return Json(new { catStatus = catStatus});

Related

ASP .NET MVC websocket client to save data in DB

I am struggling to achieve the following:
I have created a Java websocket server which publishes data every 1 sec.
In ASP MVC projest I would like to receive the data and save them in database only so no JS involved here.
I am able to read the websocket data using console application method below :
using WebSocketSharp;
List<string> readoutList = new List<string>();
public void receiveMessage() {
using (var ws = new WebSocket("ws://localhost:4567/socket/"))
{
ws.OnMessage += (sender, e) =>
{
if (e.IsText)
{
readoutList.Add(e.Data.ToString());
Console.WriteLine(e.Data.ToString());
}
};
ws.Connect();
Console.ReadKey(true);
}
}`
How to create a service of this kind within the ASP MVC project? I need some direction here.
MVC is stateless so you have to request back to the server to initiate the request (such as from a form post) but within the MVC controller response, you can kick off the request to the server (as an example using a different technology). The problem is there isn't necessarily a 1 to 1 translation in MVC; initiating the request using client-side JavaSvcript would be the option here. Initiating these types of requests within MVC may cause issues with timeouts too that you have to be aware of.
OR, you can consider a scheduled windows program or a windows service that is installed, which can manage itself and initiate the request every so often. I think this is the better option.

OWIN asynchronous startup (using Hangfire)

I am using Hangfire with SQL Storage on a remote SQL server and running it alongside my existing MVC site. My startup class is very simple:
public void Configuration(IAppBuilder app)
{
app.UseHangfire(config =>
{
config.UseSqlServerStorage("MY_CONNECTION_STRING");
config.UseServer();
});
}
The problem is that any delay in connecting to the remote server delays my MVC site from spinning up. Is there a way to start OWIN asynchronously so that the project is able to respond to requests regardless of what happens during the OWIN startup, including fatal errors?
Hangfire initialization logic is performed in a dedicated thread to decrease the start-up time of your application. So, UseServer method creates a new thread only, without any additional logic.
UseSqlServerStorage method connects to your database to check your current schema to run automatic migrations if necessary (one simple query to the Hangfire.Schema table). This is the default behavior, however you are able to disable it:
var options = new SqlServerStorageOptions
{
PrepareSchemaIfNecessary = false
};
var storage = new SqlServerStorage("<name or connection string>", options);
After performing this step, Hangfire will not connect to your database at startup (and no other class will do it). But keep an eye on release notes, they will contain information about database storage changes.

webapi odata update savechanges issue - Unable to connect to remote server

In my mvc webapplication, I am using webapi to connect to my database through odata.
Both MVC WebApp and Odata WebApi are on different ports of Azure cloud service webrole endpoints.
MVC WebApp - 80
Odata WebApi - 23900
When I do a odataproxy updateobject and call savechanges like
odataProxy.UpdateObject(xxx);
odataProxy.SaveChanges(System.Data.Services.Client.SaveChangesOptions.PatchOnUpdate);
I am getting a weird exception on savechanges method call - unable to connect to remote server.
When I tried to look into inner exceptions, It says that - No connection could be made as the target machine actively refused it 127.0.0.1:23901
So if you observe the port number in the exception, it shows as 23901 and obviously this error should come as the request is supposed to hit 23900.
I am facing this exception only when running on azure cloud solution. Whenever I do an update request, it fails by hitting a wrong port (added by 1).
Another thing is, apart from this updateobject -> savechanges, rest all works like fetching data and adding data.
FWIW, I've just run across this same thing. Darn near annoying and I really hope it doesn't happen in production. I'm surprised no other people have come across this though.
The idea of creating a new context, attaching the object(s) and calling SaveChanges really repulsed me because not only does it practically break all forms of testing, it causes debug code and production code to be fundamentally different.
I was however able to work around this problem in another way, by intercepting the request just before it goes out and using reflection to poke at some private fields in memory to "fix" the port number.
UPDATE: It's actually easier than this. We can intercept the request generation process with the BuildingRequest event. It goes something like this:
var context = new Context(baseUri);
context.BuildingRequest += (o, e) =>
{
FixPort(e);
};
Then the FixPort method just needs to test the port number and build a new Uri, attaching it back to the event args.
[Conditional("DEBUG")]
private static void FixPort(BuildingRequestEventArgs eventArgs)
{
int localPort = int.Parse(LOCAL_PORT);
if (eventArgs.RequestUri.Port != localPort)
{
var builder = new UriBuilder(eventArgs.RequestUri);
builder.Port = localPort;
eventArgs.RequestUri = builder.Uri;
}
}
Here's the original method using reflection and SendingRequest2, in case anyone is still interested.
First we create a context and attach a handler to the SendingRequest2 event:
var context = new Context(baseUri);
context.SendingRequest2 += (o, e) =>
{
FixPort(e.RequestMessage);
};
The FixPort method then handles rewriting the URL of the internal request, where LOCAL_PORT is the port you expect, in your case 23900:
[Conditional("DEBUG")]
private static void FixPort(IODataRequestMessage requestMessage)
{
var httpWebRequestMessage = requestMessage as HttpWebRequestMessage;
if (httpWebRequestMessage == null) return;
int localPort = int.Parse(LOCAL_PORT);
if (httpWebRequestMessage.HttpWebRequest.RequestUri.Port != localPort)
{
var builder = new UriBuilder(requestMessage.Url);
builder.Port = localPort;
var uriField = typeof (HttpWebRequest).GetField("_Uri",
BindingFlags.Instance | BindingFlags.NonPublic);
uriField.SetValue(httpWebRequestMessage.HttpWebRequest, builder.Uri);
}
}
I have found the root cause and a temporary workaround.
Cause:
When you hit WebApi through some port :23900 in Azure compute emulator and do an update or delete operation, somehow the last request is blocking the port and because of the port walking feature in Azure emulator, it is jumping to next port where there is no service available which is causing the issue.
Even this issue is found only in development emulators.
Temp Workaround:
Use a different proxy to attach to updated context object and then save from the other proxy object.
var odataProxy1 = xxx;
var obj = odataProxy1.xyz.FirstOrDefault();
obj.property1="abcd";
...//Other update assignments
var odataProxy2 = xxx;
odataProxy2.AttachTo("objEntitySet",obj);
odataProxy2.UpdateObject(obj)
odataProxy2.SaveChanges(ReplaceOrUpdate);

mvc4 acting as a gateway

what i want to achieve is:
a central server connected to the database, using entity framework
a server who for some reason can't reach the database but forward the requests to the central server (not all of them only the one who require the database)
some httpclients who can't reach the central server nor the database but only the middle server
I've already tried with success modifying the controller method to create an http client who redo the reuest to the cenral server, but that seems the worst way to me, especially because i've lots of controllers and methods
public User GetUser(int id)
{
if (Properties.Settings.Default.SyncEnabled)
{
System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient();
httpClient.BaseAddress = Properties.Settings.Default.SyncAddress;
var result = httpClient.GetAsync(this.Request.Url.PathAndQuery).Result;
return this.Content(result.Content.ReadAsStringAsync().Result, result.Content.Headers.ContentType.MediaType);
}
else
{
User user = DbContext.Users.Find(id);
user.LastOnline = DateTime.Now;
DbContext.SaveChanges();
return user;
}
}
i was thinking about using register route, but i'd like to know if it's a good idea, before reading how routes works...
i'm also intrested in how would you implement that.
Since nobody gave me an answer i'm gonna suggest myself to try with this:
http://www.iis.net/learn/extensions/url-rewrite-module/reverse-proxy-with-url-rewrite-v2-and-application-request-routing
seems that reverse proxy can do the trick, but i need to do it with two separate iis

SignalR client disconnect/connect on every refresh of pages. ASP.NET MVC

I am creating a chat embedded in my asp.net mvc 4 project. I have an online users ul list which add a user on OnConnected and remove it on OnDisconnected.
So, my app isn't a SinglePage app, which means that it refreshes on pages all the time.
I am encountering some difficulties to treat with this online users list on the client side, because signalr calls OnDisconnected and OnConnected on every page refresh.
While the other client is navigating normally in app, it keep being removed and added on every refresh of page.
How to avoid this behavior on client?
I am trying to do some like this, on client which are running the page with usersOnline list...
var timeout;
chat.client.login = function (chatUser) {
addUser(chatUser);
window.clearTimeout(timeout);
};
chat.client.logout = function (chatUser) {
timeout = setTimeout(function () { removeUser(chatUser.Id); }, 3000);
};
But I am suffering to deal with multi-users scenario... Because if more than one user executes the hub onDisconnected before the timeout runs, the second will override the instance of the first.
There is indeed no real way around this. A client will always disconnect when leaving a page, and connects to SignalR again when the next page is loaded.
The only way around this is to create a SPA, so SignalR doesn't need to be disconnected by navigating away.
Using the idea of SignalR hubs is to allow real-time actions with minimal programming or complications - the best way would be for SignalR to be pulling from a list of currently logged in users, not active connections, as that could have the same user multiple times.
Therefore, I suggest, instead of OnConnected and OnDisconnected, put it in your AccountController, in the LogIn and LogOut methods. For example:
public ActionResult LogIn()
{
//other stuff
var hub = GlobalHost.ConnectionManager.GetHubContext</*Hub Title*/>();
hub.client.chat.login()
}
public ActionResult LogOut()
{
// other stuff
var hub = GlobalHost.ConnectionManager.GetHubContext</*Hub Title*/>();
hub.client.chat.logout()
}

Resources