Skip to content

Instantly share code, notes, and snippets.

@lethek
Last active March 27, 2017 22:22
Show Gist options
  • Save lethek/5933672 to your computer and use it in GitHub Desktop.
Save lethek/5933672 to your computer and use it in GitHub Desktop.
Using Autofac to inject dependencies into SignalR hubs and manage dependency lifetimes: management of the lifetimes of dependencies will happen automatically as long as the Hub inherits from AirtimeHub and the dependencies are registered with Autofac with a InstancePerLifetimeScope() option.
using System;
using Microsoft.AspNet.SignalR;
namespace Airtime.Hubs
{
/// <summary>
/// Hubs that have injected dependencies which must be scoped to the same lifetime should
/// inherit this class to enable automatic lifetime scope management and prevent memory leaks.
/// </summary>
public abstract class AirtimeHub : Hub
{
internal event EventHandler Disposing;
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing) {
var handler = Disposing;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
}
}
}
using Autofac;
using Microsoft.AspNet.SignalR.Hubs;
namespace Airtime.Hubs.Framework
{
/// <summary>
/// SignalR HubActivator for creating hub instances with the correct lifetime-scope for automatic per-instance session & transaction management.
/// </summary>
internal class AirtimeHubActivator : IHubActivator
{
public AirtimeHubActivator(AirtimeHubLifetimeScopeManager hubLifetimeScopeManager, ILifetimeScope lifetimeScope)
{
LifetimeScope = lifetimeScope;
HubLifetimeScopeManager = hubLifetimeScopeManager;
}
public IHub Create(HubDescriptor descriptor)
{
return typeof(AirtimeHub).IsAssignableFrom(descriptor.HubType)
? HubLifetimeScopeManager.CreateHub<AirtimeHub>(descriptor.HubType, LifetimeScope)
: LifetimeScope.Resolve(descriptor.HubType) as IHub;
}
private ILifetimeScope LifetimeScope { get; set; }
private AirtimeHubLifetimeScopeManager HubLifetimeScopeManager { get; set; }
}
}
using System;
using System.Collections.Generic;
using Autofac;
using Microsoft.AspNet.SignalR.Hubs;
namespace Airtime.Hubs.Framework
{
internal class AirtimeHubLifetimeScopeManager
{
public T CreateHub<T>(Type type, ILifetimeScope lifetimeScope) where T : AirtimeHub
{
var scope = lifetimeScope.BeginLifetimeScope();
var hub = (T)scope.Resolve(type);
hub.Disposing += HubOnDisposing;
_hubLifetimeScopes.Add(hub, scope);
return hub;
}
private void HubOnDisposing(object sender, EventArgs eventArgs)
{
ILifetimeScope scope;
var hub = sender as IHub;
if (hub != null && _hubLifetimeScopes.TryGetValue(hub, out scope)) {
_hubLifetimeScopes.Remove(hub);
if (scope != null) {
scope.Dispose();
}
}
}
private readonly Dictionary<IHub, ILifetimeScope> _hubLifetimeScopes = new Dictionary<IHub, ILifetimeScope>();
}
}
using System;
using System.Linq;
using Airtime.Core;
using Airtime.Core.Framework.Security;
using Airtime.Core.Framework.SignalR;
using Airtime.Data;
using Airtime.Data.Client;
using Airtime.Data.Account;
using NLog;
namespace Airtime.Hubs
{
//membershipRepository is injected by Autofac and as long as this hub subclasses AirtimeHub,
//membershipRepository can be bound to the same lifetime as the hub and destroyed at the same time
//(if the dependency has been registeredd with Autofac with the InstancePerLifetimeScope() option)
public class ExampleHub : AirtimeHub
{
public ExampleHub(IMembershipRepository membershipRepository)
{
MembershipRepository = membershipRepository;
}
public virtual void Echo(string message)
{
var principal = Context.User as AirtimePrincipal;
if (principal != null && principal.Identity.MembershipId.HasValue) {
var user = MembershipRepository[principal.Identity.MembershipId.Value];
Clients.All.AddMessage("From " + user.Username + ": " + message);
} else {
Clients.All.AddMessage(message);
}
}
public IMembershipRepository MembershipRepository { get; set; }
}
}
using System;
using Airtime.Hubs.Framework;
using Autofac;
using Microsoft.AspNet.SignalR.Hubs;
namespace Airtime.Hubs
{
public class HubsModule : Module
{
protected override void Load(ContainerBuilder builder)
{
if (builder == null) {
throw new ArgumentNullException("builder");
}
builder.RegisterType<AirtimeHubLifetimeScopeManager>().SingleInstance();
builder.RegisterType<AirtimeHubActivator>().As<IHubActivator>().SingleInstance();
//Register SignalR hubs (must be marked ExternallyOwned so that instances will be destroyed by SignalR rather than Autofac)
builder.RegisterAssemblyTypes(typeof(Airtime.Hubs.AssemblyHook).Assembly)
.Where(t => !t.IsAbstract && t.IsClass && t.IsAssignableTo<IHub>())
.InstancePerDependency()
.ExternallyOwned();
//Register an example dependency that will be injected into the ExampleHub
builder.RegisterType<Airtime.Data.Hibernate.MembershipRepository>
.As<IMembershipRepository>()
.InstancePerLifetimeScope();
}
}
}
@lethek
Copy link
Author

lethek commented Sep 5, 2015

@sstorie I've now implemented support for the generic SignalR Hub class. It's in the latest v1.1.0 (https://github.com/lethek/SignalR.Extras.Autofac/). Just sub-class LifetimeHub instead of LifetimeHub.

@lethek
Copy link
Author

lethek commented Sep 5, 2015

@Sickboy Thanks for that. I've actually just started using MEF for the first time in a project earlier this week. I've got a few things to learn there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment