Checkpoints in functions VB.NET - vb.net

I have a function that check validity of inserted data of a form, and in this function I have to ask some confirmation from user, and this confirmation need to be asked outside of the function, so if I hit one of this confirmations, I create the message and send out the validation function, user confirms or not and the function would called again
so here is the problem: I need to put some checkpoints in my function so when I call the validation function I jump to that checkpoint with the selected confirmation from user and run the validation function from that checkpoint
1: is this possible at all?
2: any ideas to do this?
Edit 1: I'm doing this validation in my business layer and can not show any message boxes from there, I just create the message and return it to the UI layer and the answer get from the user and function call again with this answer but I don't want to run the function from beginning and need to run it from where I left
Public Class BL
Private Queue As Queue(Of String)
Public Sub New()
Dim checkpoints = New String(){"CheckPoint1","CheckPoint2","CheckPoint3"}
checkpoints.ToList.ForEach(Function(item) <b>Queue.Enqueue(item)</b>)
End Sub
Public Function Func(ByVal res As Response,ParamArray ByVal params As String()) As Response
Dim response As Response
Dim chk = Queue.Dequeue()
GoTo chk
CheckPoint1:
'Do some stuff
response = New Response(Response.ResponseType.Message,"Are you sure you wanna do this?")
Return response
CheckPoint2:
If(res.Type = Response.ResponseType.ResponseBack)
Dim r As DialogResult = Convert.ChangeType([Enum].Parse(GetType(DialogResult),res.Message),GetType(DialogResult))
If (r= DialogResult.OK)
'Do stuffs on DialogResult.Yes
Else
'Do stuffs on DialogResult.No
End If
'Do other stuffs with database
End If
' Do other stuff
response = New Response(Response.ResponseType.Message,"You should do this!!OK?")
Return response
CheckPoint3:
'Do stuff like CheckPoint1
End Function
End Class
Public Class Response
Friend Enum ResponseType
Message
Result
ResponseBack
None
End Enum
Friend Message As String
Friend Type As ResponseType
Friend Sub New(ByVal type As ResponseType,msg As String)
Message=msg
Type= type
End Sub
End Class
Public Class Form1
Public Sub New()
' This call is required by the designer.
InitializeComponent()
Dim BL As New BL()
' Add any initialization after the InitializeComponent() call.
Dim rese As Response
Do
rese =BL.Func(Nothing)
BL.Func(new Response(Response.ResponseType.ResponseBack,if(MessageBox.Show(rese.Message).ToString())))
Loop Until rese.Type <> Response.ResponseType.Result
MessageBox.Show(if(rese.Message="True","OK","ERROR"))
End Sub
End Class

This is not an objective answer but could help. You need some sort of class that contains question and answers. Your validation class would return a list of question (are you sure?).
Class ValidationOutput
ValidationId
Message
Result
End Class
After calling your validation function, you get a list of validation that need extra information from the user. This can be handle outside of the validation function. When you get the extra information, call the validation again and pass the same list as parameter. When validating, look at the list to see if all the extra information needed is there.

I believe your business logic should not deal with user interactions and split to two parts.
However, if you prefer this way, you can use callbacks. Define a delegate parameter for your validation/business method and call that delegate when you need confirmation. According to return value continue to save operation or not.
You can check link below for delegate passing to a method.
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/delegates/how-to-pass-procedures-to-another-procedure

This should work for you:
Public Class BL
Private Queue As Queue(Of String)
Private _checkpoints As Dictionary(Of String, Func(Of Response, Response)) = New Dictionary(Of String, Func(Of Response, Response)) From
{
{ "CheckPoint1", Function (res) New Response(Response.ResponseType.Message, "Are you sure you wanna do this?") },
{ "CheckPoint2", Function (res)
If (res.Type = Response.ResponseType.ResponseBack)
Dim r As DialogResult = CType(Convert.ChangeType([Enum].Parse(GetType(DialogResult), res.Message), GetType(DialogResult)), DialogResult)
If (r = DialogResult.OK)
'Do stuffs on DialogResult.Yes
Else
'Do stuffs on DialogResult.No
End If
'Do other stuffs with database
End If
' Do other stuff
Return New Response(Response.ResponseType.Message, "You should do this!!OK?")
End Function
},
{ "CheckPoint3", Function (r) New Response(Response.ResponseType.Message, "Are you sure you wanna do this?") }
}
Public Sub New()
_checkpoints.ToList().ForEach(Sub(item) Queue.Enqueue(item.Key))
End Sub
Public Function Func(ByVal res As Response, ParamArray ByVal params As String()) As Response
Dim chk = Queue.Dequeue()
Return _checkpoints(chk).Invoke(res)
End Function
End Class
Basically this creates a Dictionary(Of String, Func(Of Response, Response)) that maps a String to a Func(Of Response, Response) that returns the Response that you want.
There would be a couple of variations on this that might suit you better, but perhaps you could let me know if this does the job or not for you and I could suggest other options if need be.

