FluentValidation Custom Function within Validator Class - vb.net

Using this example from the FluentValidation website, I'm converting the concept to VB.NET using my own classes. The part in interest to my issue is the Must(BeOver18), which calls the protected function. Note that this call doesn't pass a parameter to BeOver18:
public class PersonAgeValidator : AbstractValidator<Person> {
public PersonAgeValidator() {
RuleFor(x => x.DateOfBirth).Must(BeOver18);
}
protected bool BeOver18(DateTime date) {
//...
}
}
I created my own validator class in VB.NET like this, using the same principal as above but instead for a function called CustomerExists:
Public Class ContractValidator
Inherits AbstractValidator(Of ContractDTO)
Public Sub New()
RuleSet("OnCreate",
Sub()
RuleFor(Function(x) x.CustomerID).NotEmpty
' Compiler error here:
' BC30455 Argument not specified for parameter 'customerID'.....
RuleFor(Function(x) x.CustomerID).Must(CustomerExists)
End Sub
)
End Sub
Protected Function CustomerExists(customerID As Integer) As Boolean
Return CustomerService.Exists(customerID)
End Function
End Class
ISSUE: The line in VB.NET with .Must(CustomerExists) is giving the "Argument not specified for parameter 'customerID'..." compiler error. The C# example does not pass a parameter to BeOver18. I tried an additional anonymous in-line function to try to pass ContractDTO.CustomerID, but it doesn't work as it's not recognized:
' This won't work:
RuleFor(Function(x) x.CustomerID).Must(CustomerExists(Function(x) x.CustomerID))
I'm at a loss on how the C# example can call it's function without a parameter, but the VB.NET conversion cannot. This is where i need help.

Your CustomerExists function needs to be treated as a delegate. In order to do that, change the following:
Original
RuleFor(Function(x) x.CustomerID).Must(CustomerExists)
Update
RuleFor(Function(x) x.CustomerID).Must(AddressOf CustomerExists)

Related

Anonymous Function Showing End Function Warning

I am trying out FluentValidation, and I'm converting a C# RuleSet example to VB.NET.
Question: Why is VS 2019 putting the green squiggly line under End Function, stating the function doesn't return a value on all code paths. Should I be doing something more in the anonymous function to remove this warning? Just ignore it?
Here is the original C# example:
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleSet("Names", () => {
RuleFor(x => x.Surname).NotNull();
RuleFor(x => x.Forename).NotNull();
});
}
}
Here is my conversion and application in VB.NET, with my own class ContractDTO:
Public Class ContractValidator
Inherits AbstractValidator(Of ContractDTO)
Public Sub New()
RuleSet("OnCreate",
Function()
RuleFor(Function(x) x.CustomerID).NotEmpty
End Function ' Green squiggly line warning here.
)
End Sub
End Class
EDIT: Note that "Show potential fixes" in Visual Studio shows nothing.
A function needs to return a value. Yours doesn't, therefore, you should use Sub/End Sub instead:
Public Class ContractValidator
Inherits AbstractValidator(Of ContractDTO)
Public Sub New()
RuleSet("OnCreate",
Sub()
RuleFor(Function(x) x.CustomerID).NotEmpty
' Add more lines.
End Sub)
End Sub
End Class
If you don't need more lines inside your sub, you may get rid of the End Sub part like so:
RuleSet("OnCreate", Sub() RuleFor(Function(x) x.CustomerID).NotEmpty)

VB.Net - Passing function with return value through constructor

I have an app which uses about 89 different UserControls with very similar methods. I have made a base class for these UserControls with the similar methods within. Four of these methods call a function for selecting, inserting, deleting, and updating database records. My idea to handle this was to pass in these four database functions as parameters to the base class, and reference them in these base methods when I need them. Pseudo Code:
Public MustInherit Class MyAbstractBaseClass
Inherits UserControl
// Protected DatabaseInsertFunction(Of IDBRecord) As Boolean
// Protected DatabaseDeleteFunction(Of IDBRecord) As Boolean
// Protected DatabaseSelectFunction(Of IDBRecord) As Boolean
// Protected DatabaseUpdateFunction(Of IDBRecord) As Boolean
Public Sub New( InsertFunc, ...)
// Me.DatabaseInsertFun = InsertFun
...
End Sub
Protected Sub DoWork(someObject As IDBRecord)
// Do some work here
Dim result As Boolean = DatabaseInsertFunction(someObject)
...
End Sub
End Class
Public Class MyDerivedClass
Inherits MyAbstractBaseClass
Public Sub New()
MyBase.New(AddressOf InsertRecord)
End Sub
Private Sub InsertRecord(someObj as IDBRecord) As Boolean
// some work here
End Sub
End Class
I have tried every variation of syntax I can find on Google and it continues to elude me. Looking at the above code snippet, I would like to pass in the function from the derived class to the base class upon construction and store it in the base' class fields so that they can be called upon by different methods within the base class later. Using Func(Of T) is great except when there are return values I can't figure it out. Any help would be much appreciated

How can I make a "RequiredIf" custom valid attribute in vb.net

