Subversion: Neue Revisionsnummer automatisch im Quelltext aktualisieren

Die Versionsverwaltung Subversion bzw. SVN ist unter Softwareentwickern allgemein bekannt. Auf dem Windows-Desktop wird sie meist in Form von TortoiseSVN eingesetzt, welches eine gute Integration in die Windows-Umbebung bietet.

Zum Verwalten der Versionen eines Projekts verwendet Subversion fortlaufende Revisionsnummern. Um Fehler in bereits freigegebenen Softwareständen zu untersuchen, ist es oft sehr hilfreich, wenn man weiß aus welcher Quelltext-Revision die jeweilige Software gebaut wurde. Ideal wäre es, wenn das Programm seine eigene Revisionsnummer z.B. in der Titelleiste oder einem Info-Dialog anzeigen würde.

Diese Anleitung zeigt, wie man die Revisionsnummer mit TortoiseSVN automatisch in einer Quelltextdatei aktualisieren lassen kann.

Vorbereitung

  1. Erstellen Sie ein neues SVN-Repository.
  2. Erstellen Sie ein neues Arbeitsverzeichnis auf ihrem PC und checken Sie das Repository darin aus. (Rechtsklick in den Ordner > SVN Checkout)
  3. Erstellen Sie mit dem VisualStudio ein neues beliebiges Projekt in dem Ordner.

Revisionsnummer in Datei einfügen

Wir werden nun eine neue Datei namens Revision.cs erstellen, in welcher die Revisionsnummer automatisch aktualisiert wird. Wird diese Datei anschließend zu einem Projekt hinzugefügt, ist die Nummer dann als Konstante zur Laufzeit im Programm verfügbar.

Erstellen Sie zunächst eine Datei namens Revision.cs.tmpl im obersten Verzeichnis des Arbeitsverzeichnisses (nicht in einem Projekt-Order) mit einem Texteditor und fügen Sie diesen Quelltext ein:

using System;

public static class Revision
{
    private const int REVISION = $WCREV$;
    
    public static int Get()
    {
        return REVISION;
    }
}

Öffnen Sie nun eine Konsole (Start > Ausführen > cmd.exe) und wechseln Sie in Ihr Arbeitsverzeichnis (cd \pfad\zum\verzeichnis ). Geben Sie nun folgenden Befehl ein:

SubWCRev . Revision.cs.tmpl Revision.cs

Subversion hat nun basierend auf Ihrer Template-Datei Revision.cs.tmlp eine neue Datei namens Revision.cs erstellt. In dieser Datei wurde das $WCREV$ ausgetauscht durch die aktuelle Versionsnummer. Diese Datei können wir nun im VisualStudio zu unserem Projekt hinzufügen:

  1. Öffnen Sie das VisualStudio.
  2. Klicken Sie mit der rechten Maustaste im Solution Explorer auf das Projekt und wählen Sie Add > Existing Item und wählen Sie die Datei Revision.cs aus.
  3. Klicken Sie NICHT auf Add, sondern auf den kleinen Pfeil neben dem Button und wählen Sie Add as Link. Wenn Sie stattdessen auf Add klicken, wird eine Kopie der Revision.cs im Projektverzeichnis erstellt, in der die Revisionsnummer nicht aktualisiert wird. Daher: Add as Link.

Gehen wir nun davon aus, bei Ihrer Anwendung handelt es sich um eine Konsolenanwendung, welche beim Start ihre Revisionsnummer auf dem Bildschirm anzeigen soll. Dies erreichen Sie in dem Sie die Datei Program.cs wie folgt anpassen:

namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Revisionsnummer: " + Revision.Get());
            Console.ReadLine();
        }
    }
}

Wir können nun jederzeit durch den Aufruf von SubWCRev (siehe oben) unsere Revision.cs aktualisieren lassen. Damit die Revisionsnummer in der Datei immer aktuell ist, müssen wir also dafür sorgen, dass SubWCRev vor jedem Build oder nach jedem SVN Commit oder SVN Update automatisch aufgerufen wird.

Automatisieren mit TortoiseSVN

In den Einstellungen von TortoiseSVN können wir den automatischen Aufruf nach einem Commit/Update einrichten. Erstellen Sie zunächst eine neue Datei namens update-rev.bat im gleichen Verzeichnis wie die Revision.cs.tmpl mit folgendem Inhalt:

