Constructor Dependency Injection WebApi Attributes - asp.net-web-api

I have been looking around for a non Parameter injection option for the WebApi attributes.
My question is simply whether this is actually possible using Structuremap?
I have been googling around but keep coming up with either property injection (which I prefer not to use) or supposed implementations of constructor injection that I have thus far been unable to replicate.
My container of choice is Structuremap however any example of this will suffice as I am able to convert it.
Anyone ever managed this?

Yes, it is possible. You (like most people) are being thrown by Microsoft's marketing of Action Filter Attributes, which are conveniently put into a single class, but not at all DI-friendly.
The solution is to break the Action Filter Attribute into 2 parts as demonstrated in this post:
An attribute that contains no behavior to flag your controllers and action methods with.
A DI-friendly class that implements IActionFilter and contains the desired behavior.
The approach is to use the IActionFilter to test for the presence of the attribute, and then execute the desired behavior. The action filter can be supplied with all dependencies (through the constructor) and then injected when the application is composed.
IConfigProvider provider = new WebConfigProvider();
IActionFilter filter = new MaxLengthActionFilter(provider);
config.Filters.Add(filter);
NOTE: If you need any of the filter's dependencies to have a lifetime shorter than singleton, you will need to use a GlobalFilterProvider as in this answer.
To wire this up with StructureMap, you will need to return an instance of the container from your DI configuration module. The Application_Start method is still part of the composition root, so you can use the container anywhere within this method and it is still not considered a service locator pattern. Note that I don't show a complete WebApi setup here, because I am assuming you already have a working DI configuration with WebApi. If you need one, that is another question.
public class DIConfig()
{
public static IContainer Register()
{
// Create the DI container
var container = new Container();
// Setup configuration of DI
container.Configure(r => r.AddRegistry<SomeRegistry>());
// Add additional registries here...
#if DEBUG
container.AssertConfigurationIsValid();
#endif
// Return our DI container instance to the composition root
return container;
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Hang on to the container instance so you can resolve
// instances while still in the composition root
IContainer container = DIConfig.Register();
AreaRegistration.RegisterAllAreas();
// Pass the container so we can resolve our IActionFilter
WebApiConfig.Register(GlobalConfiguration.Configuration, container);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
public static class WebApiConfig
{
// Add a parameter for IContainer
public static void Register(HttpConfiguration config, IContainer container)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// Add our action filter
config.Filters.Add(container.GetInstance<IMaxLengthActionFilter>());
// Add additional filters here that look for other attributes...
}
}
The implementation of MaxLengthActionFilter would look something like this:
// Used to uniquely identify the filter in StructureMap
public interface IMaxLengthActionFilter : System.Web.Http.Filters.IActionFilter
{
}
public class MaxLengthActionFitler : IMaxLengthActionFilter
{
public readonly IConfigProvider configProvider;
public MaxLengthActionFilter(IConfigProvider configProvider)
{
if (configProvider == null)
throw new ArgumentNullException("configProvider");
this.configProvider = configProvider;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor);
if (attribute != null)
{
var maxLength = attribute.MaxLength;
// Execute your behavior here (before the continuation),
// and use the configProvider as needed
return continuation().ContinueWith(t =>
{
// Execute your behavior here (after the continuation),
// and use the configProvider as needed
return t.Result;
});
}
return continuation();
}
public bool AllowMultiple
{
get { return true; }
}
public MaxLengthAttribute GetMaxLengthAttribute(ActionDescriptor actionDescriptor)
{
MaxLengthAttribute result = null;
// Check if the attribute exists on the action method
result = (MaxLengthAttribute)actionDescriptor
.GetCustomAttributes(typeof(MaxLengthAttribute), false)
.SingleOrDefault();
if (result != null)
{
return result;
}
// Check if the attribute exists on the controller
result = (MaxLengthAttribute)actionDescriptor
.ControllerDescriptor
.GetCustomAttributes(typeof(MaxLengthAttribute), false)
.SingleOrDefault();
return result;
}
}
And, your attribute which should not contain any behavior should look something like this:
// This attribute should contain no behavior. No behavior, nothing needs to be injected.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class MaxLengthAttribute : Attribute
{
public MaxLengthAttribute(int maxLength)
{
this.MaxLength = maxLength;
}
public int MaxLength { get; private set; }
}

