Ninject: property injection into ActionFilterAttribute - c#

I want to implement DI inside ActionFilterAttribute by Ninject. I've found this manual (actually I've read tens of them today) but it's for Unity. My attempt to implement it on Ninject is here:
public class MyFilter : ActionFilterAttribute
{
[Inject]
public IDepend Depend { get; set; }
// another code
}
bindings:
public class NinjectResolver : IDependencyResolver
{
// another code
private IKernel AddRequestBindings(IKernel kernel)
{
// another code
kernel.Bind<IDepend>().To<Depend>();
return kernel;
}
}
custom FilterProvider:
public class WebApiNinjectActionFilterProvider : ActionDescriptorFilterProvider, IFilterProvider
{
private readonly IKernel _kernel;
private readonly IEnumerable<IFilterProvider> _filterProviders;
public WebApiNinjectActionFilterProvider(IKernel kernel, IEnumerable<IFilterProvider> filterProviders)
{
_kernel = kernel;
_filterProviders = filterProviders;
}
public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
var filters = _filterProviders.SelectMany(fp => fp.GetFilters(configuration, actionDescriptor)).ToList();
foreach (var filter in filters)
{
// GET ActivationException HERE!!!!!
_kernel.Inject(filter.Instance);
}
return filters;
}
public static void RegisterFilterProviders(IKernel ninjectKernel)
{
var providers = GlobalConfiguration.Configuration.Services.GetFilterProviders().ToList();
GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider),
new WebApiNinjectActionFilterProvider(ninjectKernel, providers));
var defaultprovider = providers.First(i => i is ActionDescriptorFilterProvider);
GlobalConfiguration.Configuration.Services.Remove(typeof(IFilterProvider), defaultprovider);
}
}
is called from WebApiConfig:
public static void Register(HttpConfiguration config)
{
var ninjectKernel = new StandardKernel();
config.DependencyResolver = new NinjectResolver(ninjectKernel);
WebApiNinjectActionFilterProvider.RegisterFilterProviders(ninjectKernel);
}
and finally action:
[MyFilter]
public async Task<string> Test()
{
return "Hello world";
}
The problem is I get an ActivationException in this line:
_kernel.Inject(filter.Instance);
ActivationException's Activation path:
2) Injection of dependency IDepend into property Depend of type MyFilter
1) Request for MyFilter
If to inject same property to controller - it injects fine. Any ideas how to improve my code?

I was doing IActionFilter DI in wrong way. The right way I've found in this blogpost of Mark Seemann.
DI via Ninject was implemented this way:
var myFilter = new MyFilter(ninjectKernel.Get<IDepend>());
config.Filters.Add(myFilter);

Related

Asp.net Core AutoFac register generic using factory