cd %1
del Revision.cs
SubWCRev . Revision.cs.tmpl Revision.cs

Beim Aufruf dieser Datei wechselt das Skript zunächst in das als Parameter übergebene Verzeichnis (welches das Arbeitsverzeichnis sein muss) und löscht die alte Revision.cs. Anschließend wird eine neue aktuelle Revision.cs erzeugt.

TortoiseSVN soll diese Datei nun bei jeder Revisionsänderung aufrufen. Dies richten Sie wie folgt ein:

  1. Klicken Sie mit der rechten Maustaste in das Arbeitsverzeichnis im Windows-Explorer.
  2. Wählen Sie im PopUp-Menü TortoiseSVN > Settings.
  3. In dem erscheinenden Fenster wählen Sie auf der linken Seite Hook Scripts
  4. Klicken Sie auf Add um ein neues Hook Skript hinzuzufügen.
  5. In dem neuen Fenster wählen Sie als Hook Type: Post Update Hook und geben sie in das Feld Working Copy Path den Pfad zu Ihrem Arbeitsverzeichnis ein (z.B. „C:\pfad\zum\arbeitsverzeichnis“)
  6. In dem Feld Command Line To Execute tragen Sie den vollständigen Pfad zur update-rev.bat gefolgt von dem Pfad zum Arbeitsverzeichnis als Parameter. Also z.B. „C:\pfad\zum\arbeitsverzeichnis\update-rev.bat C:\pfad\zum\arbeitsverzeichnis“. Bestätigen Sie im OK.
  7. Legen Sie noch ein neues Hook Skript an und wählen Sie als Hook Type: Post Commit Hook. Tragen Sie in die Felder Working Copy Path und Command Line To Execute genau den gleichen Inhalt wie bei dem vorherigen Skript ein.

Die Datei updaterev.bat wird nun von TortouseSVN automatisch nach jedem Update und jedem Commit aufgerufen und bekommt dabei als Parameter das Arbeitsverzeichnis. Mit cd %1 wechselt sie in das Arbeitsverzeichnis, da %1 für den ersten Parameter des Skripts steht. Anschließend wird die Revision.cs aktualisiert.

 

Erstellen einer ASP.NET MVC Webapplikation mit Castle Windsor, Entity Framework und SignalR inklusive SignalR-Client

Dieser Beitrag ist eine kurze allgemeine Anleitung zum Erstellen einer MVC Webapplikation mit folgenden Komponenten:

  • Eine SQL-Datenbankanbindung mit dem Entity Framework
  • Dependency Injection mit Castle Windsor
  • Einer SignalR-API über die Daten mit einem Client ausgetauscht werden können

Weiterhin wird in dieser Anleitung ein minimaler .NET Client für die SingalR-API entwickelt.

Anlegen eines neuen Projekts in Visual Studio 2017

  1. Wählen Sie File > New > Project im Menü und wählen Sie ASP.NET Web Application (.NET Framework) aus dem Ordner Visual C# / Web als Projekttemplate aus. Geben Sie einen Namen für die Webapplikation ein z.B. „MyWebApplication“ und klicken Sie auf OK.
  2. Im folgenden Fenster wählen Sie MVC als Projekttyp und klicken Sie OK.

Installieren von Castle Windsor

  1. Klicken Sie im Solution Explorer mit der rechten Maustaste auf das Projekt und wählen Sie Manage NuGet Packages im erscheinenden PopUp-Menü.
  2. Suchen Sie im Tab Browse nach dem Paket Castle.Windsor und installieren Sie dieses.

Um während der weiteren Installation überprüfen zu können, ob Castle Windsor funktioniert erstellen wir zunächst eine Beispiel-Klasse sowie ein Interface, welches wir anschließend in Castle Windsor registrieren und verwenden können. Unsere Beispiel-Klasse stellt zwei Methoden zum Addieren und Subtrahieren zweier Zahlen zur Verfügung.
Erstellen Sie zwei neue Dateien namens „ExampleDependency.cs“ und „IExampleDependency.cs“ mit folgendem Inhalt:

IExampleDependency.cs

namespace MyWebApplication
{
  public interface IExampleDependency
  {
    int add(int a, int b);
    int subtract(int a, int b);
  }
}

