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?
"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.
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.
"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? 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:
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
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With