SignalR issues with SignalR.Ninject and overiding the IConnectionIdFactory - chat

I have all the Nuget Bits for SignalR , I am trying to use my own clientIDs as well as the dependency Injection container that comes with SignalR for all my other repositories and such. Now the strange thing is this jQuery to connect to the hub fails on:
debugger;
// Proxy created on the fly
var chat = $.connection.chat;
Basically, the chat object becomes undefined as if SignalR cannot be resolved. This started happening once I tried to overide the default resolver for SignalR with the code below.
What am I missing here?
Another issue I am having is I am not sure if my UserClientIDfactory which implements IConnectionIdFactory
is working either.
Here is the MVC3 code in my Global.asax
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
RegisterServices(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<UserIdClientIdFactory>()
.To<UserIdClientIdFactory>()
.InRequestScope();
//Rest of the other stuff to inject
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//ninject calls to create the kernal etc
IKernel kernel = CreateKernel();
//TO DO using signal IR resolver
var resolver = new NinjectDependencyResolver(kernel);
SignalR.Hosting.AspNet.AspNetHost.SetResolver(resolver);
}
Finally, here is the code for my custom clientIDfactory
public class UserIdClientIdFactory : IConnectionIdFactory
{
#region IConnectionIdFactory Members
string IConnectionIdFactory.CreateConnectionId(SignalR.Hosting.IRequest request)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
return HttpContext.Current.User.Identity.Name != null ?
//TO DO change to get profileID from Appfabric or the database and log user infor
HttpContext.Current.User.Identity.Name.ToString() :
Guid.NewGuid().ToString();
}
#endregion
}

As I read your question you ask how to do proper dependency injection in ASP.NET MVC and SignalR using the same DI container (and hence only need to declare bindings in one place). If this is correct understood, I once wrote a blog post regarding this: http://lcdev.dk/2012/02/14/using-signalr-ninject-with-asp-net-mvc3-and-the-ninject-mvc3-nuget-package/
In the blog post I assume that you are using ASP.NET MVC3 as well as the Ninject.MVC3 and the SignalR.Ninject Nuget packages.
However, if this is not the case I do have a comment to your code. To me it seems like that the kernel used to make your bindings (in RegisterServices) is not the kernel you actually register with SignalR. And if this is the case, then of course SignalR won't know about your intended bindings and might throw an exception as result of your use of an un-instantiated object reference -> which then might explain why you no longer can connect to your SignalR hub.

ok thanks for the your post man, made me do some more digging , I read the rest of the post you linked about how to use Ninject with MVC3 which lead me to realize that I had ninject but not the Nuget Bits for Ninject Mvc3 , I added that and alos modifed my global.asax using the following post
http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/
here is the working code in gloabal.asax I also removed the bootstrapper that NinJect mvc3 added to the application start folder since that is how it works in the above post
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.IgnoreRoute("{*allaxd}", new { allaxd = #".*\.axd(/.*)?" }); //added for mango chat
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
public override void Init()
{
this.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
#region "Ninject stuff for dependancy Injection
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
// kernel.Load(Assembly.GetExecutingAssembly());
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<UserIdClientIdFactory>()
.To<UserIdClientIdFactory>()
.InRequestScope();
SignalR.Hosting.AspNet.AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new UserIdClientIdFactory());
}
#endregion
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
//for project awesome
ModelMetadataProviders.Current = new AwesomeModelMetadataProvider();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}

Related

autofac webapi owin integration problems with middleware exection order not correct