ExampleDependency.cs

namespace MyWebApplication
{
  public class ExampleDependency :IExampleDependency
  {
    public int add(int a, int b)
    {
      return a + b;
    }

    public int subtract(int a, int b)
    {
      return a - b;
    }
  }
}

Als nächstes erstellen wir eine Installer-Klasse für Castle Windsor, in der alle Abhängigkeiten registriert werden. Fügen Sie die folgende neue Datei samt Inhalt zum Projekt hinzu:

WindsorInstaller.cs

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using System.Web.Mvc;

namespace MyWebApplication
{
    public class WindsorInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
            container.Register(Component.For<IExampleDependency>().ImplementedBy<ExampleDependency>());
        }
    }
}

Die Klasse WindsorInstaller registriert alle Klassen, die von IController erben. Bei einem eingehenden Webrequest kann das zu dessen Verarbeitung erforderliche MVC-Controller-Objekt im Folgenden ebenfalls durch Castle Windsor erzeugt werden. Weiterhin wird unsere Klasse ExampleDependency als Singleton-Klasse registriert. Sie wird also nur bei der ersten Verwendung einmalig instanziiert. Sie können weitere Abhängigkeiten nach dem gleichen Schema hinzufügen.

Damit das Erzeugen der MVC-Controller-Objekte durch Castle Windsor erfolgt, müssen wir noch eine neue Controller-Factory schreiben, welche dazu einen Windsor-Container verwendet. Fügen Sie die dazu folgende neue Datei samt Inhalt zum Projekt hinzu:

WindsorControllerFactory.cs

using Castle.Windsor;
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace MyWebApplication
{
    public class WindsorControllerFactory : DefaultControllerFactory
    {
        WindsorContainer container;

        public WindsorControllerFactory(WindsorContainer container)
        {
            this.container = container;
        }

        public override void ReleaseController(IController controller)
        {
            container.Kernel.ReleaseComponent(controller);
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                throw new HttpException(404, "The controller for path " + requestContext.HttpContext.Request.Path + " could not be found.");
            }
            return (IController)container.Kernel.Resolve(controllerType);
        }
    }
}

Unsere Anwendung soll einen zentralen Windsor-Container zum Erstellen aller Objekte verwenden. In diesem Controller sollen beim Start der Anwendung alle Abhängigkeiten aus unserer Klasse WindsorInstaller registriert werden.
Dazu müssen Sie die Datei Global.asax.cs wie folgt bearbeiten:

  • Legen Sie eine neue Klassenvariable namens „container“ vom Typ WindsorContainer an.
  • In der Methode Application_Start erstellen Sie einen neuen WindsorContainer und führen unseren WindsorInstaller aus. (Siehe dazu die Kommentare unten im Quelltext)
  • Damit die Controller von unserer neuen Controller-Factory erzeugt werden, registrieren Sie diese im ControllerBuilder.
  • Legen Sie die Methode Application_End an, sofern diese noch nicht existiert, und rufen Sie darin die Dispose-Methode des Windsor-Containers auf.

Anschließend sieht Ihre Global.asax.cs z.B. wie folgt aus:

using Castle.Windsor;
using Castle.Windsor.Installer;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace MyWebApplication
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static WindsorContainer container;

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            container = new WindsorContainer(); // Neuen WindsorContainer erstellen
            container.Install(FromAssembly.This()); // Unseren WindsorInstaller ausführen
            //container.Install(FromAssembly.Named("Library")); // Bei Bedarf: Weitere WindsorInstaller aus z.B. zusätzlichen DLLs ausführen.

            ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container)); // Neue Factory für Controller registrieren
        }

        protected void Application_End()
        {
            container.Dispose();
        }
    }
}

Funktionstest
Testen wir nun, ob die Dependency Injection durch Castle Windsor in Ihrer neuen Applikation funktioniert. Ersetzen Sie den Inhalt der Datei HomeController.cs durch folgenden Quelltext:

using System.Web.Mvc;

namespace MyWebApplication.Controllers
{
    public class HomeController : Controller
    {
        IExampleDependency calculator;

        public HomeController(IExampleDependency calculator)
        {
            this.calculator = calculator;
        }


