Search code examples
wpff#prismtype-providers

Unity and type providers


Upon running the following code, in a project from F# WPF template, to which is added the Nuget Prism

module MainApp

open System
open System.Windows
open System.Windows.Controls
open Microsoft.Practices.Unity
open Microsoft.Practices.Prism.UnityExtensions;
open FSharpx

type Shell = XAML<"MainWindow.xaml">

type App () =
   inherit Application()

   override x.OnStartup e =
      base.OnStartup(e);
      let bt = new BootStrapper()
      bt.Run()

and  BootStrapper () = 
   inherit UnityBootstrapper()

   override x.CreateShell() = 
     let a = x.Container.Resolve<Shell>()

     let b = a.Root
     b :> DependencyObject

   override x.InitializeShell()= 
         base.InitializeShell();
         App.Current.MainWindow <- x.Shell :?> Window
         App.Current.MainWindow.Show()


[<STAThread>]
(new App()).Run() |> ignore

I get no error at compile time, but at runtime an exception says that a.Root is a FrameworkElement, which can not be casted to a Window.

Upon debugging, I see that the runtime content of 'a' is of same type than the internal representation of the XAML Type provider, a {FSharpx.TypeProviders.XamlProvider.XamlFile}, as in here, and its internal dictionary is empty.

I am not sure the internal representation of the TP is supposed to surface out. It looks as if the Type Provider mechanic is ignored by Unity. I suppose it is because Unity seems to work with reflection to figure out dependencies.

Has anyone experienced a similar behaviour using TP that could shed some light ?

PS :This discrepency compile/runtime in F# is quite suprising. Although there must be a good reason for it I had forgotten the possibility of such occurences !


Solution

  • As I see in FSharpX source code, Xaml type provider is erased one - you don't have Shell type as is in metadata, everything that works with this type is erased to operations with base type - XamlFile. So this

    let a = x.Container.Resolve<Shell>()
    

    will be erased to smth like

    let a = x.Container.Resolve<XamlFile>()
    

    so Unity will just create fresh instance of XamlFile. In contrast, if you try to instantiate Shell directly - then F# compiler will use provided constructors so this

    let a = Shell()
    

    will effectively mean

    let a = XamlFile(XamlReader.Parse( <content-of-xaml-file> ))
    

    In your case you can probably instantiate Shell and then use x.Container.BuildUp() to populate its internals.

    type App () =
       inherit Application()
    
       override x.OnStartup e =
          base.OnStartup(e);
          let bt = new BootStrapper()
          bt.Run()
    
    and  BootStrapper () = 
       inherit UnityBootstrapper()
    
       override x.CreateShell() = 
         let a = Shell()
         x.Container.BuildUp(a.Root) :> _
    
       override x.InitializeShell()= 
             base.InitializeShell();
             App.Current.MainWindow <- x.Shell :?> Window
             App.Current.MainWindow.Show()
    
    
    [<STAThread>]
    (new App()).Run() |> ignore