I struggled with custom action filter providers, without getting it to work for my auth attributes. I also trying out various approaches with constructor and property injection, but did not find a solution that felt nice.
I finally ended up injecting functions into my attributes. That way, unit tests can inject a function that returns a fake or mock, while the application can inject a function that resolves the dependency with the IoC container.
I just wrote about this approach here: http://danielsaidi.com/blog/2015/09/11/asp-net-and-webapi-attributes-with-structuremap
It works really well in my project and solves all problems I had with the other approaches.

Related

Injecting Non-User-Submitted Data For Use During Validation

From what I can tell, ASP.Net Core performs model state validation before calling the relevant controller action method. This means that code in the action method isn't given an opportunity to add data to the model before it is validated.
What is the ASP.Net Core way of giving a view model access to additional, non-user-submitted data prior to validation?
Example
What I'm trying to do (doesn't work).
The view model's Validate method expects data to be in ValidOptions. However, since validation occurs before the controller can set this property, validation causes the view model to throw an ArgumentNullException.
// From the Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Process([Bind("SelectedId")]ViewModels.Import details)
{
// data needed for validation
details.ValidOptions = await service.ImportTypes.ToListAsync();
if (ModelState.ValidationState != ModelValidationState.Valid) {
// ...
}
}
// From ViewModels.Import
public IEnumerable<Option> ValidOptions { get; set; }
public int SelectdId {get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// throws ArgumentNullException because ValidOptions hasn't been set when this is executed
var option = ValidOptions.Single(t => t.Id == SelectdId);
//...
}
Probably many ways to skin a cat here. But the easiest for you is probably custom model binders. It's a way to "supplement" or change the binding of your model before it hits the controller. I will say that some see it as extremely bad practice to call an external service/repository at the point of model binding, but it does work and can come in handy.
You need to implement a class that inherits from IModelBinder.
public class MyViewModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
//Bind here. Including calling external services if you want.
}
}
Then you need to implement a provider, this essentially says "when" to bind.
public class MyViewModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(MyViewModel))
return new MyViewModelBinder();
return null;
}
}
In your configure method of your startup.cs, you need to add the provider to the ModelBinderProviders list.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc(config =>
config.ModelBinderProviders.Add(new MyViewModelBinderProvider())
);
}
Further Documentation :
http://dotnetcoretutorials.com/2016/12/28/custom-model-binders-asp-net-core/
http://intellitect.com/custom-model-binding-in-asp-net-core-1-0/
I don't think the official documentation has an article on custom model binders yet unfortunately.

Use named registration in autofac with MVC Filter Attribute Property Injection

I'm using Autofac to fill in public properties of my filters, according to https://code.google.com/p/autofac/wiki/Mvc3Integration#Filter_Attribute_Property_Injection and it worked great.
Until I tried to use a named registration for one of the dependencies. I cannot find a way to do it. I tried to manually register my filters like so:
builder.RegisterType<MyCustomAttribute>()
.WithProperty(ResolvedParameter.ForNamed<INamedDependency>("dependencyName"));
before calling the RegisterFilterProvider method, but that didn't work.
Any ideas? In case this has been fixed in a newer version, I'm using version 2.5.2.830.
Thanks,
Kostas
May be you just forgot to register INamedDependency instance in your container:
public class MyCustomAttribute : FilterAttribute
{
public IDependencyName DependencyName { get; set; }
}
public interface IDependencyName
{
}
public class DependencyName : IDependencyName
{
}
[Test]
public void ResolveCustomTest()
{
// Arrange
var dependencyInstance = new DependencyName();
var builder = new ContainerBuilder();
builder.RegisterInstance(dependencyInstance).Named<IDependencyName>("dependencyName");
builder.RegisterType<MyCustomAttribute>().WithProperty(ResolvedParameter.ForNamed<IDependencyName>("dependencyName"));
builder.RegisterFilterProvider();
var root = builder.Build();
// Act
var attr = root.BeginLifetimeScope("AutofacWebRequest").Resolve<MyCustomAttribute>();
// Assert
Assert.AreEqual(attr.DependencyName, dependencyInstance);
}

