F# on aspnetcore: Escaping the framework

February 15, 2017 @ 2:24 pm Posted to .Net, dotnetcore, F# by Antony Koch

Mark Seemann has both blogged and talked about escaping the OO .Net Web API framework in order to use a more idiomatic functional style. This is achieved by providing a function per verb to the controller’s constructor, and replacing the IHttpControllerActivator:

type CompositionRoot() =  
    interface IHttpControllerActivator with
        member this.Create(request, controllerDescriptor, controllerType) =
            if controllerType = typeof<HomeController> then
                new HomeController() :> IHttpController
            elif controllerType = typeof<DoesSomethingController> then
                let imp x = x * x
                let c = new DoesSomethingController(imp) :> _
            else
                raise
                <| ArgumentException(
                    sprintf "Unknown controller type requested: %O" controllerType,
                    "controllerType")    

Then in the startup for your app (global or Startup):

GlobalConfiguration.Configuration.Services.Replace(  
    typeof<IHttpControllerActivator>,
        CompositionRoot(
            reservations, 
            notifications, 
            reservationRequestObserver, 
            seatingCapacity))

This works great, and I love its honesty. It makes you feel the pain, to quote Greg Young, and in composing tight workflows in your composition root the ‘what’ of your domain is laid bare.

However, this won’t work in aspnetcore because it’s more Mvc and less WebApi, or – to use MS phrasology – more Web and less Http, meaning there’s no IHttpControllerActivator. The fix is simple, and aligned with the terminology: drop the ‘http!’ One instead replaces the IHttpControlleractivator with an IControllerActivator instance inside the aspnetcore DI framework and the same results are achieved:

type CustomControllerActivator() =  
    interface IControllerActivator with
        member this.Create(c : ControllerContext) : obj =
            if c.ActionDescriptor.ControllerTypeInfo.AsType() = 
typeof<DoesSomethingController> then  
                let imp x = x * x
                new DoesSomethingController(imp) |> box
            else   
                invalidArg "controllerType" "Cannot find controller"

        member this.Release (c : ControllerContext, ctrl : obj) =   
            ()

And in your OWIN startup:

    member this.ConfigureServices (services:IServiceCollection) =
        services.AddSingleton<IControllerActivator>(new CustomControllerActivator()) |> ignore

        services.AddMvc() |> ignore

Sorted!

Leave a Reply

Your email address will not be published. Required fields are marked *