25

I'm trying to use SignalR with MVC bundle, but having problem finding out how to include the /signalr/hubs script into the bundle. For now I have to insert the path in between jquery.signalR and my code. That will result in three javascript file requests.

Is there any way to include /signalr/hubs into my mvc bundle?

tereško
  • 58,060
  • 25
  • 98
  • 150
thenail
  • 438
  • 1
  • 5
  • 10

6 Answers6

49

A bit late, but here is my contribution:
Create a javascript file with the following contents:

(function ($) {
    $.ajax({
        url: "/signalr/hubs",
        dataType: "script",
        async: false
    });
}(jQuery));

Then add the file to the bundles collection.
This will load the "/signalr/hubs" code for you.

KTW
  • 719
  • 2
  • 6
  • 10
  • 1
    cache: true if you want to benefit from client cache – Anders Aug 29 '13 at 11:40
  • 2
    This seems to work with IIS Express but will fail with a 404 error when you deploy to full IIS. [How to reference the dynamically generated proxy](http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#dynamicproxy) – Mathew Leger Apr 29 '14 at 16:40
  • @KTW Why "async: false" ? – sports Jun 11 '14 at 01:20
  • @sports because you don't want other things that depend on it firing before it is loaded. – Daryl Teo Jun 21 '14 at 13:21
  • 3
    @DarylTeo and in terms of efficiency, this is equal to putting a script html tag in the head? – sports Jun 21 '14 at 17:08
  • @sports I'm afraid I'm not qualified to answer that. It won't load in the head though, I think. – Daryl Teo Jun 23 '14 at 01:30
  • Perhaps I don't understand the code. But isn't signalr/hubs just downloaded separately and not in the bundle? So the only benefit is that you save a line of – Karsten Mar 30 '15 at 09:13
  • I have no words to thank you enough – Natan Fernandes Nov 19 '21 at 17:53
14

The default /signalr/hubs script is generated dynamically by the runtime on the first request and then cached.

You can use hubify.exe (see http://weblogs.asp.net/davidfowler/archive/2012/06/10/signalr-0-5-1-released.aspx for details) to pre-generate the file yourself, so you can add it into the MVC bundle.

Alexander Köplinger
  • 2,497
  • 17
  • 15
  • 1
    This works well in most cases. Except if the site is hosted in a virtual directory. The hubs js generates this `$.hubConnection("/signalr" ...` which will not work if the site is in a virtual directory. – John McKim Jun 05 '13 at 01:25
  • Went to the link above and the link to generate hubify.exe is in https://github.com/SignalR/SignalR/tree/master/SignalR.ProxyGenerator and that does not work – Sandeep Nov 16 '17 at 20:52
  • It's not super well documented but you can do this now using Microsoft.AspNet.SignalR.Utils. Quick steps: * Install Microsoft.AspNet.SignalR.Utils using nuget * Add something like this to your Post build event: "$(SolutionDir)packages\Microsoft.AspNet.SignalR.Utils.2.2.2\tools\signalr" ghp "$(TargetPath)" XCOPY "$(TargetDir)server.js" "$(ProjectDir)Scripts\hubs" /R /Y – Robert MacGrogan Jan 29 '18 at 22:50
2

I know this is an old thread but I would like to add the following for SignalR 2.x. I really wanted to bundle the proxy using SquishIt and by trial and error I managed to come up with the following:

using Microsoft.AspNet.SignalR
using Microsoft.AspNet.SignalR.Hubs

var resolver = new DefaultHubManager(new DefaultDependencyResolver());
var proxy = new DefaultJavaScriptProxyGenerator(resolver, new NullJavaScriptMinifier());
string iCanHazScriptNao = proxy.GenerateProxy("/signalr");
1

From asp.net, using the SignalR.Utils NuGet package, I found that I needed to be in the directory with the DLL that has the hub in it:

(assuming you have a standard solution structure and are using 2.2.0 of SignalR.Utils)

cd C:\YourSolution\YourProjectWithTheHub\bin\Debug
..\..\..\packages\Microsoft.AspNet.SignalR.Utils.2.2.0\tools\signalr.exe ghp

After running the tool, there will be a server.js file in the directory you ran it from (in this case, Debug).

(Note: I couldn't get it to work when specifying the path with the /p flag, and for some reason even when it does work, it creates a temp directory with the signalr.exe file in it)

John B
  • 20,062
  • 35
  • 120
  • 170
1

You can generate the code with the Microsoft.AspNet.SignalR.Utils NuGet package. Add that to your project, then you can add the below as a post-build script (Project -> {project name} Properties -> Build Events). It should be a post-build script and not pre- since you want it to build against your updated hub code after it's compiled.

It will find whatever version of the Microsoft.AspNet.SignalR.Utils package that you have installed and put the server.js file in the Scripts folder.

You must also have a version redirect for Newtonsoft.Json in your web.config file (assuming your project uses Newtonsoft.Json). This is because signalr.exe is built against version 6.0.0 and you're likely using a newer version. The /configFile switch is to tell it to use your project's config file so that it uses the redirect.

Post-build script:

cd $(ProjectDir)\Scripts
FOR /F "usebackq delims=" %%p IN (`dir ..\..\packages /b /ad ^| find "Microsoft.AspNet.SignalR.Utils"`) DO (
  set "UTILSPATH=%%p"
)
$(SolutionDir)\packages\%UTILSPATH%\tools\net40\signalr.exe ghp /path:$(TargetDir) /configFile:$(TargetPath).config

Then include ~/Scripts/server.js in your bundle.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
0

I used @KTW response mentioned on this Thread and here is the complete change

BundleConfig

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/modernizr-2.6.2.js",
            "~/Scripts/jquery-2.2.3.js",
            "~/Scripts/jquery-ui-1.11.4.js",
            "~/Scripts/jquery.multiselect.js",
            "~/Scripts/jquery.dataTables.js",
            "~/Scripts/jquery.jstepper.min.js",
            "~/Scripts/underscore.min.js"
        ));

        bundles.Add(new ScriptBundle("~/bundles/SignalRScripts").Include(
            "~/Scripts/jquery.signalR-2.2.2.min.js",
            "~/Scripts/signalRBundle.js",
            "~/Scripts/Views/Search/SignalRFunctions.js"));
    }
}

