VB.NET How To Tell If New Class Instance Was Ended Early? - vb.net

I'm trying to detect if the Sub New() in my class has ended early due to missing fields. Below is a sample of my code:
Class Account
Public Sub New(ByVal Firstname As String, ByVal LastName As String, ByVal Username As String, ByVal Email As String, ByVal Password As String)
' Check For Blank Fields
If Firstname = "" Or LastName = "" Or Username = "" Or Email = "" Or Password = "" Then
MessageBox.Show("Please Enter All Information Requested")
Exit Sub
End If
' Set Public Variables Of Class
Firstname = Firstname
LastName = LastName
Username = Username
Email = Email
Password = Password
End Sub
Public Shared Sub OtherUse()
End Sub
End Class
' Create New Instance
Dim Process As New Account(txtFirstName.Text, txtLastName.Text, txtUsername.Text, txtEmail.Text, txtPassword.Text)
' HERE - How Can I Catch The Early Exit From The Instance Due To Potential Missing Fields?
' Use Instance For Other Use
Process.OtherUse()
How would I catch the Exit Sub from the class in the parent form to prevent the further processing of Process.OtherUse()?

You're approaching this problem the wrong way. Validate the input first, and then once the input is valid, create a new Account using New.
Another option would be to initialize data in New without checking if it's valid or not, then have an IsValid method in that class that you would call from another class to know whether or not the messagebox should be shown.
One way or another, the Account class shouldn't be responsible for a UI concern like showing a MessageBox on the screen. And the constructor should only be responsible for constructing the object, not validating the input, because you can't "abort" a constructor. You have a reference to a new object even though you call Exit Sub.

A fourth alternative, in addition to the three mentioned by Meta-Knight:
Have the constructor throw an exception when the parameters aren’t valid.
As an aside, your use of Or, while working, is a logical error: And and Or are bitwise arithmetic operations. You want a logical operation, OrElse (there’s also AndAlso). These may seem similar to Or and And and in this particular case, both happen to work. But they are actually quite different semantically, bitwise operations are just wrong here (it’s a pity that this code even compiles. The compiler shouldn’t allow this).

Related

I am using a method with a stored procedure, but it's always returning false