MVC: Invoking overloaded constructors conditionally

I have an MVC application where I am implementing CQRS where I have seperated saving data from reading data into seperate interfaces. I am using constructor injection for injecting the concrete instances of these interfaces into the Controller. For constructor injection I am using Unity container. See below example
//The Employee controller
public class EmployeeController : Controller
{
IEmployeeRepository _Writer;
IEmployeeQuery _Reader;
//constructor injection
public EmployeeController(IEmployeeRepository writer, IEmployeeQuery reader)
{
this._Writer = writer;
this._Reader = reader;
}
//To Do: constructor injection for write operations only
public EmployeeController(IEmployeeRepository writer)
{
this._Writer = writer;
}
//To Do: constructor injection for read operations only
public EmployeeController(IEmployeeQuery reader)
{
this._Reader = reader;
}
}
//Registration of the concrete types in the unity container.
public static class Bootstrapper
{
public static void ConfigureUnityContainer()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IEmployeeRepository, EmployeeRepository>(new HttpContextLifetimeManager<IEmployeeRepository>());
container.RegisterType<IEmployeeQuery, EmployeeQueries>(new HttpContextLifetimeManager<IEmployeeQuery>());
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
}
//The derived Controller Factory for injection dependencies in the Controller constructor
public class UnityControllerFactory : DefaultControllerFactory
{
IUnityContainer container;
public UnityControllerFactory(IUnityContainer container)
{
this.container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
{
throw new ArgumentNullException("controllerType");
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(String.Format("Type requested is not a controller: {0}", controllerType.Name), "controllerType");
}
return container.Resolve(controllerType) as IController;
}
catch (Exception)
{
return null;
}
}
}
I have figured out that for any action I will either be fetching data or writing data but not both. In that case I need to invoke the controller constructors conditionally depending on which of "_Writer" or "_Reader" I need to initialize.
How can this be done ?
Looks like you have one controller where you should use two? If you never need to be able to both read and write I would consider to refactor that component towards single responsibility.
If you don't want to do that I would consider injecting a NullObject instead of not injecting that dependency at all. See this thread.
The TecX project contains an extension that mimics NInject's contextual binding. That would allow you to specify when to inject what dependency. The code can be found inside the TecX.Unity project (folder ContextualBinding). The tests that show how to use it are inside the TecX.Unity.ContextualBinding.Test project).
What about lazy loading components? You resolve both dependencies but only one that is really used is initialized.
Sample here: http://pwlodek.blogspot.com/2010/05/lazy-and-ienumerable-support-comes-to.html

ASP.NET Web API Ninject constructor injected custom filter and attributes

I'm struggling with getting a custom attribute / filter working with ninject, constructor injection on the ASP.NET Web API.
Here's a few snippets to give some context...
//controller
[ApiAuthorise]
public IEnumerable<Thing> Get()
// Attribute definition with no body
public class ApiAuthoriseAttribute : FilterAttribute {}
// Custom Filter definition
public class ApiAuthoriseFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{ throw new NotImplementedException(); }
}
//Ninject module for my API authorisation
public class ApiAuthoriseModule : NinjectModule
{
public override void Load()
{
this.BindFilter<ApiAuthoriseFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<ApiAuthoriseAttribute>()
}}
//The registerServices(IKernel kernel) method in NinjectMVC3.cs
kernel.Load(new ApiAuthoriseModule());
That's literally all the code I have concerning this filter and attribute.
From what I understand I don't have to explicitly add the filter to the global filter collection as ninject takes care of that, is that correct?
If I place a constructor inside my attribute and throw an exception from within there I can see that the attribute is firing.
My suspicion is something I'm doing wrong within the Ninject side of things but after spending an afternoon reading others examples that appear to be identical to mine I'm know asking for help :)
TIA
There are different classes that you need to work with in Web API, not the standard System.Web.Mvc.FilterAttribute and System.Web.Mvc.IAuthorizationFilter that are used in normal controllers:
public class ApiAuthoriseAttribute : System.Web.Http.Filters.FilterAttribute
{
}
public class ApiAuthoriseFilter : System.Web.Http.Filters.IAuthorizationFilter
{
public System.Threading.Tasks.Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<System.Threading.Tasks.Task<HttpResponseMessage>> continuation)
{
throw new NotImplementedException();
}
public bool AllowMultiple
{
get { return false; }
}
}
Then you will obviously have to modify Ninject and the filter binding syntax (BindFilter extension method) to be able to register this new classes. Or wait for Ninject.MVC4 which will include this functionality.