I'm using Asp.net Core with AutoFac and following the accepted answer here:
Validation: How to inject A Model State wrapper with Ninject?
This uses ninject. I don't understand how to do the equivalent of this ninject part in autoFac, specifically the kernel.Get:
Func<Type, IValidator> validatorFactory = type =>
{
var valType = typeof(Validator<>).MakeGenericType(type);
return (IValidator)kernel.Get(valType);
};
kernel.Bind<IValidationProvider>()
.ToConstant(new ValidationProvider(validatorFactory));
Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var containerBuilder = new ContainerBuilder();
IValidator ValidatorFactory(Type type)
{
var valType = typeof(Validator<>).MakeGenericType(type);
//This line is the problem
// return (IValidator)container.Resolve(valType);
}
containerBuilder.Register(x => new ValidationProvider(ValidatorFactory)).As<IValidationProvider>().SingleInstance();
containerBuilder.RegisterType<UploadValidator>().As<Validator<AudioModel>>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return container.Resolve<IServiceProvider>();
}
The problem is that the container is only available after using .Build() so I don't see how I can do it. Do I need to register this service after calling .Build() and then call .Build() again or is .Resolve() the wrong thing to use here.
Validation classes:
internal sealed class ValidationProvider : IValidationProvider
{
private readonly Func<Type, IValidator> _validatorFactory;
public ValidationProvider(Func<Type, IValidator> validatorFactory)
{
_validatorFactory = validatorFactory;
}
public void Validate(object entity)
{
var results = _validatorFactory(entity.GetType()).Validate(entity).ToArray();
if (results.Length > 0)
throw new ValidationException(results);
}
public void ValidateAll(IEnumerable entities)
{
var results = (
from entity in entities.Cast<object>()
let validator = _validatorFactory(entity.GetType())
from result in validator.Validate(entity)
select result).ToArray();
if (results.Length > 0)
throw new ValidationException(results);
}
}
public abstract class Validator<T> : IValidator
{
IEnumerable<ValidationResult> IValidator.Validate(object entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
return Validate((T)entity);
}
protected abstract IEnumerable<ValidationResult> Validate(T entity);
}
public class UploadValidator : Validator<AudioModel>
{
protected override IEnumerable<ValidationResult> Validate(AudioModel model)
{
if (string.IsNullOrWhiteSpace(model.Name))
{
yield return new ValidationResult("Name", "Name is required");
}
}
}
Autofac has a great feature that enables us to register factories to create instances based on a parameter(s). In your example, we could register a Func<Type, IValidator> with Autofac, and have that automagically injected into our ValidationProvider.
var builder = new ContainerBuilder();
builder
//register our factory function
.Register<Func<Type, IValidator>>(
x =>
{
//get a reference to the scoped container
//e.g. if this is a web app, each HTTP request will
//spawn a child container used for the lifetime of that request
var context = x.Resolve<IComponentContext>();
return type =>
{
//create the validator from our scoped container
var valType = typeof(Validator<>).MakeGenericType(type);
return (IValidator) context.Resolve(valType);
}
}
)};
public class ValidationProvider
{
readonly Func<Type, IValidator> _factory;
//Autofac will see this class requires our previously registered
//function and inject this for us
public ValidationProvider(Func<Type, IValidator> factory)
{
_factory = factory;
}
}
As an alternative, is it possible for you to constrain the IValidator with a generic argument? Perhaps it is not feasible to refactor the code, but if it is, it may be better practice to give our services the exact dependencies they require, rather than a factory which may hide their intent.
public interface IValidator<T>
{
void Validate(T instance);
}
public class SomeClassRequiringAudioModelValidator
{
readonly IValidator<AudioModel> _validator;
public SomeClassRequiringAudioModelValidator(IValidator<AudioModel> validator)
{
_validator = validator;
}
}

Web Api OWIN Host with Unity

I'm trying to implement UNITY on my WebApi2 application.
The problem is that I'm using an existing SqlConnection, depending on an identifier found in the URL of the resource.
So I need the identifier provided in the request uri, to create my context with.
Is it possible to get the {dbIdentifier} from the request URI, and parse it into the constructor of MyRepo?
The Request usi will look like: /api/{dbIdentifier}/{controller}/{id}
The structure looks like...
Request POST /api/myDbIdentifier/my/ + PAYLOAD data
Controller:
public class MyController : ApiController
{
private readonly IRepo _repo;
public MyController(IRepo repo)
{
_repo = repo;
}
}
Repo:
public class MyRepo : IRepo
{
private readonly MyContext _context;
public MyRepo(string dbIdentifier)
{
_context = new MyContext(GetConnection(dbIdentifier));
}
public void Insert(string s)
{
//Inserting string in context and save changes
}
private DbConnection(string id)
{
//psudo find connecion from pool, and return instance of DbConnection...
}
}
public interface IRepo
{
void Insert(string s);
}
Context:
public class MyContext : DbContext
{
public MyContext(DbConnection exitingConnection) : base(existingConnection, true)
{ }
}
Btw, it's my first time playing around with WebApi and Unity, so please bear with my ignorance.
UPDATED Unity part of my code...
UnityResolver (taken from https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection):
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer Container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container), "Please provider an IUnityContainer.");
}
Container = container;
}
public void Dispose()
{
Container.Dispose();
}
public object GetService(Type serviceType)
{
try
{
return Container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return Container.ResolveAll(serviceType);
}
catch (Exception)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
return new UnityResolver(Container.CreateChildContainer());
}
}
Unity Register part in my startup:
public static void Register(HttpConfiguration config)
{
// Configuring DI Container fo IoC (Invert of Control)
var container = new UnityContainer();
container.RegisterType<IRepo, MyRepo>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
}
You can try the following:
1)Create a DelegatingHandler where you can access HtppRequestMessage.RequestUri
2)Extract dbIdentifier from Uri
3)Wrap dbIdentifier with a class (e.g. DbIdentifier) and register it in unity using HierarchicalLifetimeManager
4)Remember to register handler in owin:
httpConfiguration.MessageHandlers.Add(new DbIdentifierHandler());
EDIT.
You can look into this post to find some inspiration :
How to pass Owin context to a Repo being injected into Api controller

