Monday, April 26, 2010

VS launcher for F# web apps

Sadly, Visual Studio 2010 does not include any F# web application project type.

In practice, this means that if you want to use F# in a web project (be it MVC or WebForms), you have to start with a basic "F# library" project, then manually create a web.config or copy it from somewhere else, manually create a global.asax, global.asax.fs, etc. Or you can let the main web project (i.e. the one with the Global.asax) be a normal C# web app project and reference a F# library where the controllers/webforms are defined.

Another minor annoyance is that you lose the F5 functionality to launch the application, since Visual Studio doesn't know it's a web application. A couple of workarounds for this:

  1. Reference the VS built-in dev web server as the starting external program. This is usually in C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.EXE


    The downside of this is that it doesn't automatically launch your default web browser to your app. It might sound somewhat silly, but when you're used to getting a browser immediately, automatically, it's kind of annoying not having it.

  2. Write a little wrapper around WebServer40.exe that launches a browser. Here's the code:
    open System
    open System.IO
    open System.Diagnostics
    open System.Reflection
    let main args =
        // code adapted from FSharp.PowerPack's AspNetTester
        let progfile = 
            let prg = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
            if Environment.Is64BitProcess
                then prg + " (x86)"
                else prg
        let webserver = Path.Combine(progfile, @"Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.EXE")
        if not (File.Exists webserver)
            then failwith "No ASP.NET dev web server found."
        let getArg arg = args |> Seq.tryFind (fun a -> a.ToUpperInvariant().StartsWith arg)
        let webSitePath = 
            match getArg "PATH:" with
            | None -> Directory.GetParent(Directory.GetCurrentDirectory()).FullName
            | Some a -> a.Substring 5
        let port = 
            match getArg "PORT:" with
            | None -> Random().Next(10000, 65535)
            | Some a -> Convert.ToInt32 (a.Substring 5)
        let vpath =
            match getArg "VPATH:" with
            | None -> ""
            | Some a -> a.Substring 6
        let pathArg = sprintf "/path:%s" webSitePath
        let portArg = sprintf "/port:%d" port
        let asm = Assembly.LoadFile webserver
        let run (args: string[]) = asm.EntryPoint.Invoke(null, [| args |]) :?> int
        Process.Start (sprintf "http://localhost:%d%s" port vpath) |> ignore
        run [| pathArg; portArg |]
    Place the exe (I called it WebStarter.exe) in your web app root, then put it as starting external program:


    You can optionally define a fixed port (by default a random port is used), a different root path or a virtual path to start the browser. Set your F# web app project as "Startup Project", hit F5 and voilĂ , browser launches with the debugger hooked up :)

  3. UPDATE: Steve Gilham has another solution, you can just add a couple of elements to the fsproj to turn it into a web app project.
  4. UPDATE: Tomas Petricek created a MVC project template.

Saturday, April 24, 2010

NHWebConsole 0.1 released

I just released the embeddable NHibernate console for web applications I wrote some months ago. You can get the binary here, it also includes a sample application so you can play with it and see how it's configured. The only requirements for NHWebConsole are .NET 3.5 and NHibernate 2.1.2.

It hasn't changed much since I first wrote about it. I fixed a couple of bugs and added HQL Intellisense thanks to Fatica Labs' wonderful HQL Editor. It's not overly pretty but it mostly works. Here's a screenshot:

Source code and documentation is on github.

Let me know if you use it and/or if you find any bugs!