Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
signalr / SignalR Programming in Microsoft ASP.NET.pdf
Скачиваний:
65
Добавлен:
25.05.2015
Размер:
19.23 Mб
Скачать

instance could even be active throughout the whole time the user is connected), and if we create dependencies that use valuable resources, these might be unavailable for other uses. In these cases, it is better to opt for a close control of the lifetime of such dependencies—for example, by obtaining them through the Service Locator and delimiting their use with a using block to ensure their release when they are no longer needed.

Releasing dependencies

Sometimes dependencies use external resources that have to be released. For example, a hub could use an Entity Framework data context to persist information in the database, and we should always ensure that both it and (hence) the underlying connection it uses have been closed and released.

Currently, SignalR provides no mechanism to automatically release dependencies (or, for example, those that implement IDisposable), so developers are responsible for ensuring that this happens. Generally, a good place to do this is in the implementation of the Dispose() method of hubs:

public class CustomerHub : Hub

{

...

protected override void Dispose(bool disposing)

{

if (disposing)

{

_customerRepository.Dispose(); _mailManager.Dispose();

}

}

}

It would be necessary to do this at least with direct dependencies of the hub that implement IDisposable. If these depend in turn on other components, the latter could be released here or from their respective Dispose() methods. In any case, it is also a task that must be performed with extreme caution, because there might be components shared between instances whose early release could cause problems.

Inversion of Control containers

With the techniques seen before, we now know how to inject dependencies in hubs by simply changing the content of the Dependency Resolver register, although this task can be really laborious if we have complex dependency graphs. For example, if our hub depends on an instance that implements the IService interface, the specific class to be used needs an instance of IRepository, and in turn this instance requires an IDataStore object. We can find ourselves with quite convoluted registers, because we have to manage these relationships ourselves:

GlobalHost.DependencyResolver.Register(

typeof (MyHub),

200 Chapter 9Advanced topics

www.it-ebooks.info

() => new MyHub(new MyService(new MyRepository(new MyDataStore())))

);

A concept that frequently appears hand-in-hand with Dependency Injection is that of Inversion of Control containers (IoC containers). These are components specializing in managing instances, life cycles, and dependencies between objects, something like Dependency Resolvers on steroids. Not only can they act as powerful Service Locators, but they also tend to be much more flexible when registering services, specifying instantiation modes (for example, deciding which objects will be created as singletons and which will be instantiated upon request), and solving dependencies between components automatically.

Going back to our previous example, where we had a dependency graph, if we were to request an instance of MyHub from the IoC container, the latter would be able to analyze its constructor and

determine that it needs an instance of IService. Thanks to its register, it would know that IService must be resolved to the MyService class, but also that the latter, in turn, requires an IRepository object in its constructor, so it would check its register again to see what class would satisfy the dependency, and so on until completing the dependency graph.

Obviously, this way of managing dependencies is much more convenient and productive than doing it manually.

There are many IoC containers on the market, although we could point out Unity, Ninject, Autofac, StructureMap, or Windsor Castle, and others, for their great popularity. Virtually all are open source products, basically very similar in concept and operation, and even quite similar to the operation of the Dependency Resolver that we have previously seen:

■■Application startup is used to register in the container associations between requested types and returned types, the way they are obtained, and other aspects.

■■At run time, when an instance of some type is needed by the application, the container is called to obtain it.

IoC containers are normally integrated with SignalR through the standard mechanism of dependency resolution, because this is the point already established for obtaining component instances. Usually, the default Dependency Resolver is replaced with a custom one, which uses an IoC container in the backstage to obtain the dependencies making use of the latter’s power.

Now we will see how to use dependency injection with two of these containers: Unity and Ninject. The dependency graph that we will use is the following:

Broadcaster <requires> IMessageFormatter

MessageFormatter <implements> IMessageFormatter <requires> IClock

The Broadcaster hub requires in its constructor an instance of IMessageFormatter, which materializes in the MessageFormatter class and whose constructor, in turn, requires an instance of

IClock. In all cases, the interfaces are a true reflection of the signature of the classes, so we will omit their code for the sake of brevity.

public class Broadcaster: Hub

{

Advanced topicsChapter 9

201

www.it-ebooks.info

private IMessageFormatter _formatter;

public Broadcaster(IMessageFormatter formatter)

{

_formatter = formatter;

}

public Task Broadcast(string message)

{

var formattedMsg = _formatter.Format(message); return Clients.All.Message(formattedMsg);

}

}