SignalRFunctions.js

    $(function() {
    // Declare a proxy to reference the hub.
    var usersHub = $.connection.currentUsersHub;
    //Create a function that the hub can call to broadcast messages.
    usersHub.client.broadcastMessage = function(reservationNumber, usrName) {
        //Message broadcast from server
        //now find the id with reservationNumber on the page and to that append the user name
        var id = '#' + reservationNumber;
        if ($(id).length) {
            if (usrName.length) {
                itemsOpened($(id), usrName);
            } else {
                itemsClosed($(id));
            }
        } 
        //else {
        //    //is it possible that server broad casted for a reservationNumber and is not available at the client?
        //}
    };

    //Accepts dictionary from hub and goes through search results
    //https://stackoverflow.com/questions/7776337/reading-c-sharp-dictionary-in-javascript
    usersHub.client.broadcastCollection = function (reservationNumberAndUsers) {
       for (var resNumKey in reservationNumberAndUsers) {
            if (reservationNumberAndUsers.hasOwnProperty(resNumKey)) {
                //Message broadcast from server
                //now find the id with ReservationNumber on the page and to that append the user name
                var id = '#' + resNumKey;
                if ($(id).length) {
                    if (reservationNumberAndUsers[resNumKey].length) {
                        itemsOpened($(id), reservationNumberAndUsers[resNumKey]);
                    } else {
                        itemsClosed($(id));
                    }
                }
            }
        }
    };

    $.connection.hub.start().done(function() {
                var searchedReservationNumbers = [];
                if (typeof jsData !== 'undefined') {
                    if (jsData && jsData.length) {
                        for (var i = 0; i < jsData.length; i++) {
                            searchedReservationNumbers.push(jsData[i].UReservationNumber);
                        }
                        if (searcheduReservationNumbers.length !== 0) {
                            usersHub.server.getWorkingUsersOnUReservationNumber(searcheduReservationNumbers);
                        }
                    }
                }
            }).fail(function () { console.log('Could not Connect To SignalrHub!'); });
        /*In case we would decide to continuously reconnect making connection to server.
        $.connection.hub.disconnected(function() {
            setTimeout(function() {
                    $.connection.hub.start();
                },
                5000); // Restart connection after 5 seconds.
        });*/

function itemsOpened(elem, id) {
    var item = "Opened By - " + id;
    elem.prop('title', item);
    elem.css('background-color', 'chocolate');
};

function itemsClosed(elem) {
    elem.prop('title', "");
    elem.css('background-color', '');
};
});

signalRBundle.js

(function ($) {
$.ajax({
    url: "/signalr/hubs",
    dataType: "script",
    async: false
});
}(jQuery));
/* Source https://stackoverflow.com/questions/11556110/signalr-and-mvc-bundle */

SomePartialView.cshtml Instead of writing below in above partial view

@using Localization
@using Newtonsoft.Json
@model NameSpace.ViewModels.FilterVM

@{
    ViewBag.Title = Strings.Filter;
}

@using (Html.BeginForm())
{
    <div class="large-12 columns">
        ---SOME CODE HERE 
    </div>

}

@section scripts
{
<script type="text/javascript" language="javascript">
    var jsData = @Html.Raw(JsonConvert.SerializeObject(Model));
</script>
<script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Scripts/Views/Search/SignalRFunctions.js"></script>
}

This changed to

 @using Localization
    @using Newtonsoft.Json
    @model NameSpace.ViewModels.FilterVM

    @{
        ViewBag.Title = Strings.Filter;
    }

    @using (Html.BeginForm())
    {
        <div class="large-12 columns">
            ---SOME CODE HERE 
        </div>

    }

    @section scripts
    {
    <script type="text/javascript" language="javascript">
        var jsData = @Html.Raw(JsonConvert.SerializeObject(Model));
    </script>
    @Scripts.Render("~/bundles/SignalRScripts")
    }

Notice

@Scripts.Render("~/bundles/SignalRScripts") 

in partial view above.Without @KTW file above(ajax request to /signalr/hubs)

var usersHub = $.connection.currentUsersHub;

was always coming as null.

Sandeep
  • 615
  • 6
  • 13
  • 1
    As @mathew leger pointed out below This seems to work with IIS Express but will fail with a 404 error when you deploy to full IIS. – Sandeep Dec 14 '17 at 17:46