I'm having some difficulty using Ninject's InSingletonScope binding with Web Api RC. No matter how I create my binding, it looks like perhaps Web Api is handling scope/lifetime instead of Ninject.
I've tried a few variations on wiring up Ninject. The most common is identical to the answer here:
ASP.NET Web API binding with ninject
I've also tried this version:
http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/
In both, I'm literally creating an out of the box Web Api project, then adding the Ninject packages as described in either post. Finally, I'm adding the Resolver and Scope classes, such as this for the StackOverflow version:
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
this.resolver = resolver;
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
and:
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
Then, NinjectWebCommon looks like this:
using System.Web.Http;
using MvcApplication2.Controllers;
[assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplication2.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(MvcApplication2.App_Start.NinjectWebCommon), "Stop")]
namespace MvcApplication2.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Register Dependencies
RegisterServices(kernel);
// Set Web API Resolver
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(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<ILogger>().To<Logger>().InSingletonScope();
}
}
}
The ILogger and Logger objects don't do anything, but illustrate the issue. Logger does Debug.Writeline so that I can see when it was instantiated. And each refresh of the page shows that it's being refreshed per call, rather than the singleton I'd hoped for. Here is a controller using the Logger:
public class ValuesController : ApiController
{
private readonly ILogger _logger;
public ValuesController(ILogger logger)
{
_logger = logger;
_logger.Log("Logger created at " + System.DateTime.Now.ToLongTimeString());
}
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post(string value)
{
}
// PUT api/values/5
public void Put(int id, string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
When I put trace information into the creation of the kernel, it seems to show that the kernel is only created once. So... what am I not seeing? Why isn't the singleton persisted?
use
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel);
}
and don't dispose the kernel in the NinjectDependencyScope
#Remo Gloor
When I run your code in InMemoryHost of WebAPI and run Integration tests everything works fine and I do have singleton.
If I run WebAPI solution inside VS Cassini web server first run is successful
and when I click refresh I receive exception :
Error loading Ninject component ICache
No such component has been registered in the kernel's component container.
If I return old code with BeginBlock it works in Cassini but IsSingleton not working anymore in integration tests.
Instead of not disposing the kernel (which will not call the internal dispose) you can simply implement your own singleton:
public static class NinjectSingletonExtension
{
public static CustomSingletonKernelModel<T> SingletonBind<T>(this IKernel i_KernelInstance)
{
return new CustomSingletonKernelModel<T>(i_KernelInstance);
}
}
public class CustomSingletonKernelModel<T>
{
private const string k_ConstantInjectionName = "Implementation";
private readonly IKernel _kernel;
private T _concreteInstance;
public CustomSingletonKernelModel(IKernel i_KernelInstance)
{
this._kernel = i_KernelInstance;
}
public IBindingInNamedWithOrOnSyntax<T> To<TImplement>(TImplement i_Constant = null) where TImplement : class, T
{
_kernel.Bind<T>().To<TImplement>().Named(k_ConstantInjectionName);
var toReturn =
_kernel.Bind<T>().ToMethod(x =>
{
if (i_Constant != null)
{
return i_Constant;
}
if (_concreteInstance == null)
{
_concreteInstance = _kernel.Get<T>(k_ConstantInjectionName);
}
return _concreteInstance;
}).When(x => true);
return toReturn;
}
}
And then simply use:
i_Kernel.SingletonBind<T>().To<TImplement>();
Rather then
i_Kernel.Bind<T>().To<TImplement>().InSingletonScope();
note: although it is only matters for the first request, this implementation is not thread safe.
Related
I have an MVC4 site that uses both MVC and WebAPI in it. All was going well till I tried to change my classes to have a cross cutting AOP class that would help with caching data. I am now finding that when I call a method that does not have the InterceptAttribute on it, it will crash because Ninject didn't inject with a parameter, and it fails.
My BLL class looks like this:
public class FooBLL
{
#region Private Variables
private readonly IDAL _context;
#endregion
#region Constructor
public Foo(IDAL context)
{
_context = context;
}
#endregion
#region Public Methods
public List<Bar> GetAllBars()
{
return _context.GetAllBars();
}
public List<Bar> GetTwoBars()
{
return _context.GetTwoBars();
}
#endregion
}
My WebApi Controller looks like this:
public class FooController : ApiController
{
#region Private Variables
private readonly FooBLL _fooBll;
#endregion
#region Constructor
public FooController(FooBLL fooBll)
{
_fooBll = fooBll;
}
#endregion
#region Public Methods
#region Estimate Types
#region Get
public List<Bar> GetAllBars()
{
return _fooBll.GetAllBars();
}
public List<Bar> GetTwoBars()
{
return _fooBll.GetTwoBars();
}
#endregion
#endregion
#endregion
}
In my Website, I created the following Ninject classes for resolving the controllers:
public class NinjectRegistrations : NinjectModule
{
public override void Load()
{
Kernel.Bind<IDAL>().To<DAL>().InSingletonScope();
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver, System.Web.Mvc.IDependencyResolver
{
private readonly IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(this.kernel.BeginBlock());
}
}
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
this.resolver = resolver;
}
public void Dispose()
{
var disposable = this.resolver as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
this.resolver = null;
}
public object GetService(Type serviceType)
{
if (this.resolver == null)
{
throw new ObjectDisposedException("this", "This scope has already been disposed");
}
return this.resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (this.resolver == null)
{
throw new ObjectDisposedException("this", "This scope has already been disposed");
}
return this.resolver.GetAll(serviceType);
}
}
In Global.asax I then register this resolver:
NinjectHelper.Kernel = new StandardKernel(modules);
var ninjectResolver = new NinjectDependencyResolver(NinjectHelper.Kernel);
DependencyResolver.SetResolver(ninjectResolver); // MVC
GlobalConfiguration.Configuration.DependencyResolver = ninjectResolver; // Web API
//Register Filter Injector
GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Filters.IFilterProvider), new NinjectWebApiFilterProvider(NinjectHelper.Kernel));
Things were fine till I added the attribute Cache using Ninject.Extensions.Interception.Attributes.InterceptAttribute.
The class now looks like this (note that I added a parameterless constructor and marked one of the methods as virtual, these are both required for the Interception to work):
public class FooBLL
{
#region Private Variables
private readonly IDAL _context;
#endregion
#region Constructor
public Foo(IDAL context)
{
_context = context;
}
public Foo()
{
}
#endregion
#region Public Methods
public List<Bar> GetAllBars()
{
return _context.GetAllBars();
}
[Cache(DefaultTimeoutMinutes = 20)]
public virtual List<Bar> GetTwoBars()
{
return _context.GetTwoBars();
}
#endregion
}
Now on the WebAPI controller, when I call GetToBars(the method with the Intercept Attribute), everything still works fine.
However, when I call GetAllBars(the method that doesn't have the Intercept Attribute), I fail with an exception that _context is null.
Any help would be greatly appreciated.
Ben
Here is my Error Message which is shown when I browse: ../api/User
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'RavenReader.Web.Controllers.UserController' does not have a default constructor
</ExceptionMessage>
<ExceptionType>System.ArgumentException</ExceptionType>
<StackTrace>
at System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
</Error>
My Controller Classes are
public class BaseController : ApiController
{
private readonly ICookieStorageService _cookieStorageService;
public BaseController(ICookieStorageService cookieStorageService)
{
_cookieStorageService = cookieStorageService;
}
}
public class UserController : BaseController
{
private readonly RavenUserFacade _facade;
private readonly ICookieStorageService _cookieStorageService;
public UserController(ICookieStorageService cookieStorageService, RavenUserFacade facade):base(cookieStorageService)
{
_facade = facade;
}
// GET api/User
public IEnumerable<RavenUserView> Get()
{
var users = _facade.GetAllUser();
return users.RavenUsers;
}
..........................................
..........................................
}
According to http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/ this blog I organized my NinjectDependencyScope class, NinjectDependencyResolver class and NinjectWebCommon as follows:
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
this.resolver = resolver;
}
public void Dispose()
{
var disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
public static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(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<IDbFactory>().To<IDbFactory>().InSingletonScope();
kernel.Bind<IUnitOfWork>().To<EFUnitOfWork>();
kernel.Bind<IRavenUserRepository>().To<RavenUserRepository>();
kernel.Bind<IRavenUserFacade>().To<RavenUserFacade>();
kernel.Bind<ICookieStorageService>().To<CookieStorageService>();
kernel.Bind<ICacheStorage>().To<HttpContextCacheAdapter>();
}
}
I am using visual Studio 2013 Ninject For MVC-3
I am posting this answer in order to help you track down the root issue you are facing. It is not a solution but this is how I reproduced the exact same error you are having.
First of all, you should use a different implementation for the NinjectDependencyResolver. I've set this gist. The reason behind that is avoiding problem with scopes (specially singletons), which you can learn more about here.
Back to your problem, the error is in your bindings, somewhere. First, try removing the InSingletonScope from your IDbFactory. Then, try removing one dependency from your controller constructor and check if that works. Finally, strip out the base class and see if it works.
These steps are just to guide you track down the problem. I've replicated your scenario here and I couldn't face the same issue. My setup was like this:
public class BaseController : ApiController
{
protected readonly IService _service;
public BaseController(IService service)
{
_service = service;
}
}
public class ValuesController : BaseController
{
public ISomeOtherDependency Dependency { get; set; }
public ValuesController(IService service, ISomeOtherDependency dependency) : base(service)
{
Dependency = dependency;
}
// GET api/values/5
public string Get(int id)
{
return _service.CreatedAt.ToString("u");
}
}
public interface ISomeOtherDependency
{
}
public class ConcreteDependency : ISomeOtherDependency
{
}
public interface IService
{
DateTime CreatedAt { get; }
}
public class Service : IService
{
public Service()
{
CreatedAt = DateTime.Now;
}
public DateTime CreatedAt { get; private set; }
}
And my bindings:
kernel.Bind<IService>().To<Service>(); // If I comment this, I get the same exception.
kernel.Bind<ISomeOtherDependency>().To<ConcreteDependency>();
But by commenting out one of these bindings, I get the exact same error as you:
ValuesController' does not have a default constructor
I hope this gets you on the right track to fix your issue.
Thanks for your reply. I followed each your steps but same results here. Finally I debugged by bindings that belongs inside NinjectWebCommon.cs and I found a problem here. While debugging it shows me following problem:
Locating source for
Bootstrapper.cs not found
You need to find Bootstrapper.cs to view the source for the current call stack frame
'c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\Bootstrapper.cs'. Checksum: MD5 {13 3e b1 f3 ba a7 65 14 f6 dc 4d f1 dd aa 21 bf}
The file 'c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\Bootstrapper.cs' does not exist.
Looking in script documents for 'c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\Bootstrapper.cs'...
Looking in the projects for 'c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\Bootstrapper.cs'.
The file was not found in a project.
Looking in directory 'C:\Program Files\Microsoft Visual Studio 11.0\VC\crt\src\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 11.0\VC\crt\src\vccorlib\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 11.0\VC\atlmfc\src\atl\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 11.0\VC\atlmfc\include'...
The debug source files settings for the active solution indicate that the debugger will not ask the user to find the file: c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\Bootstrapper.cs.
The debugger could not locate the source file 'c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\Bootstrapper.cs'.
I am using Umbraco 6.1 with an UmbracoApiController which has a IUnitOfWork injected into it's constructor. To inject the dependencies, I am using Unity, like I have in the past with standard Web API projects. Normally, I set unity up in the Global.asax.cs. As Umbraco does not have this I have created my own UmbracoEvents handler, which inherits from IApplicationEventHandler, and has the methods:
OnApplicationInitialized
OnApplicationStarting
OnApplicationStarted
ConfigureApi
In the OnApplicationStarted method I set up my EF database, db initializer etc and call ConfigureApi to set up Unity. My OnApplication Started and ConfigureApi methods looks like this:
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
_umbracoApplication = umbracoApplication;
_contentService = ApplicationContext.Current.Services.ContentService;
this.ConfigureApi(GlobalConfiguration.Configuration);
Database.SetInitializer(null);
PropertySearchContext db = new PropertySearchContext();
db.Database.Initialize(true);
}
private void ConfigureApi(HttpConfiguration config)
{
var unity = new UnityContainer();
unity.RegisterType<PropertiesApiController>();
unity.RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager());
config.DependencyResolver = new IoCContainer(unity);
}
My Controller code:
public class PropertiesApiController : UmbracoApiController
{
private readonly IUnitOfWork _unitOfWork;
public PropertiesApiController(IUnitOfWork unitOfWork)
{
if(null == unitOfWork)
throw new ArgumentNullException();
_unitOfWork = unitOfWork;
}
public IEnumerable GetAllProperties()
{
return new[] {"Table", "Chair", "Desk", "Computer", "Beer fridge"};
}
}
My Scope Container/IoC Container code: (as per http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver)
public class ScopeContainer : IDependencyScope
{
protected IUnityContainer container;
public ScopeContainer(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
if (container.IsRegistered(serviceType))
{
return container.Resolve(serviceType);
}
else
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (container.IsRegistered(serviceType))
{
return container.ResolveAll(serviceType);
}
else
{
return new List<object>();
}
}
public void Dispose()
{
container.Dispose();
}
}
public class IoCContainer : ScopeContainer, IDependencyResolver
{
public IoCContainer(IUnityContainer container)
: base(container)
{
}
public IDependencyScope BeginScope()
{
var child = this.container.CreateChildContainer();
return new ScopeContainer(child);
}
}
My IUnitOfWork code:
public interface IUnitOfWork : IDisposable
{
GenericRepository<Office> OfficeRepository { get; }
GenericRepository<Property> PropertyRepository { get; }
void Save();
void Dispose(bool disposing);
void Dispose();
}
My UnitOfWork implementation:
public class UnitOfWork : IUnitOfWork
{
private readonly PropertySearchContext _context = new PropertySearchContext();
private GenericRepository<Office> _officeRepository;
private GenericRepository<Property> _propertyRepository;
public GenericRepository<Office> OfficeRepository
{
get
{
if (this._officeRepository == null)
{
this._officeRepository = new GenericRepository<Office>(_context);
}
return _officeRepository;
}
}
public GenericRepository<Property> PropertyRepository
{
get
{
if (this._propertyRepository == null)
{
this._propertyRepository = new GenericRepository<Property>(_context);
}
return _propertyRepository;
}
}
public void Save()
{
_context.SaveChanges();
}
private bool disposed = false;
public virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
I have used unity/DI with MVC4/WebAPI controllers and this implementation of UnitOfWork many times before without issue, so I'm thinking it's Umbraco specific.
I have also debugged the application and made sure that it hits OnApplicationStarted and that its parameters are not null.
The GetAllProperties method in the controller is just a test method to make sure it is all working fine, however, when I try and access this action I get the error:
"The type IUnitOfWork does not have an accessible constructor"
Does anyone have experience with using Umbraco 6.1 and it's UmbracoApiController with dependency injection/Unity?
Also, on an unrelated note, is there a way to return JSON instead of XML in the action? In Web API you would just define the formatter in the WebApi.config but there is none in Umbraco.
Thanks,
Justin
In case you haven't found a solution to your problem? Download this nuget package and right after building your unity container:
GlobalConfiguration.Configuration.DependencyResolver =
new Unity.WebApi.UnityDependencyResolver(Bootstrapper.Container);
Notice the namespace which is different than Unity.Mvc4.UnityDependencyResolver.
I am using SignalR in my MVC3 application, and since I have implemented StructureMap Dependency Injection on my controllers I would like to do the same in my hub, but I can't seem to get it working.
Please tell me what's wrong with my codes below:
SignalRSmDependencyResolver.cs
public class SignalRSmDependencyResolver : DefaultDependencyResolver
{
private IContainer _container;
public SignalRSmDependencyResolver(IContainer container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
object service = null;
if (!serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
{
// Concrete type resolution
service = _container.GetInstance(serviceType);
}
else
{
// Other type resolution with base fallback
service = _container.TryGetInstance(serviceType) ?? base.GetService(serviceType);
}
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = _container.GetAllInstances(serviceType).Cast<object>();
objects.Concat(base.GetServices(serviceType));
return objects;
}
}
SignalRExtensionsRegistry.cs
public class SignalRExtensionsRegistry : Registry
{
public SignalRExtensionsRegistry()
{
For<IDependencyResolver>().Add<SignalRSmDependencyResolver>();
}
}
IoC.cs
public static class IoC {
public static IContainer Initialize() {
var container = BootStrapper.Initialize();
container.Configure(x =>
{
x.For<IControllerActivator>().Singleton().Use<StructureMapControllerActivator>();
});
return container;
}
}
public class StructureMapControllerActivator : IControllerActivator {
public StructureMapControllerActivator(IContainer container) {
_container = container;
}
private IContainer _container;
public IController Create(RequestContext requestContext, Type controllerType) {
IController controller = DependencyResolver.Current.GetService(controllerType) as IController;
return controller;
}
}
AppStart_Structuremap.cs
[assembly: WebActivator.PreApplicationStartMethod(typeof(StoreUI.AppStart_Structuremap), "Start")]
namespace MyNameSpace {
public static class AppStart_Structuremap {
public static void Start() {
var container = (IContainer) IoC.Initialize();
DependencyResolver.SetResolver(new StructureMapDependenceyResolver(container));
AspNetHost.SetResolver(new StructureMapDependencyResolver(container));
}
}
}
NotificationsHub.cs
[HubName("notificationsHub")]
public class NotificationsHub : Hub
{
#region Declarations
private readonly IUserService userService;
#endregion
#region Constructor
public NotificationsHub(IUserService userService)
{
this.userService = userService;
}
#endregion
public void updateServer(string message)
{
Clients.updateClient(message);
}
}
Thanks
Getting Structuremap into SignalR is actually pretty easy. First you want to create your own resolver:
StructureMap Resolver
Usings:
using SignalR.Infrastructure;
using StructureMap;
Class:
public class StructureMapResolver : DefaultDependencyResolver
{
private IContainer _container;
public StructureMapResolver(IContainer container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
object service = null;
if (!serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
{
// Concrete type resolution
service = _container.GetInstance(serviceType);
}
else
{
// Other type resolution with base fallback
service = _container.TryGetInstance(serviceType) ?? base.GetService(serviceType);
}
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = _container.GetAllInstances(serviceType).Cast<object>();
return objects.Concat(base.GetServices(serviceType));
}
}
The idea here is to try and use your container to resolve the dependencies, if you do not have the dependency wired up, pass it through to the default resolver. This way you don't have to worry about all of the other dependencies in SignalR and can focus only on the stuff you want to inject into (Hubs, ConnectionIdFactory, MessageBus, etc.).
Bindings for Resolver and Hub
Next you will want to register this in your container (i like using registries):
Usings:
using SignalR.Infrastructure;
using StructureMap.Configuration.DSL;
Class:
public class ExtensionsRegistry : Registry
{
public ExtensionsRegistry()
{
For<IDependencyResolver>().Add<StructureMapResolver>();
}
}
Resolver Replacement
Finally you will want to tell SignalR to use your resolver instead of the default:
Global::Application_Start or WebActivator::Pre_Start
Usings:
using SignalR.Hosting.AspNet;
using SignalR.Infrastructure;
Application_Start:
// Make sure you build up the container first
AspNetHost.SetResolver(StructureMap.ObjectFactory.GetInstance<IDependencyResolver>());
Silly Hub with injected dependencies
Now you can just inject any dependencies your container knows about into the hubs themselves:
[HubName("defaultHub")]
public class DefaultHub : Hub, IDisconnect
{
private readonly IRepository _repo;
public DefaultHub(IRepository repo)
{
_repo = repo;
}
public void Connect()
{
Caller.setUser(Context.ConnectionId);
Clients.addMessage(string.Format("{0} has connected", Context.ConnectionId));
}
public void MessageSender(string message)
{
Caller.addMessage(_repo.RepositoryMessage());
Clients.addMessage(message);
}
public Task Disconnect()
{
var clientId = this.Context.ConnectionId;
return Task.Factory.StartNew(() => { Clients.addMessage(string.Format("{0} has disconnected", clientId)); });
}
}
Have you followed the instructions here:- https://github.com/SignalR/SignalR/wiki/Extensibility ?
You'll need to use AspNetHost.SetResolver.
I know this is an old thread, but for those who are wondering where is the AspNetHost.SetResolver in the newer version of signalR, you can use this in the App_Start StructuremapMvc.cs:
public static void Start() {
IContainer container = IoC.Initialize();
GlobalHost.DependencyResolver = new SignalRSmDependencyResolver(container); // for signalR
DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver(container);
}
Add something like this to a file in your App_Start folder. This code snippet is for Ninject, so just replace AspNetHost.SetResolver()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Ninject;
using SignalR.Hosting.AspNet;
using SignalR.Infrastructure;
using SignalR.Ninject;
using Web.Models;
[assembly: WebActivator.PreApplicationStartMethod(typeof(Web.App_Start.NinjectSignalR), "Start")]
namespace Web.App_Start
{
public static class NinjectSignalR
{
public static void Start()
{
IKernel kernel = CreateKernel();
// switch this line to the structuremap resolver
AspNetHost.SetResolver(new NinjectDependencyResolver(kernel));
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
RegisterServices(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
// add your services here
//kernel.Bind<IRepository>().To<Repository>();
}
}
}
I want to try out so SOA style logging using Snap in my MVC3 application. I'm using Ninject for IoC so have installed Ninject.MVC and Snap.Ninject via Nuget all had a look at the sample code in GitHub for Snap.Ninject. I also read Getting SNAP(AOP), NInject and ASP.Net MVC 3 working together
which seems to be doing exactly what I want.
I've updated my NinjctMVC3.cs accordingly but when I add the interceptor attribute to my method, I get an object ref error from Snap AspectUtility. Here is my NinjectMVC3.cs
public static class NinjectMVC3 {
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start() {
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop() {
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel() {
//var kernel = new StandardKernel();
NinjectAopConfiguration.NinjectAopConfigure();
var kernel = NinjectAopConfiguration._container.Kernel;
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<ILogger>().To<NLogger>()
.WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName); ;
kernel.Bind<ISomeDataFactory>().To<SomeDataFactory>();
}
}
public static class NinjectAopConfiguration {
public readonly static NinjectAspectContainer _container;
static NinjectAopConfiguration() {
_container = new NinjectAspectContainer();
}
public static void NinjectAopConfigure() {
SnapConfiguration.For(_container).Configure(c => {
c.IncludeNamespace("TestAopLogging.Model.*");
c.Bind<MyMethodInterceptor>().To<MyInterceptorAttribute>();
});
}
}
public class MyMethodInterceptor : MethodInterceptor {
public override void InterceptMethod(Castle.DynamicProxy.IInvocation invocation, MethodBase method, System.Attribute attribute) {
var logger = new NLogger(method.DeclaringType.ToString());
logger.LogInfo("Hello AOP Logger. Your method (" + method.Name + ") has been intercepted");
invocation.Proceed();
}
public override void BeforeInvocation() {
var logger = new NLogger("How do I work out what class I'm in?");
base.BeforeInvocation();
}
public override void AfterInvocation() {
var logger = new NLogger("How do I work out what class I'm in?");
logger.LogInfo("Hello AOP Logger. After Invocation");
base.AfterInvocation();
}
}
public class MyInterceptorAttribute : MethodInterceptAttribute { }
And the controller
public class HomeController : Controller
{
private ILogger _logger;
private ISomeDataFactory _someDataFactory;
public HomeController(ILogger logger, ISomeDataFactory someDataFactory) {
_logger = logger;
_someDataFactory = someDataFactory;
}
public ActionResult Index()
{
_logger.LogInfo("I've hit the index action");
_someDataFactory.GetStuffAndLogTheOldWay();
_someDataFactory.GetStuffAndLogUsingAOP();
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
}
and the factory class with the method with the intercept attribute
public interface ISomeDataFactory {
string GetStuffAndLogTheOldWay();
string GetStuffAndLogUsingAOP();
}
public class SomeDataFactory : ISomeDataFactory {
private ILogger _logger;
public SomeDataFactory(ILogger logger) {
_logger = logger;
}
public string GetStuffAndLogTheOldWay() {
_logger.LogInfo(MethodBase.GetCurrentMethod().Name + " was called");
return "I called GetStuffAndLogTheOldWay";
}
[MyInterceptor] // If I comment this out, then all is good
public string GetStuffAndLogUsingAOP() {
return "I called GetStuffAndLogUsingAOP";
}
}
and this results in the following exception
[NullReferenceException: Object reference not set to an instance of an object.]
Snap.AspectUtility.CreateProxy(Type interfaceType, Object instanceToWrap, IInterceptor[] interceptors) +29
Snap.AspectUtility.CreatePseudoProxy(IMasterProxy proxy, Type interfaceType, Object instanceToWrap) +184
Snap.Ninject.AspectProxyActivationStrategy.Activate(IContext context, InstanceReference reference) +376
Ninject.Activation.<>c__DisplayClass2.b__0(IActivationStrategy s) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Pipeline.cs:58
Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map(IEnumerable1 series, Action1 action) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Infrastructure\Language\ExtensionsForIEnumerableOfT.cs:23
Ninject.Activation.Pipeline.Activate(IContext context, InstanceReference reference) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Pipeline.cs:58
Ninject.Activation.Context.Resolve() in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Context.cs:182
Ninject.KernelBase.b__7(IContext context) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\KernelBase.cs:375
System.Linq.<>c__DisplayClass123.<CombineSelectors>b__11(TSource x) +32
System.Linq.WhereSelectEnumerableIterator2.MoveNext() +151
System.Linq.Enumerable.SingleOrDefault(IEnumerable1 source) +4178557
Ninject.Planning.Targets.Target1.GetValue(Type service, IContext parent) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Planning\Targets\Target.cs:179
Ninject.Planning.Targets.Target1.ResolveWithin(IContext parent) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Planning\Targets\Target.cs:147
Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:97
Ninject.Activation.Providers.<>c__DisplayClass2.<Create>b__1(ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:81
System.Linq.WhereSelectArrayIterator2.MoveNext() +85
System.Linq.Buffer1..ctor(IEnumerable1 source) +325
System.Linq.Enumerable.ToArray(IEnumerable1 source) +78
Ninject.Activation.Providers.StandardProvider.Create(IContext context) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:81
Ninject.Activation.Context.Resolve() in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Context.cs:157
Ninject.KernelBase.<Resolve>b__7(IContext context) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\KernelBase.cs:375
System.Linq.<>c__DisplayClass123.b__11(TSource x) +32
System.Linq.WhereSelectEnumerableIterator2.MoveNext() +151
System.Linq.Enumerable.SingleOrDefault(IEnumerable1 source) +4178557
Ninject.Web.Mvc.NinjectDependencyResolver.GetService(Type serviceType) in c:\Projects\Ninject\Maintenance2.2\ninject.web.mvc\mvc3\src\Ninject.Web.Mvc\NinjectDependencyResolver.cs:56
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +51
Thanks in advance. I want a working demo of it failing then let me know.
Thanks to Tyler Brinks for spotting my typo!
Update the namespace reference to
c.IncludeNamespace("TestAopLogging.Models.*");
and all is well.
Hope someone finds this useful.