I am using bool method with Visual Studio 2015 and SQL Server 2005.
When I am passing correct details and click loginButton, the code always returns false from the stored procedure.
This is my stored procedure in SQL Server 2005:
ALTER PROCEDURE [dbo].[UserCheckLoginDetails]
(#IsLoginIdCorrect BIT OUTPUT,
#IsPasswordCorrect BIT OUTPUT,
#LoginID NVARCHAR(200),
#Password NVARCHAR(20)
)
AS
BEGIN
SET #IsLoginIdCorrect = 0
SET #IsPasswordCorrect = 0
IF EXISTS (SELECT * FROM UserInfo
WHERE loginid = #LoginID AND password = #Password)
BEGIN
SET #IsLoginIdCorrect = 1
SET #IsPasswordCorrect = 1
END
ELSE
IF EXISTS (SELECT * FROM UserInfo WHERE loginid = #LoginID)
BEGIN
SET #IsLoginIdCorrect = 1
END
END
This is my method returning True or False:
Private Sub GetIsUserLoginCorrect(IsLoginIdCorrect As Boolean, IsPasswordCorrect As Boolean)
Using Conn As New SqlConnection(_SqlCon)
Using cmd As New SqlCommand("UserCheckLoginDetails", Conn)
cmd.CommandType = CommandType.StoredProcedure
Conn.Open()
'OutPut Parameters
cmd.Parameters.Add("#IsLoginIdCorrect", SqlDbType.Bit).Direction = ParameterDirection.Output
cmd.Parameters.Add("#IsPasswordCorrect", SqlDbType.Bit).Direction = ParameterDirection.Output
'InPut Parameters
cmd.Parameters.AddWithValue("#LoginID", LoginIDTextBox.Text)
cmd.Parameters.AddWithValue("#Password", PasswordTextBox.Text)
cmd.ExecuteNonQuery()
' Assign Parameters
IsLoginIdCorrect = Convert.ToBoolean(cmd.Parameters("#IsLoginIdCorrect").Value)
IsPasswordCorrect = Convert.ToBoolean(cmd.Parameters("#IsPasswordCorrect").Value)
End Using
End Using
End Sub
This is the Login button click event handler, even when I provide the correct values, it still always returns false:
Private Sub LoginButton_Click(sender As Object, e As EventArgs) Handles LoginButton.Click
Try
Dim IsLoginIdCorrect, IsPasswordCorrect As Boolean
GetIsUserLoginCorrect(IsLoginIdCorrect, IsPasswordCorrect)
If IsLoginIdCorrect And IsPasswordCorrect = True Then
Me.Hide()
' User Information
DeshBoard.MainUserIdLabel.Text = Me.MainUserIdLabel.Text
DeshBoard.UserNameLabel.Text = Me.UserNameLabel.Text
DeshBoard.UserLoginIdLabel.Text = Me.UserLoginIdLabel.Text
DeshBoard.UserLevelLabel.Text = Me.UserLevelLabel.Text
'Organanization Information
DeshBoard.MainOrgIDLabel.Text = Me.MainOrgIDLabel.Text
DeshBoard.OrgNameLabel.Text = Me.OrgNameLabel.Text
DeshBoard.OrgTelLabel.Text = Me.OrgTelLabel.Text
DeshBoard.OrgEmailLabel.Text = Me.OrgEmailLabel.Text
DeshBoard.OrgAddressLabel.Text = Me.OrgAddressLabel.Text
DeshBoard.Show()
Else
If IsLoginIdCorrect = False Then
MessageBox.Show("Login ID is not correct...!!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
LoginIDTextBox.Clear()
PasswordTextBox.Clear()
LoginIDTextBox.Focus()
Else
MessageBox.Show("Password ID is not correct...!!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
PasswordTextBox.Clear()
PasswordTextBox.Focus()
End If
End If
Catch ex As ApplicationException
MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Thank you very much.
You need to add ByRef to both arguments in Sub GetIsUserLoginCorrect().
To demonstrate, try the following with and without ByRef.
Private Sub ChangeBoolean(ByRef TorF As Boolean)
TorF = True
End Sub
Private Sub OPCode2()
Dim TorF As Boolean
ChangeBoolean(TorF)
Debug.Print(TorF.ToString) ' Result False without ByRef in ChangeBoolean
'When ByRef is added result is True
End Sub
First off, a method can refer to either a sub or a function. A sub is a method that performs an action. A function is a method that calculates or retrieves one or more values.
A sub should not be called Getxxx, because its primary purpose should not be returning a value.
A function should be used to return values. Since you are trying to retrieve multiple values, if you were using 2017 I would suggest returning a named tuple with your two values, since you aren’t I would create an object that has the values and return that.
On a totally different note, you really can’t tell the difference between right user wrong password and wrong user right password and wrong user wrong password - so you shouldn’t tell someone you can. You just say login unsuccessful login, or invalid username/password combination.
There's a lot wrong with your code.
Firstly, why are you using so much of SQL code ? Correct me if i am wrong : You are trying to build a log in system. So much of SQL code or even the stored procedure is worthless here. You can simply write the SQL statements in your code by using the SqlCommand class. Even though you are using the ALTER PROCEDURE statement, i can surely say that things can be simplified here.
You are also using the Me keyword. It's not C# where the use of this(same as Me in VB.Net) becomes compulsory. I assume it's a Windows Forms Application and if that's so, then using Me keyword to access it's child elements wouldn't result in any different if it's not used at all.
The next worth mentioning issues is your Name Conventions. Most or should i say all of your variables have the same name. For example : IsLoginIdCorrect - used both as a parameter of a method and also a variable inside a method.
The next issues is in these two lies :
Dim IsLoginIdCorrect, IsPasswordCorrect As Boolean
GetIsUserLoginCorrect(IsLoginIdCorrect, IsPasswordCorrect)
You are passing the boolean variables before they have been assigned any value. You are lucky it's not C# or this wouldn't even compile. Passing the boolean variables without assigning any value will, by default, set their values to False. So, literally, you are always passing the same value in which case, the outcome will always be the same.
The next issue is in your If statement inside your LoginButton_Click method aka LoginButton's click event handler's method :
If IsLoginIdCorrect And IsPasswordCorrect = True Then
The if statements, if described in simple words, means : If IsLoginIdCorrect and IsPasswordCorrect are true, then proceed.... So, in this case, IsPasswordCorrect = True doesn't affect much. However, this is not the best practice too. You should better follow the following coding rule while using If statements:
If (IsLoginIdCorrect = True AndAlso IsPasswordCorrect = True) Then
AndAlso operators evaluates each side just like the And operator. The difference is that it would return False if the left side(IsLoginIdCorrect, in this case) returns False.
The next issues is the usage of ApplicationException. I don't understand why, in this era, you are using that class! This class is usually used to derive from and create exceptions. You can simply use Exception instead of ApplicationException.
Your Try-Catch block seems not useful as well. All of your codes inside the LoginButton_Click are in If conditions and they perform very basic operation. It is unlikely to ever throw any exception at all.
Your logics, for most part, are illogical(sorry to put it this way). In your GetIsUserLoginCorrect method, you are setting IsLoginIdCorrect and IsPasswordCorrect to either true or false but it wouldn't matter because they are parameters of the method itself. So even if you set their values, they will be reset when you call the method again. The reason why ByRef (according to Mary's answer) works is because ByRef, in short, means that you are pointing to original variable that you passed(not it's copy).
And finally, the solution you are looking for....
Even though i see you have marked Mary's answer as the answer, i would like to help you out a bit as-well.
Firstly, get rid of the stored procedure if possible and also if you are not using it anywhere else. I see you are using the If Exist condition inside your SQL queries. This is actually a nice move because according to performance reports, checking if data exists in a database/table using IF EXISTS yields the fastest output. So bravo for that one. But if you follow my advice and want to ditch the stored procedure, then you need to get rid of the IF EXISTS statement as well. Rather, you can simply use the SELECT statement itself, use the ExecuteScalar method of the SqlCommand class, convert it's value to Integer and check if the value of the Integer is 1 or not.
Example :
Dim cmd = New SqlCommand("SELECT COUNT(*) FROM UserInfo
WHERE loginid = #LoginID AND password = #Password")
Dim Exists = Convert.ToInt32(cmd.ExecuteScalar)
If Exists = 1 Then
''Code if data exists
End if
Note that i have used Convert.ToInt32 here. This will prevent null-reference exception as when ExecuteScalar returns Null, it will be converted to 0 integer value.
Also, why are you using GetIsUserLoginCorrect as method ? You can simply use it as a function and return required values. As you are returning multiple values, you can easily use the Tuple type as your function's type:
Private Function GetIsUserLoginCorrect(IsLoginIdCorrect As Boolean, IsPasswordCorrect As Boolean) As Tuple(of Boolean, Boolean)
....
....
return Tuple.Create(IsLoginIdCorrect, IsPasswordCorrect)
End Sub
Usage
Dim IsLoginCorrect = GetIsUserLoginCorrect(first_boolean_variable,second_boolean_variable).Item1
Dim IsPasswordCorrect = GetIsUserLoginCorrect(first_boolean_variable,second_boolean_variable).Item2
One last thing. As you are showing DeshBoard form after hiding the main form, make sure to call MainForm.Close on the Dashboard form's Closing/Closed event. This will ensure the application's exit(unless you have other plans for the main form, of course).
Hope this answer helps you.

"Invalid procedure call or argument" when calling a sub without parenthesing one parameter

I wrote a COM-Visible DLL with this piece of code (VB.NET)
' .NET Class with implementing an interface to make it visible
Public Class VisibleClass
Public Sub callableSub(ByVal names As ACustomCollection, _
Optional ByVal doSomething As Boolean = True) _
Implements IVisibleClass.visibleCallableSub
Me.doSub(names.process, doSomething)
End Sub
End Class
' Interface for COM-visibility
<InterfaceType(ComInterfaceType.InterfaceIsDual)> _
Public Interface IVisibleClass
Sub visibleCallableSub(ByVal names As ACustomCollection, _
Optional ByVal doSomething As Boolean = True)
End Interface
Here is also the ASP web page creating the object and invoking its methods :
' stuff.asp
Dim visibleObject
Dim aCustomCollection
Set visibleObject = getNewVisibleInstance (aCollection)
Set aCustomCollection = createEmptyCollection
aCustomCollection.add someStuffs
aCustomCollection.add otherStuffs
aCustomCollection.add lastStuffs
' These calls work:
visibleObject.visibleCallableSub(aCustomCollection)
visibleObject.visibleCallableSub(aCustomCollection), False
visibleObject.visibleCallableSub(aCustomCollection), True
Call document.fetchPropertyCollection ((properties), false)
' These ones don't:
visibleObject.visibleCallableSub someparams
visibleObject.visibleCallableSub someparams, False
visibleObject.visibleCallableSub someparams, True
Call document.fetchPropertyCollection (properties, false)
Non working calls produce the following error :
Invalid procedure call or argument
I don't understand why I have to put parenthesis. I know this tells the interpreter to make a copy before passing theme, but not why it's mandatory.
Note : It's the same issue than this one, i.e. about passing a reference where a copy is required. However, the question was told like it's due to "passing the return value of another function", which make it harder to reach through research.
If the called Sub/Function asks for a value (ByValue), you can't pass a reference ('pointer'). Putting the 'make a copy' parentheses around the argument makes sure the called Sub/Function gets a value.
To make your intention explicit, you should write:
visibleObject.visibleCallableSub (aCustomCollection), False
Cf. same error, parentheses, adventures.

Passing names of class fields to a private sub to update in VB.Net

I thought this might be simple to achieve, but I can't figure out how to make it work.
I have a series of fields within a class that I want to check the value of. So instead of writing the same if ... then ... else statement I thought passing this through a private method would be ideal.
Private Sub checkParameter(ByRef p_param As Object, ByRef p_private_field As Object, p_exception As String)
If Not p_param Then
Throw New Exception(p_exception)
Else
p_private_field = p_param
End If
End Sub
Of course the problem is that I want the p_private_field to be the actual private field I am passing in.
So if I called the method with the following code:
checkParameter(i_input_folder, "p_input_folder", "Input folder must be supplied")
Then the method would check that i_input_folder did have a value and if so then assign it to the private field p_input_folder and if not throw the exception with the message provided.
Any help would be greatly appreciated.
Cheers
Seems just like a little typo
checkParameter(i_input_folder, "p_input_folder", "Input folder must be supplied")
You pass in your second parameter as string (the name of the filed i guess), but you have to pass the object itself
checkParameter(i_input_folder, p_input_folder, "Input folder must be supplied")

how to change to public

Public Sub mainlogin_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles mainlogin.Authenticate
'Are the credentials valid?
If Membership.ValidateUser(mainlogin.UserName, mainlogin.Password) Then
'Has the password expired?
Dim usrInfo As MembershipUser = Membership.GetUser(mainlogin.UserName)
Dim daysSincePwdChange As Integer = Convert.ToInt32(DateTime.Now.Subtract(usrInfo.LastPasswordChangedDate).TotalDays)
If daysSincePwdChange > SecurityUtils.DefaultPasswordExpiryInDays Then
'Password expired, send user to change password
Response.Redirect("~/ChangePassword.aspx?UserName=" & Server.UrlEncode(mainlogin.UserName))
Else
e.Authenticated = True 'Credentials valid & password is current
End If
Else
e.Authenticated = False 'Invalid!
End If
end sub
but I got this error
Error 6 'System.Web.SecurityUtils' is not accessible in this context because it is 'Friend'.
You can't - as it says, System.Web.SecurityUtils is inaccessible. You can't make it public - it's not designed to be public. Change your approach so that you don't need it.
Of course, it could be that you really want to refer to a different type... were you reading this page by any chance? My guess is that here, SecurityUtils is a different custom class, not System.Web.SecurityUtils.
All it's doing is getting a default expiry for the site though - and that's probably something you want to implement in your own way anyway. It's likely to just be a constant, or possibly something from a settings file. It's not like there's going to be a lot of logic there.

Invalid cast exception

I have a simple application to store address details and edit them. I have been away from VB for a few years now and need to refreash my knowledge while working to a tight deadline. I have a general Sub responsible for displaying a form where user can add contact details (by pressing button add) and edit them (by pressing button edit). This sub is stored in a class Contact. The way it is supposed to work is that there is a list with all the contacts and when new contact is added a new entry is displayed. If user wants to edit given entry he or she selects it and presses edit button
Public Sub Display()
Dim C As New Contact
C.Cont = InputBox("Enter a title for this contact.")
C.Fname = frmAddCont.txtFName.Text
C.Surname = frmAddCont.txtSName.Text
C.Address = frmAddCont.txtAddress.Text
frmStart.lstContact.Items.Add(C.Cont.ToString)
End Sub
I call it from the form responsible for adding new contacts by
Dim C As New Contact
C.Display()
and it works just fine. However when I try to do something similar using the edit button I get errors - "Unable to cast object of type 'System.String' to type 'AddressBook.Contact'."
Dim C As Contact
If lstContact.SelectedItem IsNot Nothing Then
C = lstContact.SelectedItem()
C.Display()
End If
I think it may be something simple however I wasn't able to fix it and given short time I have I decided to ask for help here.
I have updated my class with advice from other members and here is the final version (there are some problems however). When I click on the edit button it displays only the input box for the title of the contact and actually adds another entry in the list with previous data for first name, second name etc.
Public Class Contact
Public Contact As String
Public Fname As String
Public Surname As String
Public Address As String
Private myCont As String
Public Property Cont()
Get
Return myCont
End Get
Set(ByVal value)
myCont = Value
End Set
End Property
Public Overrides Function ToString() As String
Return Me.Cont
End Function
Sub NewContact()
FName = frmAddCont.txtFName.ToString
frmStart.lstContact.Items.Add(FName)
frmAddCont.Hide()
End Sub
Public Sub Display()
Dim C As New Contact
C.Cont = InputBox("Enter a title for this contact.")
C.Fname = frmAddCont.txtFName.Text
C.Surname = frmAddCont.txtSName.Text
C.Address = frmAddCont.txtAddress.Text
'frmStart.lstContact.Items.Add(C.Cont.ToString)
frmStart.lstContact.Items.Add(C)
End Sub
End Class
You're doing
frmStart.lstContact.Items.Add(C.Cont.ToString)
That is, you are adding strings to lstContact. Of course lstContact.SelectedItem() is a string!
Since you didn't post all of your code, I'll give you my best guess which is:
lstContact seems like it is bound to an IEnumerable of strings instead of IEnumerable of Contacts.
Therefore, when you get lstContact.SelectedItem() it is returning a string instead of a Contact (and throwing an error when trying to cast string as Contact).
It'd be more helpful if you could post the code bit that binds lstContact to a data source.