public class MessageFormatter : IMessageFormatter

{

private IClock _clock;

public MessageFormatter(IClock clock)

{

_clock = clock;

}

public string Format(string message)

{

return _clock.GetCurrentDateTime() + " > " + message;

}

}

public class Clock : IClock

{

public string GetCurrentDateTime()

{

return DateTime.Now.ToString("F");

}

}

SignalR with Unity

Unity3 is a powerful IoC container promoted by Microsoft Patterns & Practices. Like others, it can be installed very easily on our SignalR application via NuGet:

PM> Install-package Unity

Now we will create a Dependency Resolver inheriting from the class that SignalR provides: DefaultDependencyResolver. We take control in the methods used to obtain instances (GetService and GetServices), and we enter logic to search first in the IoC container and then, if appropriate, in the default register:

public class UnityDependencyResolver : DefaultDependencyResolver

{

private UnityContainer _container;

public UnityDependencyResolver(UnityContainer container)

{

3 http://unity.codeplex.com

202 Chapter 9Advanced topics

www.it-ebooks.info

_container = container;

}

public override object GetService(Type serviceType)

{

if (_container.IsRegistered(serviceType))

{

return _container.Resolve(serviceType);

}

return base.GetService(serviceType);

}

public override IEnumerable<object> GetServices(Type serviceType)

{

return _container.ResolveAll(serviceType)

.Concat(base.GetServices(serviceType));

}

}

We can now set this component as a dependency resolution mechanism for the application, sending an instance in the initial configuration object. Note that to instantiate it we need to have created a Unity container, which we will also use to register the components that the IoC container will be in charge of creating:

public void Configuration(IAppBuilder app)

{

var container = new UnityContainer(); container.RegisterType<IClock, Clock>(); container.RegisterType<IMessageFormatter, MessageFormatter>(); container.RegisterType<Broadcaster>();

app.MapSignalR(new HubConfiguration()

{

Resolver = new UnityDependencyResolver(container)

});

}

Note  Although in this example we are including the container configuration code directly on the Configuration() method, it would be much more appropriate to move all operations relating to the IoC to an independent class and, for example, enter them into an IocConfig.Setup() method, which would be invoked from this point.

In this case, the instances that will be created for all components through Unity will be single-use. Whenever a component is requested, a new instance of it will be created. If for any reason, such as performance, we want a specific component to be managed in singleton mode, we can specify this at the moment of registration. For example, the instances of IClock in our previous example could be the same for the entire application, so the register could be as follows:

container.RegisterType<IClock, Clock>(

new ContainerControlledLifetimeManager()

);

Advanced topicsChapter 9

203

www.it-ebooks.info

Thus, all calls made to the container requesting IClock type components will be answered with the same instance of Clock.

For more information about using Unity, you can review the official product documentation available at http://msdn.microsoft.com/en-us/library/dn170416.aspx.

SignalR with Ninject

Besides having a great name, Ninject is a powerful open source IoC container, created with simplicity and ease of use in mind from the beginning. Installation on a project, as usual, can be performed through NuGet:

PM> Install-package Ninject

As we did before with Unity, the next step is to create our Dependency Resolver to replace the one provided by SignalR by default:

public class NinjectDependencyResolver : DefaultDependencyResolver

{

private IKernel _kernel;

public NinjectDependencyResolver(IKernel kernel)

{

_kernel = kernel;

}

public override object GetService(Type serviceType)

{

return _kernel.TryGet(serviceType)

?? base.GetService(serviceType);

}

public override IEnumerable<object> GetServices(Type serviceType)

{

return _kernel.GetAll(serviceType)

.Concat(base.GetServices(serviceType));

}

}

As you can see, it is almost identical to the one we had with Unity, the main difference perhaps being that in Ninject dialect the container is called a “kernel.” The creation and initialization of the kernel is also done during application startup:

public void Configuration(IAppBuilder app)

{

var kernel = new StandardKernel();

kernel.Bind<IClock>().To<Clock>().InSingletonScope(); kernel.Bind<IMessageFormatter>().To<MessageFormatter>();

app.MapSignalR(new HubConfiguration()

{

Resolver = new NinjectDependencyResolver(kernel)

});

}

204 Chapter 9Advanced topics

www.it-ebooks.info

Соседние файлы в папке signalr