"
ASP.NET (snapshot 2017) Microsoft documentation and samples

Dependency Injection in SignalR

by Mike Wasson, Patrick Fletcher

Software versions used in this topic

Previous versions of this topic

For information about earlier versions of SignalR, see SignalR Older Versions.

Questions and comments

Please leave feedback on how you liked this tutorial and what we could improve in the comments at the bottom of the page. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET SignalR forum or StackOverflow.com.

Dependency injection is a way to remove hard-coded dependencies between objects, making it easier to replace an object’s dependencies, either for testing (using mock objects) or to change run-time behavior. This tutorial shows how to perform dependency injection on SignalR hubs. It also shows how to use IoC containers with SignalR. An IoC container is a general framework for dependency injection.

What is Dependency Injection?

Skip this section if you are already familiar with dependency injection.

Dependency injection (DI) is a pattern where objects are not responsible for creating their own dependencies. Here is a simple example to motivate DI. Suppose you have an object that needs to log messages. You might define a logging interface:

[!code-csharpMain]

   1:  interface ILogger 
   2:  {
   3:      void LogMessage(string message);
   4:  }

In your object, you can create an ILogger to log messages:

[!code-csharpMain]

   1:  // Without dependency injection.
   2:  class SomeComponent
   3:  {
   4:      ILogger _logger = new FileLogger(@"C:\logs\log.txt");
   5:   
   6:      public void DoSomething()
   7:      {
   8:          _logger.LogMessage("DoSomething");
   9:      }
  10:  }

This works, but it’s not the best design. If you want to replace FileLogger with another ILogger implementation, you will have to modify SomeComponent. Supposing that a lot of other objects use FileLogger, you will need to change all of them. Or if you decide to make FileLogger a singleton, you’ll also need to make changes throughout the application.

A better approach is to “inject” an ILogger into the object—for example, by using a constructor argument:

[!code-csharpMain]

   1:  // With dependency injection.
   2:  class SomeComponent
   3:  {
   4:      ILogger _logger;
   5:   
   6:      // Inject ILogger into the object.
   7:      public SomeComponent(ILogger logger)
   8:      {
   9:          if (logger == null)
  10:          {
  11:              throw new NullReferenceException("logger");
  12:          }
  13:          _logger = logger;
  14:      }
  15:   
  16:      public void DoSomething()
  17:      {
  18:          _logger.LogMessage("DoSomething");
  19:      }
  20:  }

Now the object is not responsible for selecting which ILogger to use. You can swich ILogger implementations without changing the objects that depend on it.

[!code-csharpMain]

   1:  var logger = new TraceLogger(@"C:\logs\log.etl");
   2:  var someComponent = new SomeComponent(logger);

This pattern is called constructor injection. Another pattern is setter injection, where you set the dependency through a setter method or property.

Simple Dependency Injection in SignalR

Consider the Chat application from the tutorial Getting Started with SignalR. Here is the hub class from that application:

[!code-csharpMain]

   1:  public class ChatHub : Hub
   2:  {
   3:      public void Send(string name, string message)
   4:      {
   5:          Clients.All.addMessage(name, message);
   6:      }
   7:  }

Suppose that you want to store chat messages on the server before sending them. You might define an interface that abstracts this functionality, and use DI to inject the interface into the ChatHub class.