        public ActionResult Index()
        {
            return Content("Addieren zweier Zahlen: 5 + 3  = " + calculator.add(5, 3));
        }

    }
}

Wenn Sie nun die Webapplikation starten und mit dem Webbrowser aufrufen, sehen Sie folgenden Text im Webbrowser:

Addieren zweier Zahlen: 5 + 3 = 8

Perfekt! Alles funktioniert.

Zum besseren Verständnis erkläre ich noch einmal was genau beim Laden der Webseite passiert ist:

Der Webbrowser sendet eine Http-Anfrage an unsere Applikation. Um diese zu bearbeiten benötigt ASP.NET einen neuen HomeController, welcher durch unsere neue WindsorControllerFactory erstellt werden muss. Unsere WindsorControllerFactory beauftragt den WindsorContainer, welcher beim Start der Applikation angelegt wurde, damit das Objekt zu erstellen. Der WindsorContainer erkennt, dass zum Erstellen eines HomeControllers ein Objekt, welches das Interface IExampleDependency implementiert, vom Konstruktor benötigt wird. Durch den WindsorInstaller haben Sie vorgegeben, dass hierfür ein Objekt der Klasse ExampleDependency verwendet werden soll. Dieses Objekt wird ebenfalls vom WindsorContainer erstellt und der fertige HomeController zurück gegeben, welcher die Http-Anfrage verarbeitet.

Bei einer erneuten Http-Anfrage wird der WindsorContainer kein neues ExampleDependency-Objekt erstellen, da bereits ein Objekt dieser Klasse existiert und diese Klasse mit LifeStyleSingleton() im WindsorInstaller als Singleton-Klasse markiert wurde.

Installieren des Entity Frameworks

Im nächsten Schritt möchten wir in unserer Applikation eine Datenbank verwenden und dafür das Entity Framework benutzen:

  1. Klicken Sie im Solution Explorer mit der rechten Maustaste auf das Projekt und wählen Sie Manage NuGet Packages im erscheinenden PopUp-Menü.
  2. Suchen Sie im Tab Browse nach dem Paket EntityFramework und installieren Sie dieses.

Wir werden nun die Anwendung dahingehend erweitern, dass sie Angestellte einer Firma verwalten kann. Dazu soll sie Objekte vom Typ Employee in einer Datenbank speichern und daraus laden können. Fügen Sie die Klasse Employee zu Ihrem Projekt hinzu:

Employee.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace MyWebApplication
{
    public class Employee
    {
        [Key]
        public int Id { get; set; }
        public string Forename { get; set; }
        public string Surname { get; set; }
        public string Department {get; set;}
        public DateTime Birthday { get; set; }
        public decimal Salary { get; set; }
    }
}

Erstellen Sie nun eine DbContext-Klasse. Aus dieser Klasse entnimmt das Entity Framework, welche Tabellen in der Datenbank existieren sollen. In unserem Beispiel möchten wir nur eine Tabelle haben, deren Spalten den Properties aus der Klasse Employee entsprechen. Mit dem Attribut [Key] in der Employee-Klasse legen wir fest, dass Id der PrimaryKey in der Datenbanktabelle sein soll. Fügen Sie die folgende neue Datei zum Projekt hinzu:

AppDbContext.cs

using System.Data.Entity;

namespace MyWebApplication
{
    public class AppDbContext :DbContext
    {
        public AppDbContext() : base("Persist Security Info=False;Integrated Security=true;Initial Catalog=MyWebApplication;server=localhost")
        {
        }

        public DbSet<Employee> Employees { get; set; }
    }
}

Der ConnectionString, welcher mit base() an den Konstruktor von DbContext übergeben wird, legt die Verbindungseinstellungen mit der Datenbank fest. Damit wir unsere AppDbContext-Objeke von Castle Windsor verwalten lassen können, müssen wir die Klasse zu unserem WindsorInstaller hinzufügen. Fügen Sie dazu die Zeile 8 des folgenden Listings zur Klasse WindsorInstaller hinzu.

    public class WindsorInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
            container.Register(Component.For<IExampleDependency>().ImplementedBy<ExampleDependency>().LifestyleSingleton());

            container.Register(Component.For<AppDbContext>().LifestylePerWebRequest());
        }
    }