I'm confused with Autofac Examples : WebApiExample.OwinSelfHost, the startup class is following:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// In OWIN you create your own HttpConfiguration rather than
// re-using the GlobalConfiguration.
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });
var builder = new ContainerBuilder();
// Register Web API controller in executing assembly.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL - Register the filter provider if you have custom filters that need DI.
// Also hook the filters up to controllers.
builder.RegisterWebApiFilterProvider(config);
builder.RegisterType<CustomActionFilter>()
.AsWebApiActionFilterFor<TestController>()
.InstancePerRequest();
// Register a logger service to be used by the controller and middleware.
builder.Register(c => new Logger()).As<ILogger>().InstancePerRequest();
// Autofac will add middleware to IAppBuilder in the order registered.
// The middleware will execute in the order added to IAppBuilder.
builder.RegisterType<FirstMiddleware>().InstancePerRequest();
builder.RegisterType<SecondMiddleware>().InstancePerRequest();
// Create and assign a dependency resolver for Web API to use.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// The Autofac middleware should be the first middleware added to the IAppBuilder.
// If you "UseAutofacMiddleware" then all of the middleware in the container
// will be injected into the pipeline right after the Autofac lifetime scope
// is created/injected.
//
// Alternatively, you can control when container-based
// middleware is used by using "UseAutofacLifetimeScopeInjector" along with
// "UseMiddlewareFromContainer". As long as the lifetime scope injector
// comes first, everything is good.
app.UseAutofacMiddleware(container);
// Again, the alternative to "UseAutofacMiddleware" is something like this:
// app.UseAutofacLifetimeScopeInjector(container);
// app.UseMiddlewareFromContainer<FirstMiddleware>();
// app.UseMiddlewareFromContainer<SecondMiddleware>();
// Make sure the Autofac lifetime scope is passed to Web API.
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
The FirstMiddleware and SecondMiddleware code was as following:
public class FirstMiddleware : OwinMiddleware
{
private readonly ILogger _logger;
public FirstMiddleware(OwinMiddleware next, ILogger logger) : base(next)
{
this._logger = logger;
}
public override async Task Invoke(IOwinContext context)
{
this._logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name);
await Next.Invoke(context);
}
}
public class SecondMiddleware : OwinMiddleware
{
private readonly ILogger _logger;
public SecondMiddleware(OwinMiddleware next, ILogger logger) : base(next)
{
this._logger = logger;
}
public override async Task Invoke(IOwinContext context)
{
this._logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name);
await Next.Invoke(context);
}
}
According to the comments, the middleware registration order matters. FirstMiddleware first, then SecondMiddleware. but the output was second middleware was invoked first.
the program logs output here
What's wrong with the order?
This is the autofac official example.WebApiExample.OwinSelfHost
Looks like you've found a bug! I've filed an issue about it on your behalf. You can read more technical details about it there, but the short version is that over the years we've changed some Autofac internals to support .NET Core and this looks like something we've missed.
The workaround until this is fixed will be to register the middleware in reverse order, which isn't awesome because once the fix is applied you'll have to reverse them back. :(

Using Autofac Interface Interception with IAsyncInterceptor

I did use this documentation:
https://autofaccn.readthedocs.io/en/latest/advanced/interceptors.html
to implement Interface Interceptors. To handle my async calls I used the IAsyncInterceptor interface described here:
https://github.com/JSkimming/Castle.Core.AsyncInterceptor
The registration code I came up with does look like this:
builder.Register(c => new CallResultLoggerInterceptor())
.Named<IAsyncInterceptor>("log-calls");
builder.RegisterType<AppointmentService>()
.As<IAppointmentService>()
.EnableInterfaceInterceptors()
.InstancePerDependency();
where the AppointmentService has an InterceptAttribute.
[Intercept("log-calls")]
public class AppointmentService : IAppointmentService
...
When i call the containers Build() method, it throws an ComponentNotRegisteredException with the message:
The requested service 'log-calls (Castle.DynamicProxy.IInterceptor)' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
which is correct because I do not implement IInterceptor but IAsyncInterceptor. I guess the problem is the concrete implementation of EnableInterfaceInterceptors in autofac using the "wrong" extension method of the ProxyGenerator - but how can I solve this?
Cheers,
Manuel
You need to register a named IInterceptor for Autofac interceptors to work. You're registering an IAsyncInterceptor. That won't work.
Note Autofac has no support for this extended async interceptor extension you're using. If you want to get that to work, it'll require writing a custom adapter of some nature to get it to respond to IInterceptor.
You can see my answer in the issue of Castle.Core.AsyncInterceptor:
https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/42#issuecomment-592074447
create an adapter
public class AsyncInterceptorAdaper<TAsyncInterceptor> : AsyncDeterminationInterceptor
where TAsyncInterceptor : IAsyncInterceptor
{
public AsyncInterceptorAdaper(TAsyncInterceptor asyncInterceptor)
: base(asyncInterceptor)
{ }
}
create your async interceptor
public class CallLoggerAsyncInterceptor : AsyncInterceptorBase
{
....
}
relate the interceptor to interface
[Intercept(typeof(AsyncInterceptorAdaper<CallLoggerAsyncInterceptor>))]
public interface ISomeType
register to IoC container
//register adapter
builder.RegisterGeneric(typeof(AsyncInterceptorAdaper<>));
//register async interceptor
builder.Register(c => new CallLoggerAsyncInterceptor(Console.Out));
I've made a code sample in https://github.com/wswind/aop-learn/blob/master/AutofacAsyncInterceptor
I've created my own extension method for registering application services.
This extension method simply prepares input parameter for castle core ProxyGenerator.
using System;
using System.Collections.Generic;
using System.Linq;
using Castle.DynamicProxy;
using Autofac;
namespace pixi.Extensions
{
public static class AutofacExtensions
{
private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator();
/// <summary>
/// Use this extension method to register default interceptors <code>UnitOfWorkInterceptor</code>
/// and <code>LoggingInterceptor</code> on your application service implementations. If you need custom
/// interceptors that are not part of infrastructure but are part of specific business module then pass
/// in those interceptors in params explicitly.
/// </summary>
/// <param name="builder"></param>
/// <param name="interceptors"></param>
/// <typeparam name="TImplementation"></typeparam>
/// <typeparam name="TService"></typeparam>
/// <exception cref="ArgumentException"></exception>
public static void RegisterApplicationService<TImplementation, TService>(this ContainerBuilder builder, params Type[] interceptors)
where TImplementation : class
{
ValidateInput<TService>(interceptors);
builder.RegisterType<TImplementation>().AsSelf();
builder.Register(c =>
{
var service = c.Resolve<TImplementation>();
var resolvedInterceptors = ResolveInterceptors<TImplementation, TService>(interceptors, c);
return (TService) _proxyGenerator.CreateInterfaceProxyWithTarget(
typeof(TService),
service,
ProxyGenerationOptions.Default,
resolvedInterceptors
);
}).As<TService>();
}
private static void ValidateInput<TService>(Type[] interceptors)
{
if (!typeof(TService).IsInterface)
throw new ArgumentException("Type must be interface");
if (interceptors.Any(i => i != typeof(IAsyncInterceptor)))
throw new ArgumentException("Only IAsyncInterceptor types are expected");
}
private static IAsyncInterceptor[] ResolveInterceptors<TImplementation, TService>(Type[] interceptors,
IComponentContext c) where TImplementation : class
{
var resolvedInterceptors = new List<IAsyncInterceptor>
{
c.Resolve<LoggingInterceptor>(),
c.Resolve<UnitOfWorkInterceptor>()
}.Concat(interceptors
.Where(i => i != typeof(UnitOfWorkInterceptor)
&& i != typeof(LoggingInterceptor))
.Select(i => (IAsyncInterceptor) c.Resolve(i))).ToArray();
return resolvedInterceptors;
}
}
}
I am using castle core for unit of work and logging hence the name UnitOfWorkInterceptor and LogginInterceptor. Change these two to your desired defaults. Default interceptors must be registered in this way:
public class SomeModule: Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UnitOfWorkInterceptor>().AsSelf();
builder.RegisterType<LoggingInterceptor>().AsSelf();
builder.RegisterApplicationService<SampleService, ISampleService>();
builder.RegisterType<SampleRepository>().As<ISampleRepository>();
}
}
In the above code snippet I've also demonstrated the usage provided extension method. Doing it this way I get red of tag interfaces and placing extra attributes on interfaces. That way I can keep my ApplicationService interfaces free of framework/3rd party library dependencies.
I hope this helps.