WebApi action filter dependency injection not called when applied globally

I have a simple WebApi action (exception) filter...
public class LogExceptionAttribute : ExceptionFilterAttribute
{
[Dependency]
public ILogger Logger { get; set; }
public override void OnException(HttpActionExecutedContext context)
{
Logger.Fatal(context.Exception, "Critical exception in request to WebApi controller.");
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("Internal Server Error."),
ReasonPhrase = "Critical Exception"
});
}
}
A Unity filter provider...
public class UnityFilterProvider : IFilterProvider
{
private IUnityContainer _container;
private readonly ActionDescriptorFilterProvider _defaultProvider = new ActionDescriptorFilterProvider();
public UnityFilterProvider(IUnityContainer container)
{
_container = container;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
var attributes = _defaultProvider.GetFilters(configuration, actionDescriptor);
foreach (var attr in attributes)
{
_container.BuildUp(attr.Instance.GetType(), attr.Instance);
}
return attributes;
}
}
And the following configuration for unity...
var providers = config.Services.GetFilterProviders().ToList();
config.Services.Add(typeof(IFilterProvider), new UnityFilterProvider(container));
var defaultprovider = providers.Single(i => i is ActionDescriptorFilterProvider);
config.Services.Remove(typeof(IFilterProvider), defaultprovider);
var logger = LogFactory.CreateWebApiLogger();
container.RegisterInstance<ILogger>(logger);
When the [LogException] attribute is applied to the controller, the dependency injection works correctly and the ILogger instance is available within the exception filter.
However, when I attempt to configure the filter globally...
config.Filters.Add(new LogExceptionAttribute());
the dependency is not resolved and I get a null reference exception whilst attempting to access the ILogger instance from within the filter.
What am I missing?
Try
config.Filters.Add((IExceptionFilter)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(LogExceptionAttribute)));
APPEND
After your comment, I checked mine further.
I have Constructor dependency
public class LogExceptionAttribute : ExceptionFilterAttribute, IExceptionFilter
{
private ILogger Logger { get; set; }
public LogExceptionAttribute (ILogger logger)
{
this.Logger = logger;
}
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception != null)
{
this.Logger.LogException(context.Exception);
}
}
}
So my suggestion is to switch to constructor injection.

Implementing Container Per Request pattern