Zum Testen ändern Sie die Klasse HomeController wie folgt:

    public class HomeController : Controller
    {
        IExampleDependency calculator;
        AppDbContext ctx;

        public HomeController(IExampleDependency calculator, AppDbContext ctx)
        {
            this.calculator = calculator;
            this.ctx = ctx;
        }


        public ActionResult Index()
        {
            return Content("Addieren zweier Zahlen: 5 + 3  = " + calculator.add(5, 3));
        }


        public ActionResult AddEmployees()
        {
            ctx.Employees.Add(new Employee()
            {
                Forename = "Max",
                Surname = "Mustermann",
                Department = "Entwicklungsabteilung",
                Birthday = new DateTime(1986, 4, 1),
                Salary = 65000
            });

            ctx.Employees.Add(new Employee()
            {
                Forename = "Bernd",
                Surname = "Beispiel",
                Department = "Verwaltung",
                Birthday = new DateTime(1988, 5, 10),
                Salary = 40000
            });

            ctx.SaveChanges();
            return Content("Mitarbeiter wurden gespeichert!");
        }
    }
}

Wenn Sie nun die Methode AddEmployees im Webbrowser aufrufen, wird Ihre Webapplikation sich mit der Datenbank verbinden, die Tabelle anlegen und zwei neue Mitarbeiter darin speichern. Sie können dies z.B. mit dem Microsoft SQL-Server Management Studio überprüfen.

Installation von SignalR mit Dependency Injection durch Castle Windsor

Unsere Webapplikation soll weiterhin eine SignalR-API bereitstellen, über die Clients mit der Applikation Daten austauschen können.

  1. Klicken Sie im Solution Explorer mit der rechten Maustaste auf das Projekt und wählen Sie Manage NuGet Packages im erscheinenden PopUp-Menü.
  2. Suchen Sie im Tab Browse nach dem Paket Microsoft.AspNet.SignalR und installieren Sie dieses.

Bevor wir einen SignalR-Hub zu unserer Applikation hinzufügen, werden wir (analog zu der ControllerFactory für MVC-Controller) auch eine neue Factory für SignalR-Hubs schreiben, damit wir die Hubs ebenfalls durch Castle Windsor erzeugen lassen können. Erstellen Sie dazu eine neue Klasse namens SignalrHubFactory mit folgendem Inhalt:

SignalrHubFactory.cs

using Castle.Windsor;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR;

namespace MyWebApplication
{
    public class SignalrHubFactory : DefaultDependencyResolver
    {
        WindsorContainer container;
        public SignalrHubFactory(WindsorContainer container)
        {
            this.container = container;
        }
        public override object GetService(Type serviceType)
        {
            return container.Kernel.HasComponent(serviceType) ? container.Resolve(serviceType) : base.GetService(serviceType);
        }
        public override IEnumerable<object> GetServices(Type serviceType)
        {
            return container.Kernel.HasComponent(serviceType) ? container.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
        }
    }
}

Die SignalrHubFactory bekommt in ihrem Konstruktor eine Referenz auf den WindsorContainer. Sobald ein neuer Hub von ihr erstellt werden soll, versucht sie es zunächst über den WindsorContainer zu beziehen. Falls die gewünschte Klasse dort nicht verfügbar ist, wird sie mit dem DefaultDependencyResolver von SignalR erstellt.

Damit Castle Windsor alle Hubs für uns erstellen kann, müssen die Hub-Klassen noch im WindsorInstaller registriert werden. Fügen Sie dazu die Zeile 6 aus dem folgenden Listing in Ihre Klasse WindsorInstaller ein.

    public class WindsorInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
            container.Register(Classes.FromThisAssembly().BasedOn<Microsoft.AspNet.SignalR.Hub>().LifestyleTransient());
            container.Register(Component.For<IExampleDependency>().ImplementedBy<ExampleDependency>().LifestyleSingleton());

            container.Register(Component.For<AppDbContext>().LifestylePerWebRequest());
        }
    }

Nun erstellen Sie eine OWIN-Startup-Klasse, in der Sie die neue SignalrHubFactory als Resolver für SignalR-Hubs festlegen:
Startup.cs

using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(MyWebApplication.Startup))]