EntityFramework Core automatic migrations

Is there any code to perform automatic migration in Entity Framework core code first in asp.net core project?
I do it simply in MVC4/5 by adding
Database.SetInitializer(new MigrateDatabaseToLatestVersion<AppDbContext, MyProject.Migrations.Configuration>());
public Configuration() {
AutomaticMigrationsEnabled = true;
}
This saves time when entities changed
You can call context.Database.Migrate()in your Startup.cs
eg:
using (var context = new MyContext(...))
{
context.Database.Migrate();
}
EF core doesn't support automatic migrations.So you have to do it manually.
From the perspective of automatic migrations as a feature, we are not
planning to implement it in EF Core as experience has showed code-base
migrations to be a more manageable approach.
You can read full story here : Not to implement Automatic Migrations
This is the way they do it in IdentityServer4 http://identityserver.io
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// this will do the initial DB population
InitializeDatabase(app);
}
private void InitializeDatabase(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
...
}
}
Automatic migrations is not supported in EF Core. Migration it is necessary to create hands. To automatically apply all existing handmade migrations need to add the following code in the class Program:
public class Program
{
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<MyDbContext>();
context.Database.Migrate(); // apply all migrations
SeedData.Initialize(services); // Insert default data
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Following Microsoft's documentation
https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro
If you are using dependency injection, first, you need to setup a static class Data/DbInitializer.cs and add the following code:
public static class DbInitializer
{
public static void Initialize(ApplicationDbContext context)
{
context.Database.Migrate();
// Add Seed Data...
}
}
Notice, this is also where you can add seed data.
Next, in your Program.cs file, add the following code
public static void Main(string[] args)
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var environment = services.GetRequiredService<IHostingEnvironment>();
if (!environment.IsDevelopment())
{
var context = services.GetRequiredService<ApplicationDbContext>();
DbInitializer.Initialize(context);
}
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
host.Run();
}
In my case, I'm checking the environment to make sure I'm in development so I can control the migrations/updates. However, in production, I want them to be automatic for continuous integration. As others have mentioned, this is probably not best practices but on small projects it works great.
My working automigration code Asp Net Core 2.0.7.
// startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// configure app
SeedData.Initialize(app.ApplicationServices);
}
// dbInitializer.cs
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var serviceScope = serviceProvider.CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
// auto migration
context.Database.Migrate();
// Seed the database.
InitializeUserAndRoles(context);
}
}
private static void InitializeUserAndRoles(ApplicationDbContext context)
{
// init user and roles
}
}
You can call Database.Migrate() in db context constructor.
If the model changes a lot and you manage a medium - large team, migrations leads more problems than solution at least in development phase.
I published a nuget package with automatic migration for .net core, EFCore.AutomaticMigrations - https://www.nuget.org/packages/EFCore.AutomaticMigrations/, so manual migration not needed anymore.
You can call directly in Program class, like bellow:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<Program>();
try
{
var environment = services.GetRequiredService<IWebHostEnvironment>();
if (environment.IsDevelopment())
{
var context = services.GetRequiredService<ApplicationContext>();
MigrateDatabaseToLatestVersion.ExecuteAsync(context).Wait();
}
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred creating/updating the DB.");
}
}
host.Run();
}
Frank Odoom's answer works even 4 years later in .net 5, but it is not the intended context to call the migration at runtime... And, it appears it never was because it requires us to mock the DbContext with DbContextOptions whos documentation explicitly states:
"The options to be used by a DbContext. You normally override OnConfiguring(DbContextOptionsBuilder) or use a DbContextOptionsBuilder to create instances of this class and it is not designed to be directly constructed in your application code."
Here is my suggestion:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// database provider is configured before runtime migration update is applied e.g:
optionsBuilder.UseSqlServer(ConnectionString);
Database.Migrate();
}
Edit:
My suggestion is actually horrible if you are using multiple DBContexts in the same project... It would migrate the database multiple times. Which would most likely not break anything, but it would slow startup considerably.
my best advice is not to use the automatic migration.It is always better to add migrations manually and also avoid bulk migration and stick to best practice for using manual migration
automatic migration is not a magic tool and there will be several occasions where you might want to add some addition changes to the migration. You only accomplish by using manual migration.
To enable migration, type "enable-migrations" in the package manager console
This way you will have full control of upgrading or downgrading your database and also easy to track migrations.
Just three simple steps in package manager console.
1) add-migrations [some name for your migration]
2) migrations is generated for the changes, you review them and also can
make changes to it
3) update-database your migration is complete now.
handling migration is less painful!

