In Excel writing a VSTO Plugin (using C#) I'm trying to retrieve a value from a SQL database using OLEDB. When I debug this function, it fails on the catch.
The message I get is:
must declare the scalar variable \"#uname\"
But I already did this when I bound the parameter. What am I doing wrong?
public static int getUserID(string username)
{
int result = 0;
string sql = #"select top 1 [ID] FROM " + tbl_users + " WHERE ( [UNAME]=#uname );";
Console.WriteLine("sql: " + sql);
using (OleDbConnection conn = new OleDbConnection(connStr))
{
try
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#uname", username);
result = (int)cmd.ExecuteScalar();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
result = -15;
}
finally
{
conn.Close();
conn.Dispose();
}
}
return result;
}
So I think I figured it out... This change works, but I don't really like it.
First I need to use OleDb parameter binding and not SqlParameter binding.
It also seems like OleDb does not like custom naming parameters like #uname and instead relies on the order of parameters (I don't like this).
So here's the fix in case anyone was interested:
public static int getUserID(string username)
{
int result = 0;
string sql = #"select top 1 [ID] FROM " + tbl_users + " WHERE ( [UNAME]=? );";
Console.WriteLine("sql: " + sql);
using (OleDbConnection conn = new OleDbConnection(connStr))
{
try
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
cmd.Parameters.Add("?", OleDbType.VarChar).Value = Convert.ToString(username);
result = (int)cmd.ExecuteScalar();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
result = -15;
}
finally
{
conn.Close();
conn.Dispose();
}
}
return result;
}
Related
I have code using SqlBulkCopy to clone a lot of tables, it used to work before, but very weird, recently got exception
Received an invalid column length from the bcp client for colid
I have search this exception and still not solve my problem.
sqlBulkCopy.WriteToServer(reader) will raise this exception if a table has two continous columns which are both of type Char(1) or nvarchar(nn), and both have NULL. Sometime, changing the SqlBulkCopy.BatchSize makes it work, but many times, it will not.
After simplify, I have test case as follow, and it is reproduceable on two servers:
Create a table like below: (tested on SQL Server 2012 SP 4 and SQL Server 2016 SP2)
IF OBJECT_ID('dbo.TestTable', 'U') IS NOT NULL
DROP TABLE dbo.TestTable;
CREATE TABLE [dbo].[TestTable]
(
[value2] [char](1) NULL,
[value1] [char](1) NULL
) ON [PRIMARY]
GO
DECLARE #i int = 0
WHILE #i < 262
BEGIN
SET #i = #i + 1
INSERT INTO [dbo].[TestTable]([value2], [value1])
VALUES (null, null)
END
C# console (.net framework 4.7) code as below
class Program
{
// [change here]
static string sourceConn = #"Server={YourServer};Database={YourDatabase};User ID={userYourName};Password={yourPassword};connect timeout=15";
static void Main(string[] args)
{
CopyTable(sourceConn, sourceConn, "TestTable", "testTableBAK");
Console.ReadLine();
}
static void CopyTable(string sConnSource, string sConnDest, string sTableSource, string sTableDest)
{
if (IsTableExist(sConnDest, sTableDest))
{
RunNonQuerySQL(sConnDest, "DROP TABLE " + sTableDest);
Console.WriteLine($"existing table {sTableDest} dropped");
}
CopySchema(sConnDest, sTableSource, sTableDest);
using (SqlConnection connSource = new SqlConnection(sConnSource))
{
connSource.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = connSource;
cmd.CommandText = "SELECT * FROM " + sTableSource;
// using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sConnDest, SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.KeepIdentity))
using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sConnDest))
{
// sqlBulkCopy.BatchSize = 1380; // this optional setting will work if set value smaller than 1397 for testTable on my new server (SQL server 13.0.5102.14)
// sqlBulkCopy.BatchSize = 261; // this optional setting will work if set value smaller than 261 for testTable on 2 older server (SQL server 11.0.7001)
sqlBulkCopy.DestinationTableName = sTableDest;
SqlDataReader reader = cmd.ExecuteReader();
try
{
// exception here
sqlBulkCopy.WriteToServer(reader);
Console.WriteLine("table copied");
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
sqlBulkCopy.Close();
}
}
}
static bool IsTableExist(string sConn, string sTableName)
{
bool result = false;
using (SqlConnection conn = new SqlConnection(sConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
string[] s = sTableName.Split('.');
if (s.Length > 1)
{
cmd.CommandText = "select count (*) as counter from information_schema.tables where table_name = '" + s[1] + "' and TABLE_SCHEMA='" + s[0] + "'";
}
else
{
cmd.CommandText = "select count (*) as counter from information_schema.tables where table_name = '" + sTableName + "'";
}
cmd.Connection = conn;
var count = Convert.ToInt32(cmd.ExecuteScalar());
result = count > 0;
}
return result;
}
static bool RunNonQuerySQL(string sConn, string sSQL)
{
bool result = false;
using (SqlConnection conn = new SqlConnection(sConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = sSQL;
cmd.Connection = conn;
var count = cmd.ExecuteNonQuery();
result = true;
}
return result;
}
static public bool CopySchema(string sConn, string sTableSource, string sTableDest)
{
return RunQuerySQL(sConn, "select * into " + sTableDest + " from " + sTableSource + " where 1=2");
}
static public bool RunQuerySQL(string sConn, string sSQL)
{
using (SqlConnection conn = new SqlConnection(sConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = sSQL;
cmd.Connection = conn;
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
return true;
}
else
{
return false;
}
}
}
}
I just experienced this error.
I tried to insert a string with a lenght of 5 into a table column with a definition of "varchar(4)".
The error message in my case was:
"Received an invalid column length from the bcp client for colid 2".
"Colid 2" refered to the second column of the row (DataRow) that was part of the DataTable which I used as parameter for the call to the SqlBulkCopy.WriteToServer(DataTable table) method.
The solution in my case was to add validation code that checks the lenght of the strings in my input data before trying to call SqlBulkCopy.WriteToServer().
I have searched for solutions but I can't find one; please help.
I have this code fragment in C#:
using (SqlCommand command = new SqlCommand())
{
command.Connection = openCon;
command.CommandType = CommandType.Text;
command.CommandText = "update logRecords set totalHours = DATEDIFF(HOUR,timeIn,timeOut)";
try
{
openCon.Open();
int recordsAffected = command.ExecuteNonQuery();
MessageBox.Show("Records affected: " + recordsAffected);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
openCon.Close();
GetLogData();
}
}
but it doesn't work. It didn't show the message box in the try block neither the one in the catch block.
Thanks for helping :D
You can access query with parameters, hope this help:
using(var openCon = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand())
{
command.Connection = openCon;
command.CommandType = CommandType.Text;
command.CommandText = "update logRecords set totalHours = DATEDIFF(HOUR,#timeIn,#timeOut)";
try
{
openCon.Open();
command.Parameters.AddWithValue("#timeIN", timeIn);
command.Parameters.AddWithValue("#timeOut", timeOut);
int recordsAffected = command.ExecuteNonQuery();
MessageBox.Show("Records affected: " + recordsAffected);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
GetLogData();
}
First of all I will create Stored procedure and I will update any of my table through procedure.
So, Basic SP will be like below, and I will run it in a Database(SQL).
CREATE PROCEDURE Set_LogRecords_TotalHours
-- Add the parameters for the stored procedure here
#timeIn datetime,
#timeOut datetime
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
UPDATE LogRecords
SET TotalHours = DATEDIFF(HOUR, #timeIn, #timeOut)
-- returns number of rows
SELECT ##ROWCOUNT
END
GO
Now, I will Move to code side.
I will Create a Generic method to call All of mine Stored procedures, see below.
public static DataSet GetRecordWithExtendedTimeOut(string SPName, params SqlParameter[] SqlPrms)
{
DataSet ds = new DataSet();
try
{
//here give reference of your connection and that is "openCon"
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(SPName, conn))
{
command.Parameters.AddRange(SqlPrms);
command.CommandTimeout = 0;
conn.Open();
command.CommandType = CommandType.StoredProcedure;
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(command))
{
try
{
dataAdapter.SelectCommand = command;
dataAdapter.Fill(ds);
}
catch (Exception ex)
{
return null;
}
finally
{
conn.Close();
}
}
}
}
}
catch (Exception ex)
{
//Handle Errror
}
return ds;
}
Now at last, call this method from wherever you need to access database.
Over here is the example of calling generic method.
//Add all the parameter that you want to pass to SP, here we have 2 and they are in DAteTime Formate
SqlParameter[] parameters =
{
new SqlParameter { ParameterName = "#timeIn", Value = ValueOf_TimeIN_DateTIME }
new SqlParameter { ParameterName = "#timeOut", Value = ValueOf_TimeOUT__DateTIME}
};
DataSet ds = DAL.GetRecordWithExtendedTimeOut("Set_LogRecords_TotalHours", parameters);
if (ds != null && ds.Tables.Count >= 1 && ds.Tables[0].Rows.Count >= 1)
{
//Debugg ds and you will see the number of records that affected in last update
}
I'm trying to write a method to check if a table exists. I am trying to use the using statement to keep it consistent through my database.
public void checkTableExists()
{
connectionString = #"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\keith_000\Documents\ZuriRubberDressDB.mdf;Integrated Security=True;Connect Timeout=30";
string tblnm = "BasicHours";
string str = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = " + tblnm + ");";
SqlDataReader myReader = null;
int count = 0;
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(str, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
MessageBox.Show("The count is " + count);
myReader = command.ExecuteReader();
while (myReader.Read())
{
count++;
}
myReader.Close();
MessageBox.Show("Table Exists!");
MessageBox.Show("The count is " + count);
}
connection.Close();
}
}
}
catch (SqlException ex)
{
MessageBox.Show("Sql issue");
}
catch (Exception ex)
{
MessageBox.Show("Major issue");
}
if (count > 0)
{
MessageBox.Show("Table exists");
}
else
{
MessageBox.Show("Table doesn't exists");
}
}
It throws an exception when it hits the try block. It catches in the SqlException block.
This is the point where I am learning to interact with databases again. The solution would be good, but more importantly, a brief explanation of where I have need to learn how to improve my code.
Thanks
Keith
Your code fails because when you write directly a query searching for a string value then this value should be enclosed in single quotes like 'BasicHours'.
However there are some improvements to apply to your actual code.
First, you can use a simplified sql command.
Second, you use parameters instead of string concatenations.
SqlCommand cmd = new SqlCommand(#"IF EXISTS(
SELECT 1 FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #table)
SELECT 1 ELSE SELECT 0", connection);
cmd.Parameters.Add("#table", SqlDbType.NVarChar).Value = tblName;
int exists = (int)cmd.ExecuteScalar();
if(exists == 1)
// Table exists
This command text don't require you to use an SqlDataReader because the query returns just one row with one 'column' and the value of this single cell is either 1 or 0.
A lot less overhead.
A part from this, it is of uttermost importance, that you never build sql queries concatenating strings. This method is well know to cause problems.
The worse is called SQL Injection and could potentially destroy your database or reveal confidential information to hackers. The minor ones are crashes when the string concatenated contains single quotes. Use always a parameterized query.
I have used the following code in my project and worked for me:
try
{
using (con = new SqlConnection(Constr);)
{
con.Open();
string query = $"IF EXISTS (SELECT * FROM sys.tables WHERE name = '{tableName}') SELECT 1 ELSE Select 0;"
Exists = int.Parse(sqlQuery.ExecuteScalar().ToString())==1;
con.Close();
}
}
catch{}
The problem could be the line: string tblnm = "BasicHours";. You table name is a string and should be apostrophed, try this: string tblnm = "'BasicHours'";
Inside catch blocks you could also log exception messages and details.
Thanks for the help on this issue. This is the solution that I'm implemnenting.
public void checkTableExists()
{
connectionString = #"
Data Source=(LocalDB)\MSSQLLocalDB;
AttachDbFilename=C:\Users\keith_000\Documents\ZuriRubberDressDB.mdf;
Integrated Security=True;
Connect Timeout=30";
string tblName = #"BasicHours";
string str = #"IF EXISTS(
SELECT 1 FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #table)
SELECT 1 ELSE SELECT 0";
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(str, connection))
{
connection.Open();
SqlCommand cmd = new SqlCommand(str, connection);
cmd.Parameters.Add("#table", SqlDbType.NVarChar).Value = tblName;
int exists = (int)cmd.ExecuteScalar();
if (exists == 1)
{
MessageBox.Show("Table exists");
}
else
{
MessageBox.Show("Table doesn't exists");
}
connection.Close();
}
}
}
catch (SqlException ex)
{
MessageBox.Show("Sql issue");
}
catch (Exception ex)
{
MessageBox.Show("Major issue");
}
}
I have a ASP.NET Application and a MySQL Database. I want write a Class to insert,delete and show the Data from the database. I have a Connection to the Database but I can't insert data in the database.
My Class insert method:
public string CreateEntry(string Connectionstring, string mitarbeiter)
{
connection = new MySqlConnection(Connectionstring);
try
{
var command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES ('tom')";
connection.Open();
return "Mitarbeiter wurde angelegt";
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
connection.Close();
}
}
The Connectionstring is correct. I don't get a error but there is no data in the database.
My tablename: tb_mitarbeiter
columns: ID and Vorname
You should simply execute the command
....
MySqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES ('tom')";
connection.Open();
command.ExecuteNonQuery();
....
I suppose that mitarbeiter is the real value that should be set in the database.
If this is the case remember to use parameters to insert/update your data
MySqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES (?name)";
command.Parameters.AddWithValue("?name", mitarbeiter);
connection.Open();
command.ExecuteNonQuery();
You forgot to execute the command by calling command.ExecuteNonQuery(). This is how I would typically do it:
public string CreateEntry(string connectionString, string valueToInsert)
{
var stringToReturn = "";
try
{
using(var connection = new MySqlConnection(connectionString))
{
//Open connection
connection.Open();
//Compose query using sql parameters
var sqlCommand = "INSERT INTO table_name (field_name) VALUES (#valueToInsert)";
//Create mysql command and pass sql query
using(var command = new MySqlCommand(sqlCommand, connection))
{
command.Parameters.AddWithValue("#valueToInsert", valueToInsert);
command.ExecuteNonQuery();
}
stringToReturn ="Success Message";
}
}
catch(exception ex)
{
stringToReturn = "Error Message: " + ex.Message;
}
return stringToReturn;
}
There are a few key things to keep in mind:
Wrap disposable objects with a using. In the case of
MySqlConnection, it will properly close and dispose the connection
when its out of scope.
Use SQL parameters when passing values inside
your query. This will avoid SQL injection and its much more easier
to maintain.
Personally, I like to have one exit point in a
function. In this example, the "stringToReturn" variable holds the
value to return once the function is done executing both
successfully or in case of a failure.
To do a Insert / Update / Delete u should add
connection.Open();
command.ExecuteNonQuery();
For select ()to show data from database use:
connection.Open();
command.ExecuteReader();
{
string MyConnection2 = "datasource=localhost;port=3306;username=root;password=1234";
string Query = "insert into DBname.TableName(id,Name,First_Name,Age,Address) values('" +this.IdTextBox.Text+ "','" +this.NameTextBox.Text+ "','" +this.FirstnameTextBox.Text+ "','" +this.AgeTextBox.Text+ "','" +this.AddressTextBox.Text+ "');";
MySqlConnection MyConn2 = new MySqlConnection(MyConnection2);
MySqlCommand MyCommand2 = new MySqlCommand(Query, MyConn2);
MySqlDataReader MyReader2;
MyConn2.Open();
MyReader2 = MyCommand2.ExecuteReader();
MessageBox.Show("Save Data");
while (MyReader2.Read())
{
}
MyConn2.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
You are not executing the command use SqlCommand.ExecuteNonQuery
try
{
MySqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES ('tom')";
connection.Open();
command.ExecuteNonQuery();
return "Mitarbeiter wurde angelegt";
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
connection.Close();
}
You missed to write this:-
....
connection.Open();
command.ExecuteNonQuery();
....
You can also used Sql parameter to prevent Sql Injection
try
{
MySqlCommand command = connection.CreateCommand();
command.CommandText = #"INSERT INTO `tb_mitarbeiter` (`Vorname`) VALUES (#tom)";
command.Parameters.AddWithValue("#tom", tom);
connection.Open();
command.ExecuteNonQuery();
return "Mitarbeiter wurde angelegt";
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
command.Dispose();
command.Close();
connection.Close();
}
when i specify values in my update query the query works fine and the database gets updated, but when i use parameters in my query the database does not update
here is the code i have written
try
{
OdbcConnection MyConnection = new OdbcConnection(ConfigurationManager.ConnectionStrings["myconn"].ConnectionString);
MyConnection.Open();
String MyString = "UPDATE orddetpabak SET jud1=#jud1,jud2=#jud2,jud3=#jud3,adv=#adv where fil_no=#fil_no AND orderdate=#orderdate";
OdbcCommand MyCmd = new OdbcCommand(MyString, MyConnection);
String j1=DropDownList4.SelectedValue;
String j2=DropDownList5.SelectedValue;
String j3=DropDownList6.SelectedValue;
String j4=TextBox4.Text;
String j5 = HiddenField1.Value;
String j6 = TextBox3.Text;
MyCmd.Parameters.AddWithValue("#jud1",j1);
MyCmd.Parameters.AddWithValue("#jud2",j2);
MyCmd.Parameters.AddWithValue("#jud3",j3);
MyCmd.Parameters.AddWithValue("#adv",j4);
MyCmd.Parameters.AddWithValue("#fil_no",j5);
MyCmd.Parameters.AddWithValue("#orderdate",j6);
Response.Write(DropDownList4.SelectedValue);
Response.Write(" " + DropDownList5.SelectedValue);
Response.Write(" " + DropDownList6.SelectedValue);
Response.Write(" " + TextBox4.Text);
Response.Write(" " + HiddenField1.Value);
Response.Write(" " + TextBox3.Text);
MyCmd.ExecuteNonQuery();
//MyConnection.Close();
}
catch(Exception epp)
{
Response.Write(epp);
}
Please Help
As far as I know you cannot use named parameters in MySQL. If you change your string to be
String MyString = "UPDATE orddetpabak SET jud1=?,jud2=?,jud3=?,adv=?
where fil_no=? AND orderdate=?";
and your parameters as:
MyCmd.Parameters.AddWithValue("",j1);
MyCmd.Parameters.AddWithValue("",j2);
MyCmd.Parameters.AddWithValue("",j3);
MyCmd.Parameters.AddWithValue("",j4);
MyCmd.Parameters.AddWithValue("",j5);
MyCmd.Parameters.AddWithValue("",j6);
Hope this helps.
It can be like the following: (I'm using the ADO.NET driver for MySQL version 6.3.7.0, latest one had some issues).
public bool UpdateCustomerIAR(IAR oIAR)
{
bool bRetVal = false;
try
{
MySqlConnection dbConnection = new MySqlConnection(APPSConn.ConnectionString);
MySqlCommand dbCommand = dbConnection.CreateCommand();
string szSQL = string.Empty;
szSQL = "UPDATE schema.table_name SET field_name_one=?field_name_one";
szSQL += " WHERE field_name_two=?field_name_two";
using (MySql.Data.MySqlClient.MySqlConnection conn = new
MySql.Data.MySqlClient.MySqlConnection(APPSConn.ConnectionString))
{
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = szSQL;
cmd.Parameters.AddWithValue("?field_name_one", oIAR.Title);
cmd.Parameters.AddWithValue("?field_name_two", oIAR.IARID.ToString());
conn.Open();
cmd.ExecuteNonQuery();
bRetVal = true;
}
return bRetVal;
}
catch (MySqlException ex)
{
ErrorHandler(ex.ToString());
return bRetVal;
}
catch (Exception ex)
{
ErrorHandler(ex.ToString());
return bRetVal;
}
}