[!code-csharpMain]

   1:  public interface IChatRepository
   2:  {
   3:      void Add(string name, string message);
   4:      // Other methods not shown.
   5:  }
   6:   
   7:  public class ChatHub : Hub
   8:  {
   9:      private IChatRepository _repository;
  10:   
  11:      public ChatHub(IChatRepository repository)
  12:      {
  13:          _repository = repository;
  14:      }
  15:   
  16:      public void Send(string name, string message)
  17:      {
  18:          _repository.Add(name, message);
  19:          Clients.All.addMessage(name, message);
  20:      }

The only problem is that a SignalR application does not directly create hubs; SignalR creates them for you. By default, SignalR expects a hub class to have a parameterless constructor. However, you can easily register a function to create hub instances, and use this function to perform DI. Register the function by calling GlobalHost.DependencyResolver.Register.

[!code-csharpMain]

   1:  public void Configuration(IAppBuilder app)
   2:  {
   3:      GlobalHost.DependencyResolver.Register(
   4:          typeof(ChatHub), 
   5:          () => new ChatHub(new ChatMessageRepository()));
   6:   
   7:      App.MapSignalR();
   8:   
   9:      // ...
  10:  }

Now SignalR will invoke this anonymous function whenever it needs to create a ChatHub instance.

IoC Containers

The previous code is fine for simple cases. But you still had to write this:

[!code-csharpMain]

   1:  ... new ChatHub(new ChatMessageRepository()) ...

In a complex application with many dependencies, you might need to write a lot of this “wiring” code. This code can be hard to maintain, especially if dependencies are nested. It is also hard to unit test.

One solution is to use an IoC container. An IoC container is a software component that is responsible for managing dependencies.You register types with the container, and then use the container to create objects. The container automatically figures out the dependency relations. Many IoC containers also allow you to control things like object lifetime and scope.

[!NOTE] “IoC” stands for “inversion of control”, which is a general pattern where a framework calls into application code. An IoC container constructs your objects for you, which “inverts” the usual flow of control.

Using IoC Containers in SignalR

The Chat application is probably too simple to benefit from an IoC container. Instead, let’s look at the StockTicker sample.

The StockTicker sample defines two main classes:

StockTickerHub holds a reference to the StockTicker singleton, while StockTicker holds a reference to the IHubConnectionContext for the StockTickerHub. It uses this interface to communicate with StockTickerHub instances. (For more information, see Server Broadcast with ASP.NET SignalR.)

We can use an IoC container to untangle these dependencies a bit. First, let’s simplify the StockTickerHub and StockTicker classes. In the following code, I’ve commented out the parts that we don’t need.

Remove the parameterless constructor from StockTickerHub. Instead, we will always use DI to create the hub.

[!code-csharpMain]

   1:  [HubName("stockTicker")]
   2:  public class StockTickerHub : Hub
   3:  {
   4:      private readonly StockTicker _stockTicker;
   5:   
   6:      //public StockTickerHub() : this(StockTicker.Instance) { }
   7:   
   8:      public StockTickerHub(StockTicker stockTicker)
   9:      {
  10:          if (stockTicker == null)
  11:          {
  12:              throw new ArgumentNullException("stockTicker");
  13:          }
  14:          _stockTicker = stockTicker;
  15:      }
  16:   
  17:      // ...

For StockTicker, remove the singleton instance. Later, we’ll use the IoC container to control the StockTicker lifetime. Also, make the constructor public.

[!code-csharpMain]

   1:  public class StockTicker
   2:  {
   3:      //private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
   4:      //    () => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
   5:   
   6:      // Important! Make this constructor public.
   7:      public StockTicker(IHubConnectionContext<dynamic> clients)
   8:      {
   9:          if (clients == null)
  10:          {
  11:              throw new ArgumentNullException("clients");
  12:          }
  13:   
  14:          Clients = clients;
  15:          LoadDefaultStocks();
  16:      }
  17:   
  18:      //public static StockTicker Instance
  19:      //{
  20:      //    get
  21:      //    {
  22:      //        return _instance.Value;
  23:      //    }
  24:      //}

Next, we can refactor the code by creating an interface for StockTicker. We’ll use this interface to decouple the StockTickerHub from the StockTicker class.

Visual Studio makes this kind of refactoring easy. Open the file StockTicker.cs, right-click on the StockTicker class declaration, and select RefactorExtract Interface.

In the Extract Interface dialog, click Select All. Leave the other defaults. Click OK.

Visual Studio creates a new interface named IStockTicker, and also changes StockTicker to derive from IStockTicker.

Open the file IStockTicker.cs and change the interface to public.

[!code-csharpMain]

   1:  public interface IStockTicker
   2:  {
   3:      void CloseMarket();
   4:      IEnumerable<Stock> GetAllStocks();
   5:      MarketState MarketState { get; }
   6:      void OpenMarket();
   7:      void Reset();
   8:  }

In the StockTickerHub class, change the two instances of StockTicker to IStockTicker:

[!code-csharpMain]

   1:  [HubName("stockTicker")]
   2:  public class StockTickerHub : Hub
   3:  {
   4:      private readonly IStockTicker _stockTicker;
   5:   
   6:      public StockTickerHub(IStockTicker stockTicker)
   7:      {
   8:          if (stockTicker == null)
   9:          {
  10:              throw new ArgumentNullException("stockTicker");
  11:          }
  12:          _stockTicker = stockTicker;
  13:      }

Creating an IStockTicker interface isn’t strictly necessary, but I wanted to show how DI can help to reduce coupling between components in your application.

Add the Ninject Library

There are many open-source IoC containers for .NET. For this tutorial, I’ll use Ninject. (Other popular libraries include Castle Windsor, Spring.Net, Autofac, Unity, and StructureMap.)

Use NuGet Package Manager to install the Ninject library. In Visual Studio, from the Tools menu select Library Package Manager | Package Manager Console. In the Package Manager Console window, enter the following command:

[!code-powershellMain]

   1:  Install-Package Ninject -Version 3.0.1.10

Replace the SignalR Dependency Resolver

To use Ninject within SignalR, create a class that derives from DefaultDependencyResolver.

[!code-csharpMain]

   1:  internal class NinjectSignalRDependencyResolver : DefaultDependencyResolver
   2:  {
   3:      private readonly IKernel _kernel;
   4:      public NinjectSignalRDependencyResolver(IKernel kernel)
   5:      {
   6:          _kernel = kernel;
   7:      }
   8:   
   9:      public override object GetService(Type serviceType)
  10:      {
  11:          return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
  12:      }
  13:   
  14:      public override IEnumerable<object> GetServices(Type serviceType)
  15:      {
  16:          return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
  17:      }
  18:  }

This class overrides the GetService and GetServices methods of DefaultDependencyResolver. SignalR calls these methods to create various objects at runtime, including hub instances, as well as various services used internally by SignalR.

Configure Ninject Bindings

Now we’ll use Ninject to declare type bindings.

Open your application’s Startup.cs class (that you either created manually as per the package instructions in readme.txt, or that was created by adding authentication to your project). In the Startup.Configuration method, create the Ninject container, which Ninject calls the kernel.

[!code-csharpMain]

   1:  var kernel = new StandardKernel();

Create an instance of our custom dependency resolver:

[!code-csharpMain]

   1:  var resolver = new NinjectSignalRDependencyResolver(kernel);

Create a binding for IStockTicker as follows:

[!code-csharpMain]

   1:  kernel.Bind<IStockTicker>()
   2:      .To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>()  // Bind to StockTicker.
   3:      .InSingletonScope();  // Make it a singleton object.

This code is saying two things. First, whenever the application needs an IStockTicker, the kernel should create an instance of StockTicker. Second, the StockTicker class should be a created as a singleton object. Ninject will create one instance of the object, and return the same instance for each request.

Create a binding for IHubConnectionContext as follows:

[!code-csharpMain]

   1:  kernel.Bind(typeof(IHubConnectionContext<dynamic>)).ToMethod(context =>
   2:                      resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
   3:                       ).WhenInjectedInto<IStockTicker>();

This code creatres an anonymous function that returns an IHubConnection. The WhenInjectedInto method tells Ninject to use this function only when creating IStockTicker instances. The reason is that SignalR creates IHubConnectionContext instances internally, and we don’t want to override how SignalR creates them. This function only applies to our StockTicker class.

Pass the dependency resolver into the MapSignalR method by adding a hub configuration:

[!code-csharpMain]

   1:  var config = new HubConfiguration();
   2:  config.Resolver = resolver;
   3:  Microsoft.AspNet.SignalR.StockTicker.Startup.ConfigureSignalR(app, config);

Update the Startup.ConfigureSignalR method in the sample’s Startup class with the new parameter:

[!code-csharpMain]

   1:  public static void ConfigureSignalR(IAppBuilder app, HubConfiguration config)
   2:  {
   3:      app.MapSignalR(config);
   4:  }

Now SignalR will use the resolver specified in MapSignalR, instead of the default resolver.

Here is the complete code listing for Startup.Configuration.

[!code-csharpMain]

   1:  public class Startup
   2:  {
   3:      public void Configuration(IAppBuilder app)
   4:      {
   5:          // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
   6:   
   7:          var kernel = new StandardKernel();
   8:          var resolver = new NinjectSignalRDependencyResolver(kernel);
   9:   
  10:          kernel.Bind<IStockTicker>()
  11:              .To<Microsoft.AspNet.SignalR.StockTicker.StockTicker>()  // Bind to StockTicker.
  12:              .InSingletonScope();  // Make it a singleton object.
  13:   
  14:          kernel.Bind(typeof(IHubConnectionContext<dynamic>)).ToMethod(context =>
  15:                  resolver.Resolve<IConnectionManager>().GetHubContext<StockTickerHub>().Clients
  16:                      ).WhenInjectedInto<IStockTicker>();
  17:   
  18:          var config = new HubConfiguration();
  19:          config.Resolver = resolver;
  20:          Microsoft.AspNet.SignalR.StockTicker.Startup.ConfigureSignalR(app, config);
  21:      }
  22:  }

To run the StockTicker application in Visual Studio, press F5. In the browser window, navigate to http://localhost:*port*/SignalR.Sample/StockTicker.html.

The application has exactly the same functionality as before. (For a description, see Server Broadcast with ASP.NET SignalR.) We haven’t changed the behavior; just made the code easier to test, maintain, and evolve.



Comments ( )
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnet/signalr/overview/advanced/dependency-injection.htm
< THANKS ME>