Related

Webhook receiver response

I have a Webhook receiver with aspnet webapi and use this packages
Microsoft.AspNet.WebHooks.Common
Microsoft.AspNet.WebHooks.Receivers
Microsoft.AspNet.WebHooks.Receivers.Generic
And this is my handler
Public Class GenericJsonWebHookHandler
Inherits WebHookHandler
Public Sub New()
Me.Receiver = GenericJsonWebHookReceiver.ReceiverName
End Sub
Public Overrides Function ExecuteAsync(ByVal receiver As String, ByVal context As WebHookHandlerContext) As Task
Dim data As JObject = context.GetDataOrDefault(Of JObject)()
If data.HasValues Then
'Do something
Return Task.FromResult(True)
Else
'Here I want to return a Bad Request or a different that 200 OK
End If
End Function
End Class
I want make some verification with the json I recieve and if fails I need to return a differente status that 200 OK, How can I do it?
It was very easy, if someone have the same problem here it is:
context.Response = context.Request.CreateResponse(HttpStatusCode.BadRequest, "Some message")
Return Task.FromResult(True)

How to call a public function from another form

Frm1 contains the code for validation of textbox:
Public Function AlphabeticalOnly(ByVal Str As String) As Boolean
Dim pattern As String = "^[a-zA-Z\s]+$"
Dim reg As New Regex(pattern)
If reg.IsMatch(Str) = False Then
MsgBox(Str & " is invalid! Please enter alphabetical characters only!", MsgBoxStyle.Critical, "Error")
End If
Return reg.IsMatch(Str)
End Function
Because there're quite an amount of validations, I don't want to repeat all the code again in the other forms.
Private Sub btnDone_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDone.Click
If AlphabeticalOnly(txtName.Text) = False Then
Exit Sub
End If
...
End Sub
I tried the code above in another form, but the error list shows that AlphabeticalOnly is not declared.
Is there anything that I need to add to my code?
First of all, don't put the function on a form. If it's common code shared by all forms, put it in its own class file.
Second, this common code shouldn't be prompting the user with a message box. This function should just perform the logic and nothing more. (This also makes the function easier to unit test.) Then allow the consuming code (in this case a form) to interact with the user. (Especially since the current implementation checks the match twice, which isn't necessary.)
Since this function doesn't rely on object state, you can make it Shared. Something like this:
Public Class CommonFunctions
Public Shared Function IsAlphabeticalOnly(ByVal Str As String) As Boolean
Dim pattern As String = "^[a-zA-Z\s]+$"
Dim reg As New Regex(pattern)
Return reg.IsMatch(Str)
End Function
End Class
Then on your forms you can invoke that function:
If CommonFunctions.IsAlphabeticalOnly(txtName.Text) = False Then
MsgBox(Str & " is invalid! Please enter alphabetical characters only!", MsgBoxStyle.Critical, "Error")
End If

Identify form the method is being called from

I have a global method (in a module) that multiple forms are calling. I can't figure out how to pass/identify the calling form so that it's controls are recognised when referenced in the method:
Public Sub SomeFunction(callingForm As Form)
callingForm.ErrorProvider.SetError(TextBox1, "Faux pas!")
End Sub
Public Sub SomeOtherFunction(callingForm As Form)
SomeFunction(Me)
End Sub
I the above example, I've attempted passing the form as a parameter but I'm being told:
ErrorProvider is not a member of System.Windows.Forms.Form.
This is pretty common, to want to treat all your forms the same, yet different. You will want to create another class that each form can implement. This allows you to do something specific... generically.
Create a class similar to this, call it and the function whatever you want:
Public Interface IErrorForm
Sub MyValidate
End Interface
Implement it in your forms:
Public Class Form1
Implements IErrorForm
Public Sub MyValidate() Implements IErrorForm.MyValidate
'...Your code here
'TextBox1.Text = "Faux pas!"
End Sub
Now, wherever you want to call the function, you could do something like this:
Public Sub SomeFunction(callingForm As Form)
If GetType(IErrorForm).IsAssignableFrom(xFrm.GetType) Then
CType(xFrm, IErrorForm).MyValidate()
End If
End Sub
Another approach with returning value from Validate function
As you mentioned in the comments
...one of the key purposes of my method is to avoid having to set the
errors outside of the method, to reduce duplicitous code
As I understand you want Validate function check, given control as parameter, for errors and show error message through ErrorProvider.
My suggestion will be shared Validate function return string value, which contains error message generated after validating control
If no error then function return Nothing or String.Empty
Public Function Validate(ctrl As Object, formats As String) As String
Dim errorMessage As String = Nothing 'or String.Empty
' Your validating code here
' If no error errorMessage = Nothing
Return errorMessage
End Function
Then in the form (possible in the Validated event handler)
'....
Me.MyErrorprovider.SetError(Me.MyTextBox,
MyGlobalFunctions.Validate(Me.MyTextBox, "formats"))

