Model binding with FromServices in ASP.Net 5

Here’s a new nifty feature I found in ASP.Net 5 – you can construct your model during model binding with IoC without any additional work. This is available on the dev branch on GitHub and is based on something called ServicesModelBinder. This is actually pretty cool because it means that you can have a model wired up with all of it’s dependencies based on IoC and then bound to your controller action’s parameters.

FromServices attribute

There’s a new attribute called FromServices which is what you use to enable this functionality. For example:

public async Task<ActionResult> GetProduct(
    [FromServices]ProductModel product)
{

}

What this attribute does is tell MVC to bind the model using IServiceActivatorBinderMetadata which is what the FromServices attribute implements. This in turn tells MVC to lookup the model binder that is aware of IServiceActivatorBinderMetadata which happens to be the ServicesModelBinder.

ServicesModelBinder

This model binder is pretty simple, it’s just going to resolve the model type from your IoC container. That could be pretty useful if you need to build up your model properties based on other services. I think some people might argue that this isn’t great practice because this is putting the binding logic in to the model itself instead of using a separate class to perform the binding logic. I suppose it’s up to the individual developer as to what their preference is. You can of course still create your own IModelBinder and use the ModelBinderAttribute to keep the model binding logic in the binder itself.

Incoming route values

Since the model is being created from IoC, how do you get the current route values to build up your model? To do that you’d put a constructor dependency on IContextAccessor<ActionContext>. This will give you all of the current route values, HttpContext, etc… basically everything you’d need to pull the data out of the current request to build your model.

Example

Given the above GetProduct example, the ProductModel class could look like:

public class ProductModel
{
    public ProductModel(IContextAccessor<ActionContext> action, IProductService prodService)
    {
        //TODO: Do some error checking...
        var productId = action.Value.RouteData.Values["product"];
        Value = prodService.Get(productId);
    }

    public IProduct Value { get; private set; }
}

This is pretty simple – the IProduct is looked up from the IProductService based on the incoming ‘product’ route value. Then in the controller you could just do: product.Value to get the value for IProduct.

You then need to ensure that both IProductService and ProductModel are registered as services in your container and it’s really important that the ProductModel is registered as a Transient object

Author

Shannon Thompson

I'm a Senior Software Engineer working full time at Microsoft. Previously, I was working at Umbraco HQ for about 10 years. I maintain several open source projects (many related to Umbraco) such as Articulate, Examine and Smidge, and I also have a commercial software offering called ExamineX. Welcome to my blog :)

comments powered by Disqus