Sirwan Afifi Sirwan Afifi - 2 months ago 26
ASP.NET (C#) Question

How to report file progress to clients using SignalR?

I have written a component that gets and excel file and stores its data into a database. Since I wanted this class to support different environments (for example using it inside a console app) So I decided to create some events:

public interface IDataSeeder
{
Task Seed(string fileName);
event EventHandler<UserSucceedEventArgs> UserAdded;
event EventHandler<UserErrorEventArgs> Error;
event EventHandler<UserUpdateEventArgs> UserUpdated;
event EventHandler<FinishDataSeederEventArgs> ProcessCompleted;
}


Each of this events will trigger in different places inside
Seed
method. It works like a charm in my console application.

Now I want to use this component inside my ASP.NET MVC app, for doing so I decided to use SignalR for pushing event's data to client, So I created a hub like this:

public class ProgressHub : Hub
{
public static void UserAdded(UserSucceedEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.userAdded(e);
}

public static void Error(UserErrorEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.error(e);
}

public static void UserUpdated(UserUpdateEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.userUpdated(e);
}

public static void ProcessCompleted(FinishDataSeederEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.processCompleted(e);
}
}


Then I created this action method for getting uploaded file and pass it to my component:

[HttpPost]
public async Task<ActionResult> AddFromExcel(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
var fileName = Utilities.UploadFile.UploadFile.UploadCommonFile(excelFile, "users");
_dataSeeder.UserAdded += DataSeeder_Succeed;
_dataSeeder.Error += DataSeeder_Error;
_dataSeeder.UserUpdated += DataSeeder_Update;
_dataSeeder.ProcessCompleted += DataSeeder_Finish;
await _dataSeeder.Seed(Server.MapPath($"~/Content/Files/users/{fileName}"));
return RedirectToAction("AddFromExcel");
}
return RedirectToAction("List");
}

private static void DataSeeder_Finish(object sender, FinishDataSeederEventArgs e)
{
ProgressHub.ProcessCompleted(e);
}

private static void DataSeeder_Update(object sender, UserUpdateEventArgs e)
{
ProgressHub.UserUpdated(e);
}

private static void DataSeeder_Error(object sender, UserErrorEventArgs e)
{
ProgressHub.Error(e);
}

private static void DataSeeder_Succeed(object sender, UserSucceedEventArgs e)
{
ProgressHub.UserAdded(e);
}


As you can see inside each event handler I notify the clients using my signalr hub.

All of this process is like a messaging system, but I have no idea how to implement it inside a web application. A flaw with my code is that after attaching the event handler I immediately redirect the user to another action method, I know it must be an asynchronous process but I have no idea how to make my events async.

Any idea?

Ted Ted
Answer

What you have to do is match the SignalR server calls with JavaScript functions that will show the results to the connected clients:

<script type="text/javascript">
   // the proxy to your hub
   var hub = $.connection.ProgressHub;

   // the function that will be called when you call
   // ctx.Clients.All.userAdded(e);
   hub.client.userAdded = function (data) {
      // show the data
      alert(data);
   };

   // follow suit with the rest of the functions
   hub.client.error = function (data) {
      alert(data);
   };

   // etc

</script>