log4net: How to distinguish between different forms on the same UI thread?

is there a way (NDC, Properties, ...?) to have a name/id per form that is included in all log4net messages, so I can distinguish between the forms in all log messages?
I have many service methods etc. that are used in all my forms, and I'd like to see e.g. that a service was called as a result of user input in what form (think multiple nonmodal similar forms (same class), running in the same UI thread, containing a button, and in the button's Click-Event, a service method is called. Inside the service method, there are logging calls. In the log messages, I'd like to have a property containing the information of in exactly which form instance the button was clicked in).
I don't want to modify ALL logging calls. The examples in the web for log contexts / NDC all only talk about multiple clients / asp.net requests / etc., not multiple forms in 1 thread.
Thanks,
Tim
To do this, set the properties in the form's Activated event to what you want to log:
private void Form1_Activated(object sender, System.EventArgs e)
{
// for example
log4net.GlobalContext.Properties["Name"] = this.GetType().Name;
log4net.GlobalContext.Properties["Id"] = this.Id;
}
The in your logging configuration, you can reference the properties in the PatternLayout for each appender:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Name} : %property{Id} : [%level]- %message%newline" />
</layout>
Edit: to preserve multiple values, use a stack, as in this unit test which outputs:
Now in TestClass1 Now in TestClass2
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using NUnit.Framework;
namespace log4net.Tests
{
[TestFixture] // A NUnit test
public class log4net_Stacks
{
[SetUp]
public void Setup()
{
ConsoleAppender ca = new ConsoleAppender
{
Layout = new PatternLayout("%property{demo}"),
Threshold = Level.All
};
ca.ActivateOptions();
BasicConfigurator.Configure(ca);
}
[Test]
public void Stacks_Demo()
{
new TestClass1().Method1();
LogManager.GetLogger("logger").Debug("");
ThreadContext.Stacks["demo"].Clear();
}
private abstract class BaseTestClass
{
protected static void AddToStack(string message)
{
ThreadContext.Stacks["demo"].Push(message);
}
}
private class TestClass1 : BaseTestClass
{
public void Method1()
{
AddToStack("Now in " + GetType().Name);
var tc2 = new TestClass2();
tc2.Method2();
}
}
private class TestClass2 : BaseTestClass
{
public void Method2()
{
AddToStack("Now in " + GetType().Name);
}
}
}
}