Issues with my MVC repository pattern and StructureMap

I have a repository pattern i created on top of the ado.net entity framework. When i tried to implement StructureMap to decouple my objects, i kept getting StackOverflowException (infinite loop?). Here is what the pattern looks like:
IEntityRepository where TEntity : class
Defines basic CRUD members
MyEntityRepository : IEntityRepository
Implements CRUD members
IEntityService where TEntity : class
Defines CRUD members which return common types for each member.
MyEntityService : IEntityService
Uses the repository to retrieve data and return a common type as a result (IList, bool and etc)
The problem appears to be with my Service layer. More specifically with the constructors.
public PostService(IValidationDictionary validationDictionary)
: this(validationDictionary, new PostRepository())
{ }
public PostService(IValidationDictionary validationDictionary, IEntityRepository<Post> repository)
{
_validationDictionary = validationDictionary;
_repository = repository;
}
From the controller, i pass an object that implements IValidationDictionary. And i am explicitly calling the second constructor to initialize the repository.
This is what the controller constructors look like (the first one creates an instance of the validation object):
public PostController()
{
_service = new PostService(new ModelStateWrapper(this.ModelState));
}
public PostController(IEntityService<Post> service)
{
_service = service;
}
Everything works if i don't pass my IValidationDictionary object reference, in which case the first controller constructor would be removed and the service object would only have one constructor which accepts the repository interface as the parameter.
I appreciate any help with this :) Thanks.
It looks like the circular reference had to do with the fact that the service layer was dependent on the Controller's ModelState and the Controller dependent on the Service layer.
I had to rewrite my validation layer to get this to work. Here is what i did.
Define generic validator interface like below:
public interface IValidator<TEntity>
{
ValidationState Validate(TEntity entity);
}
We want to be able to return an instance of ValidationState which, obviously, defines the state of validation.
public class ValidationState
{
private readonly ValidationErrorCollection _errors;
public ValidationErrorCollection Errors
{
get
{
return _errors;
}
}
public bool IsValid
{
get
{
return Errors.Count == 0;
}
}
public ValidationState()
{
_errors = new ValidationErrorCollection();
}
}
Notice that we have an strongly typed error collection which we need to define as well. The collection is going to consist of ValidationError objects containing the property name of the entity we're validating and the error message associated with it. This just follows the standard ModelState interface.
public class ValidationErrorCollection : Collection<ValidationError>
{
public void Add(string property, string message)
{
Add(new ValidationError(property, message));
}
}
And here is what the ValidationError looks like:
public class ValidationError
{
private string _property;
private string _message;
public string Property
{
get
{
return _property;
}
private set
{
_property = value;
}
}
public string Message
{
get
{
return _message;
}
private set
{
_message = value;
}
}
public ValidationError(string property, string message)
{
Property = property;
Message = message;
}
}
The rest of this is StructureMap magic. We need to create validation service layer which will locate validation objects and validate our entity. I'd like to define an interface for this, since i want anyone using validation service to be completely unaware of the StructureMap presence. Besides, i think sprinkling ObjectFactory.GetInstance() anywhere besides the bootstrapper logic a bad idea. Keeping it centralized is a good way to insure good maintainability. Anyway, i use the decorator pattern here:
public interface IValidationService
{
ValidationState Validate<TEntity>(TEntity entity);
}
And we finally implement it:
public class ValidationService : IValidationService
{
#region IValidationService Members
public IValidator<TEntity> GetValidatorFor<TEntity>(TEntity entity)
{
return ObjectFactory.GetInstance<IValidator<TEntity>>();
}
public ValidationState Validate<TEntity>(TEntity entity)
{
IValidator<TEntity> validator = GetValidatorFor(entity);
if (validator == null)
{
throw new Exception("Cannot locate validator");
}
return validator.Validate(entity);
}
#endregion
}
I'm going to be using validation service in my controller. We could move it to the service layer and have StructureMap use property injection to inject an instance of controller's ModelState to the service layer, but i don't want the service layer to be coupled with ModelState. What if we decide to use another validation technique? This is why i'd rather put it in the controller. Here is what my controller looks like:
public class PostController : Controller
{
private IEntityService<Post> _service = null;
private IValidationService _validationService = null;
public PostController(IEntityService<Post> service, IValidationService validationService)
{
_service = service;
_validationService = validationService;
}
}
Here i am injecting my service layer and validaton service instances using StructureMap. So, we need to register both in StructureMap registry:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
That's it. I don't show how i implement my PostValidator, but it's simply implementing IValidator interface and defining validation logic in the Validate() method. All that's left to do is call your validation service instance to retrieve the validator, call the validate method on your entity and write any errors to ModelState.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "PostId")] Post post)
{
ValidationState vst = _validationService.Validate<Post>(post);
if (!vst.IsValid)
{
foreach (ValidationError error in vst.Errors)
{
this.ModelState.AddModelError(error.Property, error.Message);
}
return View(post);
}
...
}
Hope i helped somebody out with this :)
I used a similar solution involving a generic implementor of IValidationDictionary uses a StringDictionary and then copied the errors from this back into the model state in the controller.
Interface for validationdictionary
public interface IValidationDictionary
{
bool IsValid{get;}
void AddError(string Key, string errorMessage);
StringDictionary errors { get; }
}
Implementation of validation dictionary with no reference to model state or anything else so structuremap can create it easily
public class ValidationDictionary : IValidationDictionary
{
private StringDictionary _errors = new StringDictionary();
#region IValidationDictionary Members
public void AddError(string key, string errorMessage)
{
_errors.Add(key, errorMessage);
}
public bool IsValid
{
get { return (_errors.Count == 0); }
}
public StringDictionary errors
{
get { return _errors; }
}
#endregion
}
Code in the controller to copy the errors from the dictionary into the model state. This would probably be best as an extension function of Controller.
protected void copyValidationDictionaryToModelState()
{
// this copies the errors into viewstate
foreach (DictionaryEntry error in _service.validationdictionary.errors)
{
ModelState.AddModelError((string)error.Key, (string)error.Value);
}
}
thus bootstrapping code is like this
public static void BootstrapStructureMap()
{
// Initialize the static ObjectFactory container
ObjectFactory.Initialize(x =>
{
x.For<IContactRepository>().Use<EntityContactManagerRepository>();
x.For<IValidationDictionary>().Use<ValidationDictionary>();
x.For<IContactManagerService>().Use<ContactManagerService>();
});
}
and code to create controllers is like this
public class IocControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return (Controller)ObjectFactory.GetInstance(controllerType);
}
}
Just a quick query on this. It's helped me out quite a lot so thanks for putting the answer up, but I wondered which namespace TEntity exists in? I see Colletion(TEntity) needs System.Collections.ObjectModel. My file compiles without anything further but I see your TEntity reference highlighted in Blue which suggests it has a class type, mine is Black in Visual Studio. Hope you can help. I'm pretty keen to get this working.
Have you found any way to seperate validation into the service layer at all? My gut tells me that validating in the Controller is a bit smelly but I've looked high and low to find a way to pass validation error messages back to the controller without tightly coupling the service layer to the controller and can't find anything. :(
Again, thanks for the great post!
Lloyd

Resources