I am coding a Sql-Server-ce application in C#.
Recently I have been converting my code to use using statements, as they are much cleaner. In my code I have a GetLastInsertedID function which is very simple - it returns the last inserted ID. The working version is as follows:
public static int GetLastInsertedID()
{
int key = 0;
try
{
SqlCeCommand cmd = new SqlCeCommand("SELECT CONVERT(int, ##IDENTITY)", DbConnection.ceConnection);
key = (int)cmd.ExecuteScalar();
}
catch (Exception ex)
{
MessageBox.Show("Could not get last inserted ID. " + ex.Message);
key = 0;
}
return key;
}
Below is the code that does NOT work once I wrap it in using statements:
public static int GetLastInsertedID()
{
int key = 0;
try
{
using (SqlCeConnection conn = new SqlCeConnection(DbConnection.compact))
{
conn.Open();
using (SqlCeCommand cmd = new SqlCeCommand("SELECT CONVERT(int, ##IDENTITY)", conn))
key = (int)cmd.ExecuteScalar();
}
}
catch (Exception ex)
{
MessageBox.Show("Could not get last inserted ID. " + ex.Message);
key = 0;
}
return key;
}
The error that I'm getting is specified cast is not valid. Although this error is usually self-explanatory, I cannot see why I would be getting it inside the second block of code, but not the first. This error occurs on the line key = (int)cmd.ExecuteScalar();.
What am I doing wrong with the second block of code?
From the ##IDENTITY documentation:
##IDENTITY and SCOPE_IDENTITY will return the last identity value generated in any table in the current session.
I think your change now starts a new session for each using statement. Therefore ##IDENTITY is null.
First of all, ##Identity will return any last generated ID from anywhere in SQL Server. Most probably you need to use SCOPE_IDENTITY() instead.
This shows your actual problem and design issue - you need to keep Connection and Command separate. Connection embeds transaction and though SCOPE_IDENTITY() will work until connection is closed; Command can be created, used and disposed.
So you need method which accept connection and use it to obtain identity - something like this (didn't check it but think idea should be clear):
public static int GetLastInsertedID(DbConnection connection)
{
try
{
string query = "SELECT CONVERT(int, SCOPE_IDENTITY())";
using (SqlCeCommand cmd = new SqlCeCommand(query, conn)) {
return (int)cmd.ExecuteScalar();
}
}
catch (Exception ex)
{
MessageBox.Show("Could not get last inserted ID. " + ex.Message);
return 0;
}
}
For working with connection you can create helper method like this:
public static SqlCeConnection OpenDefaultConnection()
{
SqlCeConnection conn = new SqlCeConnection(DbConnection.compact);
conn.Open();
return conn;
}
And use it like this:
...
using (SqlCeConnection conn = OpenDefaultConnection()) {
//... do smth
int id = GetLastInsertedID(conn);
//... do smth
}
...
in my opinion, the reason that it doesn't work is not related to the using statement.
If you use a static class to do the operation of connecting database, like DBHelper. The problem will be caused by that you close the connection of database before you execute the select ##identity and when you execute select ##identity, you open it again. This executing sequence will cause that the return result of select ##identity is NULL. That is, you can not use DBHelper.xxx() twice for getting the automated ID, because every time you call DBHelper.xxx(), the process of the opening database and the closing database will be done.
I have a solution but it maybe not the best one. Instead of using select ##identity, you can use select count(*) from xxx to get the same result.
Hope that it can help you
Related
I have a class function where I'm trying to read data from a database table using the SqlDataReader. When the SqlCmd.ExecuteReader() is called, I see(upon debugging) that the resultView of the SqlDataReader says "Enumeration yielded no results". However, the SqlDataReader.Read() still returns true and therefore enters the while() code block.
The query executes correctly on SQL, and even changing the query to get data from other tables give the same results. Please note that I have several other functions in a separate class executing the similar code to get data from these same database tables and they work without any issue.
Another observation is that after the ExecuteReader() is called, the VisibleFieldCount field of the SQLDataReader has the value = 11, which is equal to the total number of columns in the Customer table. This suggests that the reader is able read atleast some of the data from the database.
Any suggestions or help will be appreciated. Please let me know if any more information is required.
I have tried simplifying the function code as much as possible by removing any code logic other than the data retrieval part for the ease of debugging.
public bool MatchPassword(string username, string enteredPassword)
{
bool loginSuccessful = false;
string returnedpasswordbinary;
DatabaseConnection databaseConnectionObj = new DatabaseConnection();
databaseConnectionObj.CreateDBConnection(); //Sets the connection string and opens database connection
string query = "SELECT * FROM dbo.Customers WHERE CustomerID='ALFKI';";
SqlCommand sqlCommandObj = new SqlCommand();
sqlCommandObj.CommandText = query;
sqlCommandObj.Connection = databaseConnectionObj.SqlConnectionObj1;
try
{
SqlDataReader sqlDataReaderObj = sqlCommandObj.ExecuteReader();
if (sqlDataReaderObj.HasRows)
{
while (sqlDataReaderObj.Read())
{
returnedPasswordBinary = sqlDataReaderObj[0].ToString();
}
}
}
catch (Exception ex)
{
throw ex;
}
return loginSuccessful;
}
Can you please set commandtype
command.CommandType = CommandType.Text;
I am using Microsoft.Practices.EnterpriseLibrary.Data for database related activities in my application. I have written some code where I am executing a deletion as well as updating some records using two ExecuteNonQuery. I want to put these in a single transaction. How I can implement that using Microsoft.Practices.EnterpriseLibrary.Data?
What modification is required in the following code to use a transaction?
Code is as following:
int iUpdate = 0;
Database db = DatabaseFactory.CreateDatabase(dbRegion);
try
{
string sSQL = "DELETE FROM table1 WHERE Number = 1 ";
db.ExecuteNonQuery(CommandType.Text, sSQL);
string sqlCommand = "spInsertToTable";
DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand);
iUpdate = db.ExecuteNonQuery(dbCommand);
}
catch (Exception ex)
{
throw;
}
You can use TransactionScope object for this.
Ok so i have a webform and 5 FileUpload control..a user can upload any number of files from 1 to 5 but if anyone of these files does not get uploaded then I want to rollback everything...
For ex:
if user has selected 4 files and if something unexpected occurs at 4th then I want to remove or rollback all the previous 3 file uploads..
I tried this..
try
{
using (TransactionScope scope = new TransactionScope())
{
dboperation dbinsert=new dboperation();
if (file1.ContentLength > 0)
{
.......
.......
dbo.insert(bytes, lastid, file2.FileName);
}
if (file2.ContentLength > 0)
{
.......
.......
dbo.insert(bytes, lastid, file2.FileName);
}
if (file3.ContentLength > 0)
{
.......
.......
dbo.insert(bytes, lastid, file2.FileName);
}//till ...file5
scope.Complete();
}//end of transactionscope
}
catch { }
'dboperation' is a class in c# file and 'dbinsert' is a method which is executing an insert stored procedure. My guess is that I need to use Transaction Scope but I am not sure if I am correct and even if I am how am I supposed to achieve this?
You need to implement transaction. You should start the transaction before inserting first one and catch any errors that occur. in case of error you have to rollback the transaction. And if all goes well you can commit your transaction.
You should also move you connection outside the dboperation or make a method in dboperation that takes connection from outside and uses that
for this you need to use Transaction something like this. I give you example.
class WithTransaction
{
public WithTransaction()
{
string FirstQuery = "INSERT INTO Table1 VALUES('Vineeth',24)";
string SecondQuery = "INSERT INTO Table2 VALUES('HisAddress')";
int ErrorVar = 0;
using (SqlConnection con = new SqlConnection("your connection string"))
{
try
{
SqlCommand ObjCommand = new SqlCommand(FirstQuery, con);
SqlTransaction trans;
con.Open();
trans = con.BeginTransaction();
ObjCommand.Transaction = trans;
//Executing first query
//What ever operation on your database do here
ObjCommand.ExecuteNonQuery(); //Exected first query
ObjCommand.CommandText = SecondQuery;
ObjCommand.ExecuteNonQuery(); //Exected first query
//Everything gone fine. So commiting
ObjCommand.Transaction.Commit();
}
catch (Exception ex)
{
Console.WriteLine("Error but we are rollbacking");
ObjCommand.Transaction.Rollback();
}
con.Close();
}
}
}
Or you can use TransactionScope
check this Link
TransactionScope
I hope this will help you.
These are my functions to Update student record and Insert Student record in SQL batabase.
public void UpdateStudent(ref student stu, string rollno) //Update the values to corresponding roll number 'rollno'
{
try
{
connection.Open(); //I have already defined the connection and command
command = new SqlCommand("update student set FirstName='"+stu.Firstname+"',LastName='"+stu.Lastname+"',YearOfAdmission="+stu.Yearofadmission+",Branch='"+stu.Branch+"' where RollNo='"+rollno+"'", connection); //Yearofadmission is int
command.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
finally
{
if (connection != null)
connection.Close();
}
}
public void insertstudent(ref student s)
{
try
{
connection.Open();
command = new SqlCommand("insert into student values('"+s.Rollno+"','"+ s.Firstname+"','"+s.Lastname+"',"+s.Yearofadmission+",'"+s.Branch+"','"+s.Password+"')", connection);
command.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
finally
{
if (connection != null)
connection.Close();
}
}
My second function 'insertstudent' to insert value into SQL table is working correctly and inserting the values properly into database table. But the first function 'Update student' is not updating the values in the databse table. Its is not providing any error either.
So where i am wrong?
Thanks in advance!
Check to make sure that rollno passed to the update function is correct. If the command is not throwing error, most likely it's executing correctly and ending up updating nothing because no record hits the supplied rollno.
Put a break point at beginning of update function and check the supplied value of rollno.
Also, roll no. in your insert statement is a subset of 's' whereas in update, its provided separately, you may need to check if that's OK.
Correct way using parameters. You also need to Dispose of your connection and command objects.
using (connection = new SqlConnection("connectionstring"))
{
using (command = connection.CreateCommand())
{
command.CommandText = "update student set FirstName= #FirstName ,LastName= #LastName, YearOfAdmission= #YearOfAdmission, Branch=#Branch WHERE RollNo= #RollNo";
command.Parameters.AddWithValue("#FirstName", stu.FirstName);
command.Parameters.AddWithValue("#LastName", stu.LastName);
command.Parameters.AddWithValue("#YearOfAdmission", stu.YearOfAdmission);
command.Parameters.AddWithValue("#Branch", stu.Branch);
command.Parameters.AddWithValue("#RollNo", stu.RollNo);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
}
I am running unit tests and when I try to insert data in the database and getting it right after, I don't get anything (I have tried with DataAdapter and DataReader).
However when I put a 3 seconds sleep (even with 1 second it doesn't work...) between the insert and the select I get the result.
In SQL Server Profiler I can see the execution, the insert is well done and is completed about 10 miliseconds before the select begins.
I can't find out where this comes
The code looks like this :
Insert method
SqlCommand command = new SqlCommand(sqlTemplate);
command.Parameters.Add(Sql4oConstants.Sql4oIdParameterName, SqlDbType.UniqueIdentifier).Value = id;
command.Parameters.Add(Sql4oConstants.Sql4oTimestampParamterName, SqlDbType.DateTime).Value = DateTime.Now;
command.CommandTimeout = dataSourceDescription.CommandTimeout;
DatabaseManager.ExecuteNonQuery(dataSourceDescription.ConnectionString, command);
Get method
public static void Fill(string connectionString, DataTable table, SqlCommand command)
{
try
{
LogStorageWriter.WriteLogEntry(log, EStorageLevelLog.Debug, string.Format("Execute query: {0}", command.CommandText));
using (SqlConnection conn = new SqlConnection(connectionString))
{
command.Connection = conn;
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(table);
}
}
}
catch (InvalidOperationException e)
{
LogStorageWriter.WriteLogEntry(log, EStorageLevelLog.Error, string.Format("Exception : {0}", e.ToString()));
}
}
I solved it.
In fact, it was because my request used a CONTAINS. I then discovered that using a CONTAINS calls the SQL Server Indexer to fetch data. But the engine does not index data instantly.
That's why I had to wait 2 or 3 seconds to get my data back.