How do I modify this registration code so it checks if email entered already exists in the database?
I already have a query written for it, but I don't know how to implement it
[HttpPost("Register")]
public async Task<ActionResult<User>> Register(UserDto request, Guid guid)
{
string query = #"
insert into dbo.Users(UserID,Name,Email,PasswordHash,PasswordSalt)
values (#UserID,#Name,#Email,#PasswordHash,#PasswordSalt)
";
string emailValidationQuery = #"SELECT * FROM dbo.Users WHERE Email = #Email";
CreatePasswordHash(request.Password, out byte[] passwordHash, out byte[] passwordSalt);
string psw = PasswordHash(request.Password);
Guid guid1 = Guid.NewGuid();
guid = guid1;
user.UserID = guid;
user.Username = request.Username;
user.Email = request.Email;
user.PasswordHash = Encoding.UTF8.GetBytes(psw);
user.PasswordSalt = passwordSalt;
DataTable table = new DataTable();
string sqlDataSource = _configuration.GetConnectionString("ContactAppCon");
SqlDataReader myReader;
using (SqlConnection myCon = new SqlConnection(sqlDataSource))
{
myCon.Open();
using (SqlCommand myCommand = new SqlCommand(query, myCon))
{
myCommand.Parameters.AddWithValue("#UserID", Guid.NewGuid());
myCommand.Parameters.AddWithValue("#Name", request.Username);
myCommand.Parameters.AddWithValue("#Email", request.Email);
myCommand.Parameters.AddWithValue("#PasswordHash", psw);
myCommand.Parameters.AddWithValue("#PasswordSalt", passwordSalt);
myReader = myCommand.ExecuteReader();
table.Load(myReader);
myReader.Close();
myCon.Close();
}
}
return Ok(user);
}
Try something like below (open and dispose the connections properly)
string emailValidationQuery = #"SELECT Count(*) FROM dbo.Users WHERE Email = #Email";
....
using SqlCommand command = new SqlCommand(emailValidationQuery, myCon);
int count = (Int32) command.ExecuteScalar();
if(count > 0)
return new User() // or whatever you required
Why not a single statement:
INSER INTO dbo.Users (UserID, Name, Email, ...)
VALUES (#UserID, #Name, #Email, ...)
WHERE NOT EXISTS
(
SELECT 0
FROM dbo.Users WITH (SERIALIZABLE, UPDLOCK)
WHERE Email = #Email
);
If this affects 0 rows (you can check with ##ROWCOUNT), then the e-mail already existed (and maybe you should run an update instead in that case, but it's not clear from the question) or the insert failed for some other reason (which you can check with simple try/catch patterns).
And you can prevent race conditions and avoid costly exceptions by doing it a little differently:
BEGIN TRANSACTION;
IF NOT EXISTS
(
SELECT 0 FROM dbo.Users WITH (SERIALIZABLE, UPDLOCK)
WHERE Email = #Email
)
BEGIN
INSERT ...
END
ELSE
BEGIN
-- UPDATE? RAISERROR? Again requirements aren't clear.
END
COMMIT TRANSACTION;
Don't go for simple or expensive when correct and more efficient are better.
Related
I have a login form that I wanna select the userID (which is in the form of an int) from the database, and store it as a string.
string insertQuery =
"SELECT UserID FROM Customers WHERE Email = #Email AND Password = #Password";
SqlCommand com = new SqlCommand(insertQuery, conn);
com.Parameters.AddWithValue("#Email", tbEmail.Text);
com.Parameters.AddWithValue("#Password", tbPassword.Text);
string result = (string)com.ExecuteScalar();
But after I login, I get this error:
System.InvalidCastException: 'Unable to cast object of type
'System.Int32' to type 'System.String'.
What if the record doesn't exist (i.e. the cursor is empty)? Let's read and check if we have at least one record:
// Keep Sql being readable
string insertQuery =
#"SELECT UserID
FROM Customers
WHERE Email = #Email
AND Password = #Password";
// Do not forget to dispose IDisposable
using (SqlCommand com = new SqlCommand(insertQuery, conn)) {
com.Parameters.AddWithValue("#Email", tbEmail.Text);
com.Parameters.AddWithValue("#Password", tbPassword.Text);
using (var reader = com.ExecuteReader()) {
string result = reader.Read()
? Convert.ToString(reader[0]) // record exists
: null; // cursor is empty
//TODO: put relevant code which works with result here
}
}
You can try using like below
string result = Convert.ToString(com.ExecuteScalar());
ExecuteScalar returns Object type and you can convert it into which ever type you like
public override object ExecuteScalar ();
You can call it's ToString() method and it returns string form of it's value.
Try this,
string result = string.Empty;
SqlCommand com = new SqlCommand(..);
..
object executedResult = com.ExecuteScalar();
if(executedResult != null) {
result = executedResult.ToString();
}
Hope helps,
Probably the simplest solution would be (assuming query always return result):
string result = com.ExecuteScalar().ToString();
You can cast as nvarchar in your query also:
string insertQuery = "SELECT cast(UserID as nvarchar) FROM Customers WHERE Email = #Email AND Password = #Password";
Below are my findings
string userID;
using(SqlConnection conn = new SqlConnection(connectionString))
{
string insertQuery = "SELECT UserID FROM Customers WHERE Email = #Email AND
Password = #Password";
SqlCommand com = new SqlCommand(insertQuery, conn);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.AddWithValue("#Email", tbEmail.Text.ToString().Trim());
com.Parameters.AddWithValue("#Password", tbPassword.Text.ToString().Trim());
SqlDataReader reader = com.ExecuteReader();
while(reader.Read())
{
userID = reader["UserID"].ToString();
}
}
I am inserting a data row into my SQL Server database and then I want to query the data to get the unique identifier from the inserted row but my SqlDataReader is returning an empty dataset. I am thinking it maybe that the transaction hasn't been committed or something like that but I am not sure. I do not get an error.
Here is my code:
try
{
strQuery = "INSERT INTO clientnames VALUES(NEWID(),'" + txtACLastName.Text + "','" + txtACFirstName.Text + "'," + 1 + ")";
using (SqlCommand sqlInsertCmd = new SqlCommand(strQuery, sqlConn))
{
intQueryResult = sqlInsertCmd.ExecuteNonQuery();
if (intQueryResult == 0)
{
blnSuccess = false;
goto InsertClientNamesError;
}
else
{
blnSuccess = true;
}
sqlInsertCmd.Dispose();
}
if (blnSuccess)
{
strQuery = "select clientID from clientnames where firstname = '" + txtACFirstName.Text + "' and lastname = '" + txtACLastName.Text + "'";
using (SqlCommand sqlSelectCmd = new SqlCommand(strQuery, sqlConn))
{
SqlDataReader sqlDataRead = sqlSelectCmd.ExecuteReader();
while (sqlDataRead.Read())
{
strClientID = sqlDataRead.ToString();
}
sqlDataRead.Close();
sqlSelectCmd.Dispose();
}
}
}
catch (Exception exQuery)
{
System.Windows.MessageBox.Show("InsertClientNames: Error, " + exQuery.Message + ", has occurred.");
}
You are not getting the desired result because perhaps the SqlConnection is not opened explicitly (just a guess hard to tell without having full code). But this link shows you how to read from reader --> https://msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx
But I suggest that you Please do not do it this way. Reason is you are making Two round trips to the DB Server when only one would have done the job for you IF you were using stored procedures. Also you are exposing yourselves to SQL Injection attacks as you are not parameterizing your queries.
Stored procedure:
CREATE PROCEDURE dbo.INS_clientnames
(
#FirstName varchar(100),
#LastName varchar(100),
#NewID int out
)
AS
BEGIN
Declare #Err int
set #NewID = NewID() -- Get the New ID and store it in the variable ( #NewID ) that the SP will return back to the caller
INSERT INTO clientnames values (#NewID , #FirstName , #LastName)
SET #Err = ##ERROR
IF #Error <> 0 -- Check If there was an error
Begin
SET #NewID = -1 -- Indicates that there was an error. You could log this into a Log Table with further details like error id and name.
END
RETURN
END
C# code to execute the above stored procedure and get the NewID:
using(SqlConnection conn = new SqlConnection(connectionString ))
{
using(SqlCommand cmd = new SqlCommand("dbo.INS_clientnames", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// set up the parameters that the Stored Procedure expects
cmd.Parameters.Add("#FirstName", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#LastName" , SqlDbType.VarChar, 100);
cmd.Parameters.Add("#NewId" , SqlDbType.Int).Direction = ParameterDirection.Output;
// set parameter values that your code will send to the SP as parameter values
cmd.Parameters["#FirstName"].Value = txtACFirstName.Text ;
cmd.Parameters["#LastName"].Value = txtACLastName.Text ;
// open connection and execute stored procedure
conn.Open();
cmd.ExecuteNonQuery();
// read output value from #NewId
int NewID = Convert.ToInt32(cmd.Parameters["#NewId"].Value);
}
}
Add the following line to your stored procedure that inserts the record
SELECT SCOPE_IDENTITY()
This will return the last identity value inserted in that table.
And use cmd.ExecuteScalar() instead of ExecuteNonQuery()
ExecuteScalar() executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored. [More info][1]
I see two approaches to do this:
either you generate the new GUID on the client side in your C# code and pass it into the query - then you already know what the new id is going to be, so you don't need to do a second query to get it:
you create your GUID on the server side and return it to the caller using the OUTPUT clause in your query
Approach #1:
// define connection string and query
string connStr = "--your connection string here--";
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) VALUES(#ID, #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// create the GUID in C# - this is the ID - no need to go get it again - this *IS* the id
Guid id = Guid.NewGuid();
// set the parameters
cmd.Parameters.Add("#ID", SqlDbType.UniqueIdentifier).Value = id;
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Peter";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Miller";
// open connection, execute query, close connection
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
Approach #2:
// define connection string and query
string connStr = "--your connection string here--";
// query has an "OUTPUT" clause to return a newly inserted piece of data
// back to the caller, just as if a SELECT had been issued
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) OUTPUT Inserted.ClientID VALUES(NEWID(), #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// set the parameters - note: you do *NOT* send in a GUID value - the NEWID() will create one automatically, on the server
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Frank";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Brown";
// open connection
conn.Open();
// execute query and get back one row, one column - the value in the "OUTPUT" clause
object output = cmd.ExecuteScalar();
Guid newId;
if (Guid.TryParse(output.ToString(), out newId))
{
//
}
conn.Close();
}
i want to store the data in sqlserver and i'm able with this code.Now i want to check if table is exists than insert the data or create new table and insert the data..So i need help...thnku
SqlConnection con = new SqlConnection("Data Source=PRAWAT; Initial Catalog=StudentData ;Integrated security=true; ");
string query = "insert into NewValidStudentData(StudentId,Name,Marks) values (#StudentId,#Name,#Marks);";
SqlCommand cmd = new SqlCommand();
SqlDataAdapter da = new SqlDataAdapter();
da.InsertCommand = new SqlCommand(query, con);
con.Open();
da.InsertCommand.Parameters.Clear();
da.InsertCommand.Parameters.AddWithValue("#StudentId", System.Data.SqlDbType.NVarChar).Value = value[0];
da.InsertCommand.Parameters.AddWithValue("#Name", System.Data.SqlDbType.NVarChar).Value = value[1];
da.InsertCommand.Parameters.AddWithValue("#Marks", System.Data.SqlDbType.NVarChar).Value = value[2];
da.InsertCommand.ExecuteNonQuery();
con.Close();
You can edit your query to something like:
IF (EXISTS
(SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'YourSchemaName'// if you don't know the name, try 'dbo'
AND TABLE_NAME = 'NewValidStudentData'))
BEGIN
INSERT INTO NewValidStudentData(StudentId, Name, Marks)
VALUES (#StudentId, #Name, #Marks);";
END
Just wrap this query as a string and execute the same. This way, you can control the table's existence and insert the data within a single call to database server.
First check if your table exists with this snippet (also see this answer):
bool exists;
string tableName = "WhatEverItIs";
try {
// ANSI SQL way. Works in PostgreSQL, MSSQL, MySQL.
var cmd = new OdbcCommand(
"select case when exists((select * from information_schema.tables where table_name = '" + tableName + "')) then 1 else 0 end");
exists = (int)cmd.ExecuteScalar() == 1;
} catch {
try {
// Other RDBMS. Graceful degradation
exists = true;
var cmdOthers = new OdbcCommand("select 1 from " + tableName + " where 1 = 0");
cmdOthers.ExecuteNonQuery();
} catch {
exists = false;
}
}
Then if it wasn't exist:
if(!exists) {
var createTableSql = "CREATE TABLE WHAT_YOUR_TABLE_SCHEME_IS";
// execute a command with above createTableSql, to create your table
}
And then, do the rest of your code
StudentId as NVARCHAR? Does Student Id has characters in it?
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[NewValidStudentData]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[NewValidStudentData](
StudentId NVARCHAR(10),
Name NVARCHAR(100),
Marks NVARCHAR(3)
)
END
Note: I would suggest handle this in stored procedure instead of writing all this in c# code.
I've written this registration form which adds data to my SQL Server database. What I want is an exception when the user enters a username that is already in the database.
protected void Button1_Click(object sender, EventArgs e)
{
try
{
SqlConnection conn2 = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn2.Open();
string CheckUser = "select Username from UserData where Username like #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn2);
com2.Parameters.AddWithValue("#Username", "'%"+ UsernameTextBox.Text +"%'");
com2.ExecuteNonQuery();
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
conn2.Close();
if (IsMatch == 0)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn.Open();
string InsertQuery = "insert into UserData (Username, Email, Password, Country) values (#Username, #Email, #Password, #Country)";
SqlCommand com = new SqlCommand(InsertQuery, conn);
com.Parameters.AddWithValue("#Username", UsernameTextBox.Text);
com.Parameters.AddWithValue("#Email", EmailTextBox.Text);
com.Parameters.AddWithValue("#Password", PasswordTextBox.Text);
com.Parameters.AddWithValue("#Country", CountryDropDownList.SelectedItem.ToString());
com.ExecuteNonQuery();
Response.Redirect("Manager.aspx");
conn.Close();
}
else
{
Response.Write("User Already Exists!");
}
}
catch (Exception ex)
{
Response.Write(Convert.ToString(ex));
}
}
When I run it, I get an exception on the following line:
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
Blam's second solution works, but the IsMatch can be simplified a bit by casting to int instead of going to string and parsing.
This should also be handled at the database level. Set a primary key on your username column:
ALTER TABLE UserData ADD CONSTRAINT
PK_UserData PRIMARY KEY CLUSTERED (Username)
If you do it this way, then you don't even have to check for duplicates explicitly, you can just try to create the user and handle the exception if it fails:
try
{
using (var conn = new SqlConnection((ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString)))
{
conn.Open();
#if DOUBLE_CHECK
string CheckUser = "select count(*) from UserData where Username = #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn);
com2.Parameters.AddWithValue("#Username", UsernameTextBox.Text);
if ((int)com2.ExecuteScalar() > 0)
{
Response.Write("User already exists");
return;
}
#endif
string InsertQuerry = "insert into UserData (Username,Email,Password,Country) values (#Username,#Email,#Password,#Country)";
SqlCommand com = new SqlCommand(InsertQuerry, conn);
com.Parameters.AddWithValue("#Username", UsernameTextBox.Text);
com.Parameters.AddWithValue("#Email", EmailTextBox.Text);
com.Parameters.AddWithValue("#Password", PasswordTextBox.Text);
com.Parameters.AddWithValue("#Country", CountryDropDownList.SelectedItem.ToString());
com.ExecuteNonQuery();
Response.Redirect("Manager.aspx");
}
}
catch (SqlException se)
{
if (se.Errors.OfType<SqlError>().Any(e => e.Number == 2627))
{
Response.Write("User already exists");
}
else
{
Response.Write(se.ToString());
}
}
catch (Exception ex)
{
Response.Write(ex.ToString());
}
If you handle the exception this way, the #if DOUBLE_CHECK section is redundant and can be removed. An attempt to add duplicate name will cause a SQL error and exception, and this will detect and handle the "duplicate key" error.
Two unrelated notes on your code:
Response.Redirect() will abort the current thread and your conn.Close() will not be called. Use a using() to ensure it's called.
Storing a password in the database as plain text is a disaster waiting to happen. PLEASE take a look at Best way to store password in database for some ideas about how to do this correctly
That won't return an integer
string CheckUser = "select count(*) from UserData where Username like #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn2);
com2.Parameters.AddWithValue("#Username", "'%"+ UsernameTextBox.Text +"%'");
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
And you don't need to use two different connections.
Just use one and close it in a Finally.
string CheckUser = "select count(*) from UserData where Username = #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn2);
com2.Parameters.AddWithValue("#Username", UsernameTextBox.Text );
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
This returns 0 or 1. This should fix your issue. Looks like you need to return an int type. Or you could change it to bool if you want. Either way, this sql statement should help! :)
select
isnull(convert(bit,(select top 1 case
when username != '' then 1
else 0 end
from UserData
where username like #Username)),0)
I need to access a variable in my SQL database, along with a username which is already implemented properly. I query the database using this statement:
private const string _getUserByUsernameQuery = #"
SELECT
[User].[username]
FROM
[User] WITH (NOLOCK)
INNER JOIN [Company] WITH (NOLOCK)
ON [User].[companyId] = [Company].[id]
WHERE
[User].[username] = #username
AND [User].[password] = #password";
Then connect to the database and access the username:
using (SqlConnection connection = new SqlConnection(SQLConfiguration.ConnectionString))
{
SqlCommand command = new SqlCommand(_getUserByUsernameQuery, connection);
command.Parameters.AddWithValue("#username", username);
command.Parameters.AddWithValue("#password", password);
try
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Username = Convert.ToString(reader["username"]);
//CompanyId = Convert.ToString(reader["companyId"]);
lblUsername = Username;
//lblCompanyId = CompanyId;
Debug.WriteLine("Testing2::");
Debug.WriteLine(lblUsername);
//Debug.WriteLine(lblCompanyId);
}
}
}
catch (Exception)
{
if(connection.State == System.Data.ConnectionState.Open)
connection.Close();
}
}
In the if statement where I set reader["username"] equal to Username, I output Username using debug and the value is correct. What i have in comments relating to CompanyId is what I want to do, but was unable. Doing so doesn't cause errors, but it does ignore the entire statement (even the Username variable which works otherwise). based on my query string, how can I access the variable companyId?
In your _getUserByUsernameQuery you are only selecting the username field. Make sure that fields that you want to read from reader[...] are present in your select statement.
Looks like you need to add company id to your select statement to be able to retrieve it:
private const string _getUserByUsernameQuery = #"
SELECT
[User].[username], [User].[companyId]
FROM
[User] WITH (NOLOCK)
INNER JOIN [Company] WITH (NOLOCK)
ON [User].[companyId] = [Company].[id]
WHERE
[User].[username] = #username
AND [User].[password] = #password";