I am trying to create a customValidAttribute in VB.NET
Namespace EventRules
Public Class CustomRuleAttribute
Inherits ValidationAttribute
Protected Overrides Function IsValid(value As Object, validationContext as validationContext) As ValidationResult
If EventIsInOneWeek = True Then
'Property is required
End If
Return New ValidationResult(Me.FormatErrorMessage(validationContext.DisplayName))
End Function
And in my Interface
Imports System.ComponentModel.DataAnnotations
Imports EventRules
Namespace Contracts
Public Interface IEvent
Property EventIsInOneWeek As Boolean
<CustomRule()>
Property AdditionalProperty
So, the error I am getting is on EventIsInOneWeek and says "Reference to a non-shared member requires an object reference"
Edit: The object being passed in is a different property than 'EventIsInOneWeek', and I only want it to be required if EventIsInOneWeek is true.
Edit: Also updated the code more completely
As mentioned above -- the simple solution I was looking for was passing the entire business object in with validationContext. However, this exposes some security flaws in my system, so I created this work-around.
In my base business logic:
Public Overridable Function CheckRules() As Boolean
Me.Errors = New List(Of ValidationRules)()
Return Me.Validate(Me.Errors)
End Function
...
Public Overridable Function Validate(validationContext As ValidationContext) As IEnumerable(Of Validation Result) Implements IValidateObject.Validate
Return Nothing
End Function
And In my Business logic for the object itself
Protected Overrides Function Validate(validationContext As ValidationContext) As IEnumerable(Of Validation Result)
Dim results = New List(Of ValidationResult)()
'valiation conditionals here
Return results
End Function
I am passing my base logic into my Business Object, and it seems to be working well, unfortunately it does not auto generate front-end validation the way CustomValidationAttributes do.
This also allows me validationRules a little bit of re-usability that would not be afforded when passing a validationContext in.

VB typeof operator with generics

When comparing types in VB the following works as expected and enables the current instance to be compared against a specific inherited class, in this case returning False (snippet from LINQPad)
Sub Main
Dim a As New MyOtherChildClass
a.IsType().Dump()
End Sub
' Define other methods and classes here
MustInherit class MyBaseClass
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass
End Class
Class MyOtherChildClass
Inherits MyBaseClass
End Class
However when generics are introduced the VB compiler fails with the error Expression of type 'UserQuery.MyBaseClass(Of T)' can never be of type 'UserQuery.MyChildClass'.
' Define other methods and classes here
MustInherit class MyBaseClass(Of T)
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass(Of String)
End Class
Class MyOtherChildClass
Inherits MyBaseClass(Of String)
End Class
The equivalent code in C# compiles and allows the comparison, returning the correct result
void Main()
{
var a = new MyOtherChildClass();
a.IsType().Dump();
}
// Define other methods and classes here
abstract class MyBaseClass<T>
{
public bool IsType()
{
return this is MyChildClass;
}
}
class MyChildClass : MyBaseClass<string>
{
}
class MyOtherChildClass : MyBaseClass<string>
{
}
Why does the VB compiler not allow this comparison?
You raise an interesting point about VB/C# compilation that I can't really speak to. If you're looking for a solution, here's a way to do it from the question How can I recognize a generic class?
Define these functions:
Public Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type) As Boolean
Dim isParentGeneric As Boolean = parentType.IsGenericType
Return IsSubclassOf(childType, parentType, isParentGeneric)
End Function
Private Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type, ByVal isParentGeneric As Boolean) As Boolean
If childType Is Nothing Then
Return False
End If
If isParentGeneric AndAlso childType.IsGenericType Then
childType = childType.GetGenericTypeDefinition()
End If
If childType Is parentType Then
Return True
End If
Return IsSubclassOf(childType.BaseType, parentType, isParentGeneric)
End Function
Call like this:
Dim baseType As Type = GetType(MyBaseClass(Of ))
Dim childType As Type = GetType(MyOtherChildClass)
Console.WriteLine(IsSubclassOf(childType, baseType))
'Writes: True
Here's a Microsoft Connect Ticket that might deal with this issue and give some explanation as to whether this was a feature or a bug of generic typing.
Although this case doesn't seem supported by the Type Of documentation which states that for classes, typeof will return true if:
objectexpression is of type typename or inherits from typename
I'm familiar with C# but less so with VB. However, the example VB code and example C# code appear to be different. In the VB example you use Return TypeOf Me Is MyChildClass, which in C# would be return typeof(this) is MyChildClass;. But the (supposedly working) C# example just has return this is MyChildClass;.
I would expect that TypeOf Me Is MyChildClass is asking whether the instance expression on the left (which is a Type) can be assigned to a variable declared as the type on the right (MyChildClass). Since the framework class Type has no connection to your MyChildClass this is impossible and thus a likely mistake which the compiler can catch with a warning or error--possibly the one you're getting.
Instead, I would think that the VB code should be Return Me Is MyChildClass to match the C# example, which should correctly ask if the instance Me can be assigned to a variable declared as MyChildClass. Does VB still object if this syntax is used, or does that fix the error and get the correct behavior?

Adding a global function

I am trying to add a new global function
I am doing it like that:
Function MessageYNC() As String
{
return "dd";
}
End Function
Public Class SatelliteAPI
End Class
But i am getting error -> Statement is not valid in a namespace.
on the first line
Any idea what is wrong ?
The function belongs inside
the class.
The curly
braces and semicolons don't belong
in VB. Those are C#.
In
order for your method to be global,
you will need to declare it as
Shared. If your entire class
consists of nothing but shared
methods, you may consider creating a
module as opposed to a class.
Here's an explanation of the
difference between a class with
shared methods and a module.
Public Class SatelliteAPI
Public Shared Function MessageYNC() As String
Return "dd"
End Function
End Class 'SatelliteAPI
Public Class TestClass
Public Sub TestMessageMethod()
Console.WriteLine(SatelliteAPI.MessageYNC)
End Sub
End Class 'TestClass
Put the function into a class or module. If you put it into a class, you need to make it Shared. So it's either
Module MyFunctions
Function MessageYNC() As String
Return "dd"
End Function
End Module
or
Public Class SatelliteAPI
Shared Function MessageYNC() As String
Return "dd"
End Function
End Class
in which case you would access it as StaelliteAPI.MessageYNC.