Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular reference and constructors

I'm trying to build an Attribute that validates a certain instance of a type.

In order to do this I have to cast the ObjectInstance to that type.

And I need to set the attribute on the member of that type.

So we need to resort to the and keyword for the circular definition.

However in the following case I get the error that

A custom attribute must invoke an object constructor

On the line marked below.

namespace Test

open System
open System.ComponentModel.DataAnnotations

[<AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)>]
type MyAttribute() =
    class
    inherit ValidationAttribute ()

    override this.IsValid (value: Object, validationContext: ValidationContext) =
        match validationContext.ObjectInstance with
        | :? MyClass as item ->
            // TODO more validation
            ValidationResult.Success
        | _ ->
            new ValidationResult("No no no")
    end
and MyClass(someValue) =
    [<Required>]
    [<Range(1, 7)>]
  //vvvvvvvvvvvvvvv
    [<MyAttribute>]
  //^^^^^^^^^^^^^^^
    member this.SomeValue : int = someValue

I tried manually invoking the constructor, such as:

[<MyAttribute()>]
// or
[<new MyAttribute()>]

But none of them are accepted by the system.

Can an F# guru help me out here?

like image 310
Snake Avatar asked Apr 13 '16 12:04

Snake


People also ask

What is an example of a circular reference?

"When an Excel formula refers back to its own cell, either directly or indirectly, it creates a circular reference.". For instance, if you select cell A1 and type =A1 in it, this would create an Excel circular reference.

Can We pass circular references via setters in spring?

We can, however, refactor our above code and can pass circular references via setters but that would kill the semantics of 'initializing mandatory final fields via constructors. The same problem Spring faces when it has to inject the circular dependencies via constructor. Spring throws BeanCurrentlyInCreationException in that situation.

How do you create a circular reference in Excel?

"When an Excel formula refers back to its own cell, either directly or indirectly, it creates a circular reference.". For instance, if you select cell A1 and type =A1 in it, this would create an Excel circular reference. Entering any other formula or calculation referring to A1 would have the same affect, e.g.

What is a circular dependency?

What Is a Circular Dependency? It happens when a bean A depends on another bean B, and the bean B depends on the bean A as well: Of course, we could have more beans implied:


3 Answers

Interesting one. It seems that the type inference is really not getting that right. The correct syntax to use here is [<MyAttribute()>], but despite you using the and keyword, the MyAttribute class is not yet known.

Here is a workaround: First check that the object to validate is really of the right type, then use reflection to invoke a validation method:

[<AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)>]
type MyAttribute() =
    inherit ValidationAttribute ()

    override this.IsValid (value: Object, validationContext: ValidationContext) =
        let t = validationContext.ObjectInstance.GetType()
        if t.FullName = "Test.MyClass" then
            let p = t.GetMethod("IsValid")
            if p.Invoke(validationContext.ObjectInstance, [| |]) |> unbox<bool> then
                ValidationResult.Success
            else
                ValidationResult("failed")
        else
            new ValidationResult("No no no")

type MyClass(someValue: int) =
    [<Required>]
    [<Range(1, 7)>]
    [<MyAttribute()>]
    member this.SomeValue = someValue

    member this.IsValid() = someValue <= 7

Edit: to make that slightly cleaner, you could add an interface, that you use in your validation attribute, and later implement in your class.

type IIsValid =
    abstract member IsValid: unit -> bool

Your IsValid method then becomes

    override this.IsValid (value: Object, validationContext: ValidationContext) =

        match validationContext.ObjectInstance with
        | :? IIsValid as i -> 
            if i.IsValid() then
                ValidationResult.Success
            else
                ValidationResult("failed")
        | _ ->
            ValidationResult("No no no")

in your class, this looks like:

type MyClass(someValue: int) =
    [<Required>]
    [<Range(1, 7)>]
    [<MyAttribute()>]
    member this.SomeValue = someValue

    interface IIsValid with
        member this.IsValid() = someValue <= 7
like image 74
Anton Schwaighofer Avatar answered Oct 25 '22 19:10

Anton Schwaighofer


One solution would be to first describe your types in a signature files.

Since the attribute is specified in the signature file, you don't need to add it again in the implementation file:

Foo.fsi:

namespace Foo

open System

[<AttributeUsage(AttributeTargets.Property)>]
type MyAttribute =
    inherit System.Attribute

    new : unit -> MyAttribute

    member Foo : unit -> MyClass

and MyClass =
    new : someValue : int -> MyClass

    [<MyAttribute()>]
    member SomeValue : int

Foo.fs:

namespace Foo

open System

[<AttributeUsage(AttributeTargets.Property)>]
type MyAttribute() =
    inherit Attribute()

    member this.Foo () =
        new MyClass(1)

and MyClass(someValue) =
    // [<MyAttribute()>] -> specified in the fsi, still appears in compiled code
    member this.SomeValue : int = someValue

See https://msdn.microsoft.com/en-us/library/dd233196.aspx for reference

like image 29
Arnaud Rebts Avatar answered Oct 25 '22 17:10

Arnaud Rebts


One thing that you can do to get rid of mutual recursion is to break up MyClass definition into two and use type augmentation to add the members you want to mark with the attribute.

type MyClass(someValue: int) =
    member internal this.InternalSomeValue = someValue

type MyAttribute() = 
    inherit ValidationAttribute()
    (* you can refer to MyClass here *)

type MyClass with
    [<MyAttribute()>]
    member this.SomeValue = this.InternalSomeValue

That's closer to what you're asking for, but I like the interface idea better.

like image 36
scrwtp Avatar answered Oct 25 '22 18:10

scrwtp