I'm trying to make validation for a registration form to check whether a username/name + surname already exists.
This is what I tried:
Dim conn As New SqlConnection("Data Source=BRIAN-PC\SQLEXPRESS;Initial Catalog=master_db;Integrated Security=True")
Dim registerSQL As SqlCommand
Dim checkCredentialsSQL As SqlCommand
Dim sqlComm As String
Dim sqlCommName As String
Dim sqlCommUsername As String
sqlComm = "INSERT INTO users(Username, Password, Name, Surname, Address1, Address2, " +
"City, Country, date_of_birth, age, Occupation, department, work_location, " +
"project_manager,team_leader, team_leader_id, project_manager_id, " +
"date_registration, contract_type, contract_duration) " +
"VALUES(@p1, @p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p10,@p11,@p12,@p13,@p14,@p15," +
"@p16,@p17,@p18,@p19,@p20)"
sqlCommName = "SELECT name, surname FROM users WHERE name='" + txtName.Text + "' and surname='" + txtSurname.Text + "'"
sqlCommUsername = "SELECT username FROM users WHERE username='" + txtUsername.Text + "'"
conn.Open()
checkCredentialsSQL = New SqlCommand(sqlCommName, conn)
If checkCredentialsSQL.ExecuteScalar IsNot Nothing Then
lblName.Text = txtName.Text + " " + txtSurname.Text + "is already registered."
lblName.Visible = True
Else
checkCredentialsSQL = New SqlCommand(sqlCommUsername, conn)
If checkCredentialsSQL.ExecuteScalar IsNot Nothing Then
lblUsername.Text = "'" + txtUsername.Text + "' is already taken."
Else
registerSQL = New SqlCommand(sqlComm, conn)
registerSQL.Parameters.AddWithValue("@p1", Username)
registerSQL.Parameters.AddWithValue("@p2", Password)
registerSQL.Parameters.AddWithValue("@p3", Name)
registerSQL.Parameters.AddWithValue("@p4", Surname)
registerSQL.Parameters.AddWithValue("@p5", Address1)
registerSQL.Parameters.AddWithValue("@p6", Address2)
registerSQL.Parameters.AddWithValue("@p7", City)
registerSQL.Parameters.AddWithValue("@p8", Country)
registerSQL.Parameters.AddWithValue("@p9", DOB)
registerSQL.Parameters.AddWithValue("@p10", Age)
registerSQL.Parameters.AddWithValue("@p11", Occupation)
registerSQL.Parameters.AddWithValue("@p12", Department)
registerSQL.Parameters.AddWithValue("@p13", WorkLocation)
registerSQL.Parameters.AddWithValue("@p14", ProjectManager)
registerSQL.Parameters.AddWithValue("@p15", TeamLeader)
registerSQL.Parameters.AddWithValue("@p16", TeamLeaderID)
registerSQL.Parameters.AddWithValue("@p17", ProjectManagerID)
registerSQL.Parameters.AddWithValue("@p18", RegistrationDate)
registerSQL.Parameters.AddWithValue("@p19", ContractType)
registerSQL.Parameters.AddWithValue("@p20", ContractDuration)
registerSQL.ExecuteNonQuery()
End If
End If
conn.Close()
Is it viable/safe/recommended to do it as that?
Here is a simple stored procedure that checks the two conditions you want to check (more on that below):
CREATE PROCEDURE dbo.CreateUser
@username NVARCHAR(255),
@name NVARCHAR(64),
@surname NVARCHAR(64)
/* ... other params ... */
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM dbo.users WHERE username = @username)
BEGIN
RAISERROR('Username %s is already registered.', 11, 1, @username);
RETURN -1;
END
IF EXISTS (SELECT 1 FROM dbo.users WHERE name = @name AND surname = @surname)
BEGIN
RAISERROR('Name %s %s is already registered.', 11, 1, @name, @surname);
RETURN -2;
END
BEGIN TRY
INSERT dbo.Users(username, name, surname, ...other columns...)
SELECT @username, @name, @suername, ...other params...;
END TRY
BEGIN CATCH
DECLARE @msg NVARCHAR(255) = ERROR_MESSAGE();
RAISERROR(@msg, 11, 1);
RETURN -3;
END CATCH
END
Now your VB.Net code is much simpler:
Dim conn As New SqlConnection("...conn string...")
conn.Open()
Dim sql As New SqlCommand("dbo.CreateUser", conn)
sql.CommandType = CommandType.StoredProcedure
rv = sql.Parameters.Add("@rv", SqlDbType.Int)
rv.Direction = ParameterDirection.ReturnValue
sql.Parameters.AddWithValue("@username", Username)
sql.Parameters.AddWithValue("@name", Name)
sql.Parameters.AddWithValue("@surname", Surname)
... other params ...
sql.ExecuteNonQuery()
Debug.Print "Return value: " & rv.ToString
...act accordingly depending on error raised and/or return value
...maybe you want TRY/CATCH here?
conn.Close()
While attempting to insert (with a WHERE
clause, or just letting constraints raise errors) might be a technically possible approach, I don't think it's a good idea. The first because your code can't tell you which condition caused the problem, and the second because it is very expensive to let SQL Server raise the exception for you:
http://www.sqlperformance.com/2012/08/t-sql-queries/error-handling
That doesn't mean you shouldn't have the underlying constraints, of course. They should be there for cases where users don't go through your stored procedure, application tier, etc.
Oh and I agree with others. Do you really think only one John Smith will ever register? Having the same name as someone else is not a very valid reason to prevent someone from registering IMHO.
PS I apologize if my VB.Net code is far from perfect. I don't write in VB and some of that is an educated guess...
The sqlCommName
command isn't secured against SQL injections. if txtName.Text
contains existing_username AND 1 = 0
the user could be registred twice.
Although I disagree with the criteria that you're using (because name+surname is almost certainly not a unique identifier for a person), the general form should be an attempted INSERT
:
INSERT INTO TabA (Val1,Val2,Val3)
SELECT @Val1,@Val2,@Val3
WHERE NOT EXISTS (SELECT * FROM TabA where Val1 = @Val1 or (Val2 = @Val2 and Val3 = @Val3)
Otherwise, you're still exposing yourself to race conditions.
And, depending on what concurrency settings you're running under, you may still need to deal with constraint violations (I assume you are enforcing any uniqueness constraints that you do have down at the database level also)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.