I'm going to add in the necessary infrastructure to bind each request to its own nested container, So in this case having a Container Per Request gives us a unique session (Context Per Request), In my code I'm using the new implementation of ObjectFactory:
public static class SmObjectFactory
{
private static readonly Lazy<Container> _containerBuilder =
new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static IContainer Container
{
get { return _containerBuilder.Value; }
}
private static Container defaultContainer()
{
return new Container(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
});
}
}
So, for example If I just set a break point in the ApplicationDbContext's constructor, each time an instance is created. For example in my case I have these controllers:
public partial class HomeController : Controller
{
private readonly IUnitOfWork _uow;
public HomeController(IUnitOfWork uow)
{
_uow = uow;
}
public virtual ActionResult Index()
{
return View();
}
}
public class TestController : Controller
{
private readonly IUnitOfWork _uow;
public TestController(IUnitOfWork uow)
{
_uow = uow;
}
public ActionResult GetData()
{
return Content("Data");
}
}
So the view returned by Index action uses this code to pull in content from TestController:
#Html.Action("GetData", "Test")
In that example, several instances are created per request!
So I've changed SmObjectFactory this way:
public class NewObjectFactory
{
public static IContainer Container { get; set; }
static NewObjectFactory()
{
Container = new Container();
Container.Configure(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
}
}
Then in the Global.asax I've added these lines of code for using nested container:
public IContainer Container
{
get
{
return (IContainer)HttpContext.Current.Items["_Container"];
}
set
{
HttpContext.Current.Items["_Container"] = value;
}
}
public void Application_BeginRequest()
{
Container = NewObjectFactory.Container.GetNestedContainer();
}
public void Application_EndRequest()
{
Container.Dispose();
Container = null;
}
And inside Application_Start:
DependencyResolver.SetResolver(
new StructureMapDependencyResolver(() => Container ?? NewObjectFactory.Container));
And inside of the DependencyResolver I've implemented the factory function this way:
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly Func<IContainer> _factory;
public StructureMapDependencyResolver(Func<IContainer> factory)
{
_factory = factory;
}
public object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
var factory = _factory();
return serviceType.IsAbstract || serviceType.IsInterface
? factory.TryGetInstance(serviceType)
: factory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _factory().GetAllInstances(serviceType).Cast<object>();
}
}
Finaly when I run the application I get this error:
No default Instance is registered and cannot be automatically
determined for type
'Microsoft.Owin.Security.DataProtection.IDataProtectionProvider'
But when I comment this line in the Startup.cs file:
ConfigureAuth(app)
everything works and this time the ApplicationDbContext is created once and then disposed. So that's what I want: Only a single instance of the context is created now and it is correctly disposed of at the end of the web request, It means that the nested container reused the context to satisfy the dependencies of both controllers. :)
Any idea?

Castle Windsor Constructor Injection

I have been trying to get Castle Windsor to inject my DB Context to my controllers I have been following the tutorials on the Castle Windsor website
my code is as follows
Bootstrapper
internal class IOCContainerBootstrap
{
private static IWindsorContainer container;
public static void Configure()
{
container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new GravityClimbingControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
#region IDisposable Members
public static void Dispose()
{
container.Dispose();
}
#endregion
}
Installers
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IController>()
.LifestyleTransient());
container.Register(Component.For<DbContext>().ImplementedBy<GravityClimbingEntities>());
}
}
public class APIInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes
.FromThisAssembly()
.BasedOn<IHttpController>()
.ConfigureFor<ApiController>(c => { c.PropertiesIgnore(pi => false); })
.LifestyleTransient());
}
}
And finally
My API Controller
public class ArticalsController : ApiController
{
private readonly DbContext _context;
private readonly Db.Repositories.ArticalRepository repository;
public ArticalsController(DbContext context)
{
_context = context;
repository = new Db.Repositories.ArticalRepository(context);
}
[HttpGet]
public string HelloWorld()
{
return "Hello.world";
}
}
When I Debug I get no errors and it says it can resolve the dependency
But when i try to call the API controller I get the following Error Message
{
"Message" : "An error has occurred.",
"ExceptionMessage" : "Type 'ArticalsController' does not have a default constructor",
"ExceptionType" : "System.ArgumentException"
}
Is there something silly I am doing wrong, that I cannot see?
For people who are facing the same issue I found the issue.
Castle Windsor didn't know about the IHTTPControler (Base for the APIController) so i needed to create an IHttpControllerActivator and attach it to the GlobalConfiguration.Configuration.Services
I found this link to enable this here

Categories