namespace MyWebApplication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var factory = new SignalrHubFactory(MvcApplication.container);
            var cfg = new HubConfiguration() { Resolver = factory, EnableDetailedErrors = true };
            app.MapSignalR(cfg);
        }
    }
}

Abschließend erstellen wir noch als Beispiel einen SignalR-Hub, welcher zwei Funktionen zum addieren und subtrahieren zweier Zahlen bereitstellt. Der Hub berechnet die Summen bzw. Differenzen nicht selbst, sondern greift dazu auf unsere ExampleDependency zurück, welche von Castle Windsor über den Konstruktor des Hubs bereit gestellt wird. Erstellen Sie eine neue Klasse namens ExampleHub.cs und fügen Sie den folgenden Inhalt darin ein:
ExampleHub.cs

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

namespace MyWebApplication
{
    [HubName("ExampleHub")]
    public class ExampleHub : Hub
    {
        IExampleDependency calculator;

        public ExampleHub(IExampleDependency calculator)
        {
            this.calculator = calculator;
        }

        public int Add(int a, int b)
        {
            return calculator.add(a, b);
        }

        public int Subtract(int a, int b)
        {
            return calculator.subtract(a, b);
        }
    }
}

Programmieren eines SignalR-Clients

Zum Testen unserer SignalR-Schnittstelle, werden wir eine einfache Client-Anwendung programmieren. Dazu erstellen wir eine neue Applikation für die Konsole, welche sich beim Start mit der API verbindet und mit ihrer Hilfe zwei Zahlen subtrahiert.

  1. Wählen Sie File > New > Project im Menü und wählen Sie Console App (.NET Framework) aus dem Ordner Visual C# als Projekttemplate aus. Geben Sie einen Namen für die Webapplikation ein z.B. „MyClient“ ein.
  2. Wählen Sie unten unter Solution „Add to Solution“ aus und klicken Sie auf OK.
  3. Klicken Sie im Solution Explorer mit der rechten Maustaste auf das Projekt und wählen Sie Manage NuGet Packages im erscheinenden PopUp-Menü.
  4. Suchen Sie im Tab Browse nach dem Paket Microsoft.AspNet.SignalR.Client und installieren Sie dieses.

Öffnen Sie die Datei Program.cs und fügen Sie folgenden Inhalt ein:

using Microsoft.AspNet.SignalR.Client;
using System;

namespace MyClient
{
    class Program
    {
        static void Main(string[] args)
        {
            using (HubConnection hubConnection = new HubConnection("http://localhost:57256/"))
            {
                IHubProxy proxy = hubConnection.CreateHubProxy("ExampleHub");
                hubConnection.Start().Wait();

                int d = proxy.Invoke<int>("Subtract", 10, 3).Result;
                Console.WriteLine("Subtrahieren zweier Zahlen: 10 - 3 = " + d);
            }
            Console.ReadLine();
        }
    }
}

Vergessen Sie nicht die Portnummer in Zeile 10 durch die Portnummer, auf der Ihre Webapplikation läuft, zu ersetzen.

Klicken Sie nun mit der rechten Maustaste im Solution Explorer auf Ihre Solution und wählen Sie im PopUp-Menü Set Startup Projects. Wählen Sie Multiple Startup Projects und stellen Sie beide Projekte (die Webapplikation und den Client) auf Start.

Wenn Sie nun Ihre Solution starten, werden beide Projekte ausgeführt. Die Webapplikation erscheint wie gehabt im Browserfenster. Der Client erscheint als Konsolenfenster und zeigt folgenden Text an:

Subtrahieren zweier Zahlen: 10 – 3 = 7

Im Folgenden soll der Ablauf noch einmal erklärt werden: Beim Start erstellt der Client einen Proxy, über den wir auf die Methoden des ExampleHubs zugreifen können. Durch den Aufruf von proxy.Invoke() rufen wir eine Methode auf dem Server auf (im Beispiel die Methode Subtract). Dazu wird zunächst eine Instanz des ExampleHubs benötigt, welche von unserer SignalrHubFactory mit dem WindsorContainer erstellt wird. Castle Windsor erkennt, dass zum erstellen eines ExampleHubs eine ExampleDependency benötigt wird, und stellt auch diese automatisch bereit.

Den volständigen Beispiel-Quellcode können Sie hier herunterladen: MyWebApplication.zip