Hub
is the key component for SignalR on the server. We put the key functional logic
in the hub class. So, it is very much possible that we will have to access
database or files on the server from the hub class. This dependency may make
the hub less testable.
To avoid such difficulties, we will have to implement Dependency Inversion Principle. The hub has to contain an abstract reference of the dependency. An object of the concrete implementation of the abstract type will be injected into the hub using an Inversion of Control (IoC) container. This makes the hub less dependent on the concrete implementation, hence testable in isolation. Inversion of Control is a huge topic to discuss. If you are new to IoC, read this nice post by Joel Abrahamsson. If you have a subscription to Pluralsight, watch the course on Inversion of Control by John Sonmez.
In .NET, we inject the dependencies into a class using a library known as Inversion of Control container or IoC container. There are several implementations of IoC containers. Some of them are:
Usage of the IoC containers differs from each other. Before using any of these libraries, we have to learn and understand the syntax using which we configure the types and the way in which we have to resolve them.
To avoid such difficulties, we will have to implement Dependency Inversion Principle. The hub has to contain an abstract reference of the dependency. An object of the concrete implementation of the abstract type will be injected into the hub using an Inversion of Control (IoC) container. This makes the hub less dependent on the concrete implementation, hence testable in isolation. Inversion of Control is a huge topic to discuss. If you are new to IoC, read this nice post by Joel Abrahamsson. If you have a subscription to Pluralsight, watch the course on Inversion of Control by John Sonmez.
In .NET, we inject the dependencies into a class using a library known as Inversion of Control container or IoC container. There are several implementations of IoC containers. Some of them are:
- Ninject
- Unity
- Structure Map
- Castle Windsor
- MEF
Usage of the IoC containers differs from each other. Before using any of these libraries, we have to learn and understand the syntax using which we configure the types and the way in which we have to resolve them.
SignalR has a dependency resolver class (SignalR.DefaultDependencyResolver).
This class is used by the SignalR library to resolve all the dependencies. You
may check the source code on GitHub to know what this class does. To inject the dependencies in
our application, we have to write a class that derives from the DefaultDependencyResolver
class. We have to override the GetService
and GetServices methods. The logic
in these methods is responsible to accept the abstract type and return an
object of the concrete implementation.
For example, say the hub class talks to a database. In
enterprise applications, we use the Repository Pattern
to perform database operations. Say ICustomerRepository
is a repository interface and CustomerRepository
is the concrete implementation of this interface.
public interface ICustomerRepository { void AddCustomer(Customer customer); void ModifyCustomer(Customer customer); IEnumerable<Customer> GetCustomersByDepartment(string departmentId); } public class CustomerRepository : ICustomerRepository { //concrete implementations of the methods }
Following snippet shows an incomplete implementation of a Hub using the above repository:
public class CustmerHub : Hub { ICustomerRepository repository; public CustmerHub(ICustomerRepository repository) { this.repository = repository; } … }
An object of CustomerRepository type has to be injected into the CustomerHub from an external agent. Add the NuGet package for Ninject to the application.
Now we need to create our own dependency resolver by extending DefaultDependencyResolver. Following is the implementation for Ninject:
public class NinjectDependencyResolver:DefaultDependencyResolver { private readonly IKernel _kernel; public NinjectDependencyResolver(IKernel kernel) { if (kernel == null) { throw new ArgumentNullException("kernel"); } _kernel = kernel; } public override object GetService(Type serviceType) { var service = _kernel.TryGet(serviceType) ?? base.GetService(serviceType); return service; } public override IEnumerable<object> GetServices(Type serviceType) { var services = _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType)); return services; } }
We have to configure requested types and target types so that Ninject will be able to return the right objects when they are requested. Following class does this for us:
public static class NinjectIoC { public static IKernel Initialize() { IKernel kernel = new StandardKernel(); kernel.Bind<ICustomerRepository>().To<CustomerRepository>(); kernel.Bind<RequestedType>().To<TargeTtype>(); return kernel; } }
Finally, we need to set the resolver to RouteTable.Route.MapHubs when the application starts.
public class Global : System.Web.HttpApplication { void Application_Start(object sender, EventArgs e) { // Code that runs on application startup GlobalHost.DependencyResolver = new NinjectDependencyResolver(NinjectIoC.Initialize()); RouteTable.Routes.MapHubs(); } }
With this step, we have set the dependency resolver to SignalR. Whenever it needs to resolve a dependency, the control will be passed to NinjectDependencyResolver to create the object.
Important note: If you are using SignalR in an ASP.NET MVC project, then configure SignalR first and then ASP.NET MVC. Check SignalR wiki for more details.
Happy coding!
I have a generic Repository IGenericRepository and GenericRepository if I am referencing this class with the repository in my Winform client and also in my SelfHost-Windows Service , do I need dependency injection ? How would I do this with SignalR
ReplyDelete