Normal v Async calls to a service

I have a wcf service reference configured on a client application. It provides a whole series of functions to both retrieve and send data to a web based database. As an example:
Function errorCodesGetAll(ByVal uname As String, ByVal pword As String) As String
and
Function errorCodesGetAllAsync(ByVal uname As String, ByVal pword As String) As System.Threading.Tasks.Task(Of String)
I know that I can populate a rich text box with the first function by using the following code:
RichTextBox1.Text = getCountryList
Private Function getCountryList() As String
Dim svc As New ServiceReference2.ERSAPIServiceClient
svc.Open
Dim str As String = svc.errorCodesGetAll(username, password)
svc.Close()
Return str
End Function
As WCF is still a very new area to me I'm wondering how I would populate the same rich text box but this time using the Async variant of the errorCodesGetAll function?
Thanks for any advice or general pointers as to how the async variants are best used.
Your service will expose a "completed" event as well as the async method, you need to handle that event.
Open the service, wire up the event and call the async method
Private Sub GetCodes()
Dim svc As New ServiceReference2.ERSAPIServiceClient
AddHandler ServiceReference2.errorCodesGetAllCompleted, AddressOf errorCodesGetAllCompletedhandler
ServiceReference2.errorCodesGetAllAsync()
ServiceReference2.Close()
End Sub
Handle the event. This will get called when the service returns. (normally I would not add the "handler" to the end of the method and name it exactly the same as the event, but I thought it might help distinguish the event and the handler)
Private Sub errorCodesGetAllCompletedHandler(ByVal sender As Object, ByVal e As ServiceReference2.errorCodesGetAllEventArgs)
If Not e.Result Is Nothing Then
textbox.text = e.Result
End If
End Sub
Calling the async version of the method is interesting when you're on the UI thread (e.g., on the Click event handler for a button in your form), since that won't "freeze" the UI by blocking the thread, waiting for the networking call to complete.
Since you get the *Async method which returns a Task<T> result, I assume you're using the .NET Framework 4.5. If this is the case, you can take advantage of the Async / Await keywords to call the asynchronous version in a fairly simple way, while still preventing the UI thread from being blocked.
Private Async Sub Button_Click(ByVal sender as Object, ByVal e as EventArgs)
RichTextBox1.Text = Await getCountryList
End Sub
Private Async Function getCountryList() As Task(Of String)
Dim svc As New ServiceReference2.ERSAPIServiceClient
svc.Open
Dim str As String = Await svc.errorCodesGetAllAsync(username, password)
svc.Close()
Return str
End Function

How to pass multiple parameters in thread in VB

