4

In ReactiveUI 5.2.0 and F# 3.1, the following F# code causes an InvalidOperationException when constructing the object (from a C# WPF app)

The message is "The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized" and it occurs during a read of the property passed into ObservableForProperty (although the default is to skip the initial value).

type MyVM() as this =
inherit ReactiveObject()

let source : obj = obj()

let ofp =            
    this.ObservableForProperty([|"Source"|])
         .Select(fun x -> x.Value)
        .ToProperty(this, (fun y -> y.Result), obj()) // Exception when executing this

member this.Result with get() = ofp.Value
member this.Source with get() = source // Exception here "The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized"

Edited to add: The problem seems to be that the ToProperty is causing ObservableForProperty to query the "Source" property on subscribe, and F# has checks that the constructor has completed before properties are queried.

More info: In ReactiveNotifyPropertyChangedMixin.nestedObservedChanges, the combination of kicker and fillInValue cause the value to be queried before any change is notified via PropertyChanged

marklam
  • 5,346
  • 1
  • 24
  • 26

3 Answers3

3

From ReactiveUI version 5.4.0, there's a new overload to ObservableForProperty that takes a simple non-chained property to monitor.

If you add to that the following extension method:

let toPropName(query : Expr) = 
    match query with
        | PropertyGet(a, b, list) -> b.Name
        | _ -> ""

[<Extension>]
type ReactiveObjectExtender =
    [<Extension>]
    static member ObservableForProperty<'u, 't when 'u :> ReactiveObject>(this: 'u, expr : Expr<'t>, ?beforeChange, ?skipInitial) =
        let propertyName = toPropName expr
        this.ObservableForProperty<'u, 't>(propertyName, defaultArg beforeChange false, defaultArg skipInitial true)

Then you can observe property changes using the syntax:

this.ObservableForProperty(<@ this.MyProperty @>)
marklam
  • 5,346
  • 1
  • 24
  • 26
2

Hm, while I'm no F# expert, you might have to eschew ToProperty (it's actually just a helper anyways) and use a read/write property (i.e. one constructed via RaiseAndSetIfChanged) and a simple Subscribe + property assignment. So Mutable and Gross!

ReactiveUI really likes to initialize properties in the constructor, because it sets up the initial state of the app (if you use ObservableForProperty, you'll find that you have to use the .StartWith operator all the time or else things won't work until the first time they're changed)

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • I'm also no F# expert - but since I like Reactive UI in C#-land, I wanted to use it in F#-land too. I found I can avoid that mutable grossness if I only need to monitor a single (non-chained) property on the ReactiveObject, but it needs to used the Mixin internals. I've sent a pull request for a new overload, together with an example F# snippet that makes it nicer to use. See what you think of it. – marklam Nov 15 '13 at 11:09
1

Calling ToProperty with deferSubscription = true also solves this.

Chris Pacejo
  • 2,817
  • 4
  • 17
  • 17