Proper Hub dependency lifetime management for SignalR and Castle Windsor

I have some SignalR hubs which may need to access some transient and singleton dependencies. Hooking the creation of the Hub is easy and works just fine however SignalR does its own Dispose() call on the created Hub rather than notifying the dependency resolver and letting it get involved in the disposal.
This isn't such a big deal if the dependencies are registered singletons, but if they're registered as transients then they'll never get disposed (if that was required) and Windsor will keep them alive until the Windsor container is collected (when the web server is shutting down anyway).
I see several possible ways of handling this...
a) Someone here points out a way to subclass SignalR's HubDispatcher class so that it can do proper disposal. It's not part of SignalR's standard DependencyResolver so this might be difficult / impossible
b) Some other class in SignalR, elsewhere in the pipeline, can be overridden or easily replaced so that we could subclass HubDispatcher and ensure that subclass is used. From what I can tell this would have to be the Owin middleware class HubDispatcherMiddleware. Is there some way to force Owin to not register this class and instead register my own version of this (which in turn uses my own HubDispatcher)?
c) There's some way of intercepting the Dispose() call made by SignalR on my Hub classes so that a call could be made back to Windsor to ensure any dependencies are properly disposed and released from the container
d) Studiously avoid using transient lifestyle dependencies and instead pass in typed factories so that we can resolve and release each dependency via the typed factory within the Hub
At the moment (d) is the only one I know how to do. (a) or (b) would be great. (c) is mostly covered by this post http://kozmic.net/2010/01/27/transparently-releasing-components-in-windsor/, however, the interceptor requires that Dispose() be called via IDisposable. SignalR's HubDispather class' implementation of hub disposal is
private static void DisposeHubs(IEnumerable<IHub> hubs)
{
foreach (var hub in hubs)
{
hub.Dispose();
}
}
No casting to IDisposable there... Also Dispose() on the Hub class is virtual and that blog post implies that a virtual Dispose() could add some complexity (I'm not quite sure how much and I don't know enough about Castle's interceptors and whether or not that missing cast to IDisposable can be worked around anyway).
I appreciate I've written this question for a fairly narrow audience - those who have used Windsor AND SignalR and care about more than just resolving dependencies. Every example I've found, including those on StackOverflow, seems to just ignore the release of dependencies.
Thanks!
I've had a bit similar problem but with Unity instead of Castle Windsor.
My requirements:
I wanted to avoid singleton registrations on the container.
All objects are resolved in Hub and should be disposed on Hub destruction.
Registrations reused across Web Api and SignalR.
Object lifetime is managed by HierarchicalLifetimeManager - child containers resolve and manage separate object instances. Registered like this:
container.RegisterType<IMessageService, MessageService>(new HierarchicalLifetimeManager());
This is my solution:
[HubName("exampleHub")]
public class ExampleHub : Hub
{
IUnityContainer _container;
public CarrierApiHub(IUnityContainer container) // container itself injected in hub
{
_container = container.CreateChildContainer(); // child container derived from the main container.
}
public async Task<int> UnreadMessagesCount()
{
// Here i'm resolving instance of IMessageService which depends on
// other registrations specified on the container. Full object graph
// is constructed and destroyed on hub disposal.
var messageSvc = _container.Resolve<IMessageService>();
return await messageSvc.CountUnreadOf(UserId);
}
protected override void Dispose(bool disposing)
{
_container.Dispose(); // child container destroyed. all resolved objects disposed.
base.Dispose(disposing);
}
private int UserId
{
get
{
// only an example
var claim = ((ClaimsPrincipal)Context.User).GetClaim("user_id");
return int.Parse(claim.Value);
}
}
}
SignalR and dependency resolver configuration:
public static class ConfigureSignalR
{
public static void Initialize(UnityContainer unityContainer, IAppBuilder app)
{
app.Map("/signalr", map =>
{
var resolver = new AppSignalRDependencyResolver(unityContainer);
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJavaScriptProxies = false,
EnableJSONP = true, // Required for IE 9 (supports only polling)
Resolver = resolver
};
map.RunSignalR(hubConfiguration);
});
}
}
Dependency resolver implementation:
public class AppSignalRDependencyResolver : DefaultDependencyResolver
{
protected IUnityContainer _container;
public AppSignalRDependencyResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this._container = container.CreateChildContainer();
}
public override object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return base.GetService(serviceType);
}
}
public override IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType).Concat(base.GetServices(serviceType));
}
catch (ResolutionFailedException)
{
return base.GetServices(serviceType);
}
}
protected override void Dispose(bool disposing)
{
_container.Dispose();
base.Dispose(disposing);
}
}