I'm looking to pass two or more parameters to a thread in VB 2008.
The following method (modified) works fine without parameters, and my status bar gets updated very cool-y.
But I can't seem to make it work with one, two or more parameters.
This is the pseudo code of what I'm thinking should happen when the button is pressed:
Private Sub Btn_Click()
Dim evaluator As New Thread(AddressOf Me.testthread(goodList, 1))
evaluator.Start()
Exit Sub
This is the testthread method:
Private Sub testthread(ByRef goodList As List(Of OneItem), ByVal coolvalue As Integer)
StatusProgressBar.Maximum = 100000
While (coolvalue < 100000)
coolvalue = coolvalue + 1
StatusProgressBar.Value = coolvalue
lblPercent.Text = coolvalue & "%"
Me.StatusProgressBar.Refresh()
End While
End Sub
First of all: AddressOf just gets the delegate to a function - you cannot specify anything else (i.e. capture any variables).
Now, you can start up a thread in two possible ways.
Pass an Action in the constructor and just Start() the thread.
Pass a ParameterizedThreadStart and forward one extra object argument to the method pointed to when calling .Start(parameter)
I consider the latter option an anachronism from pre-generic, pre-lambda times - which have ended at the latest with VB10.
You could use that crude method and create a list or structure which you pass to your threading code as this single object parameter, but since we now have closures, you can just create the thread on an anonymous Sub that knows all necessary variables by itself (which is work performed for you by the compiler).
Soo ...
Dim Evaluator = New Thread(Sub() Me.TestThread(goodList, 1))
It's really just that ;)
Something like this (I'm not a VB programmer)
Public Class MyParameters
public Name As String
public Number As Integer
End Class
newThread as thread = new Thread( AddressOf DoWork)
Dim parameters As New MyParameters
parameters.Name = "Arne"
newThread.Start(parameters);
public shared sub DoWork(byval data as object)
{
dim parameters = CType(data, Parameters)
}
Dim evaluator As New Thread(Sub() Me.testthread(goodList, 1))
With evaluator
.IsBackground = True ' not necessary...
.Start()
End With
Well, the straightforward method is to create an appropriate class/structure which holds all your parameter values and pass that to the thread.
Another solution in VB10 is to use the fact that lambdas create a closure, which basically means the compiler doing the above automatically for you:
Dim evaluator As New Thread(Sub()
testthread(goodList, 1)
End Sub)
In addition to what Dario stated about the Delegates you could execute a delegate with several parameters:
Predefine your delegate:
Private Delegate Sub TestThreadDelegate(ByRef goodList As List(Of String), ByVal coolvalue As Integer)
Get a handle to the delegate, create parameters in an array, DynamicInvoke on the Delegate:
Dim tester As TestThreadDelegate = AddressOf Me.testthread
Dim params(1) As Object
params(0) = New List(Of String)
params(1) = 0
tester.DynamicInvoke(params)
Just create a class or structure that has two members, one List(Of OneItem) and the other Integer and send in an instance of that class.
Edit: Sorry, missed that you had problems with one parameter as well. Just look at Thread Constructor (ParameterizedThreadStart) and that page includes a simple sample.
Pass multiple parameter for VB.NET 3.5
Public Class MyWork
Public Structure thread_Data
Dim TCPIPAddr As String
Dim TCPIPPort As Integer
End Structure
Dim STthread_Data As thread_Data
STthread_Data.TCPIPAddr = "192.168.2.2"
STthread_Data.TCPIPPort = 80
Dim multiThread As Thread = New Thread(AddressOf testthread)
multiThread.SetApartmentState(ApartmentState.MTA)
multiThread.Start(STthread_Data)
Private Function testthread(ByVal STthread_Data As thread_Data)
Dim IPaddr as string = STthread_Data.TCPIPAddr
Dim IPport as integer = STthread_Data.TCPIPPort
'Your work'
End Function
End Class
I think this will help you...
Creating Threads and Passing Data at Start Time!
Imports System.Threading
' The ThreadWithState class contains the information needed for
' a task, and the method that executes the task.
Public Class ThreadWithState
' State information used in the task.
Private boilerplate As String
Private value As Integer
' The constructor obtains the state information.
Public Sub New(text As String, number As Integer)
boilerplate = text
value = number
End Sub
' The thread procedure performs the task, such as formatting
' and printing a document.
Public Sub ThreadProc()
Console.WriteLine(boilerplate, value)
End Sub
End Class
' Entry point for the example.
'
Public Class Example
Public Shared Sub Main()
' Supply the state information required by the task.
Dim tws As New ThreadWithState( _
"This report displays the number {0}.", 42)
' Create a thread to execute the task, and then
' start the thread.
Dim t As New Thread(New ThreadStart(AddressOf tws.ThreadProc))
t.Start()
Console.WriteLine("Main thread does some work, then waits.")
t.Join()
Console.WriteLine( _
"Independent task has completed main thread ends.")
End Sub
End Class
' The example displays the following output:
' Main thread does some work, then waits.
' This report displays the number 42.
' Independent task has completed; main thread ends.
With VB 14, you can do the following with Tuples:
Shared Sub _runner(data as (goodList As List(Of OneItem), coolvalue As Integer))
Console.WriteLine($"goodList: {data.goodList}")
Console.WriteLine($"coolvalue: {data.coolvalue}")
' do stuff...
End Sub
Dim thr As New Thread(AddressOf _runner)
thr.Start((myGoodList, cval))