How to parameterise table name in ODBC query - c#

I have an ODBC connection to a database and I would like the user to be able to view data within any table. As this is an ASP.net application I cannot trust that the table name sent doesn't also contain nasties. I have tried using a parameterised query but I always get an error saying that I "Must declare the table variable" - this appears to be an issue because it is the table name
string sql = "SELECT TOP 10 * FROM ? ";
OdbcCommand command = new OdbcCommand(sql, dbConnection);
command.Parameters.Add(new OdbcParameter("#table", tableName));
OdbcDataAdapter adapter = new OdbcDataAdapter();
adapter.SelectCommand = command;
adapter.Fill(tableData);
What is the best method to achieve this in a secure way?

Use a stored procedure, it's the safest way.
Some hints:
You probably may also use the System.Data.SqlClient namespace objects
Enclose your connection, command and adapter objects initializations in using statements
Here's a simple example:
string sqlStoredProcedure = "SelectFromTable";
using (OdbcConnection dbConnection = new OdbcConnection(dbConnectionString))
{
dbConnection.Open();
using (OdbcCommand command = new OdbcCommand(sqlStoredProcedure, dbConnection))
{
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new OdbcParameter("#table", tableName));
using (OdbcDataAdapter adapter = new OdbcDataAdapter(command))
{
adapter.SelectCommand = command;
adapter.Fill(tableData);
}
}
}
Another way to go would be to retrieve all table names and validate the tableName string variable as an entry in the list, maybe using:
DataTable tables = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Tables);
Here's a simple implementation based on your scenario:
string sql = "SELECT TOP 10 * FROM {0}";
using (OdbcConnection dbConnection = new OdbcConnection(dbConnectionString))
{
dbConnection.Open();
DataTable tables = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Tables);
var matches = tables.Select(String.Format("TABLE_NAME = '{0}'", tableName));
//check if table exists
if (matches.Count() > 0)
{
using (OdbcCommand command = new OdbcCommand(String.Format(sql, tableName), dbConnection))
{
using (OdbcDataAdapter adapter = new OdbcDataAdapter(command))
{
adapter.SelectCommand = command;
adapter.Fill(tableData);
}
}
}
else
{
//handle invalid value
}
}

Related

SqlCommand with more than one result table

I'm working on an ASP.NET Web Application with Visual Studio 2010. My target framework is ".NET Framework 4" and I'm sending queries to a SQL Server 2008 database which version is "Microsoft SQL Server 2008 R2 (SP2)".
I'm connecting using the following connection string "Data Source=XXXX;Initial Catalog=XXXX;Integrated Security=False;User Id=XXXX;Password= XXXX;MultipleActiveResultSets=True" and sending queries with the code below:
public static List<DataTable> getData(String query)
{
var results = new List<DataTable>();
try
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
command.CommandTimeout = 0;
using (SqlDataReader reader = command.executeReader())
{
do
{
while (reader.Read()) ;
var dataTable = new DataTable();
dataTable.Load(reader);
results.Add(dataTable);
} while (reader.NextResult());
}
connection.Close();
}
}
}
}
The query I'm sending is an Stored Procedure which returns two tables, at first it had a loop which calls another Stored Procedure depending on some internal condition, creation and insertion on a tempdb..#table and two SELECT statements.
But now it only contains:
SELECT 1,2,3,4,5
SELECT 6,7,8,9,0
I don't know why but the reader.NextResult() is always false so I never get the second table result.
Does anyone know what I'm doing wrong? What should I do to receive and read the two results from the query?
if this is using a stored proc you need something like this: notice the command type
using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(myConnString))
{
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand())
{
cmd.CommandText = "myMultipleTablesSP";
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter(cmd);
DataSet ds = new DataSet();
adapter.Fill(ds);
conn.Close();
}
}
if for example you return 2 tables in your SP, like:
SELECT * FROM [TableA];
SELECT * FROM [TableB];
you would access this tables as:
DataTable tableA = ds.Tables[0];
DataTable tableB = ds.Tables[1];
OK, I have run some test and find out the problem is from dataTable.Load(reader); somehow and I don't know why and what is exactly happening behind that method.
but if you use
do {
while(reader.Read()) {
...
}
} while (reader.NextResult());
everything works as expected.

Why does my MySQL connection not work in visual studio code C#?

i am making a program in visual studio code with C#, it is a paid program so it needs an hwid system. Basically i want it to check if your computer HWID exists in the HWID table in my users database. But it says it can't connect to the database. Can you help me? This is my code.`
string connectionString = "Server=SomeServer;Database=i got you this is notthe real database;User ID=same;Password=same for password;";
MySqlConnection mydbCon = new MySqlConnection(connectionString);
mydbCon.Open();
MySqlCommand command = mydbCon.CreateCommand();
command.CommandText = "SELECT * FROM yourTable WHERE hwid = GetHDDSerial";
IDataReader reader = command.ExecuteReader();
`
It could be that the connection string isn't formatted the way the MySQL connector wants it. The MySQL documentation shows "uid" instead of User, and "pwd" instead of Password. https://dev.mysql.com/doc/connector-net/en/connector-net-programming-connecting-connection-string.html
This should do what you need:
string connectionString = "Server=SomeServer;Database=i got you this is notthe real database;User ID=same;Password=same for password;";
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
using (MySqlCommand command = new MySqlCommand())
{
string sql = "SELECT * FROM yourTable WHERE hwid = #val1";
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = sql;
command.Parameters.AddWithValue("#val1", "GetHDDSerial");
connection.Open();
using (MySqlDataAdapter adapter = new MySqlDataAdapter())
{
using (DataSet ds = new DataSet())
{
adapter.SelectCommand = command;
adapter.Fill(ds);
if (ds.Tables.Count > 0)
{
DataTable dt = ds.Tables[0];
foreach (DataRow row in dt.Rows)
{
// Do something here. You can access the data like this:
// row["Id"] or whatever your field names are.
// int id = (int) row["Id"];
// Of course, I don't know your field names, so you'll have to complete this.
}
}
}
}
}
}

List.Count always returns 0

I'm creating a public static List<> of variables (from MySQL query) but the List's Count always returns 0! I've tried everything so far but no success. Here is my code:
public static List<string> GetHashedVars(string ID)
{
List<string> lst = new List<string>();
MySqlConnection conn;
MySqlCommand cmd;
MySqlDataReader reader;
string connString, queryStr = "";
connString = ConfigurationManager.ConnectionStrings["GameserverConnString"].ToString();
using (conn = new MySqlConnection(connString))
{
//The query for execution
queryStr = "SELECT * FROM account.account WHERE id_hashed=?hid LIMIT 1";
//Open the connection to the database
conn.Open();
//execute command
cmd = new MySqlCommand(queryStr, conn);
cmd.Parameters.AddWithValue("?hid", ID);
using (reader = cmd.ExecuteReader())
{
//Loop through results
while (reader.Read())
{
lst.Add(reader.GetString(reader.GetOrdinal("id_hashed")));
lst.Add(reader.GetString(reader.GetOrdinal("login_hashed")));
lst.Add(reader.GetString(reader.GetOrdinal("webcode_hashed")));
lst.Add(Encryption.CipherEncryption(reader.GetString(reader.GetOrdinal("status")).Trim()));
}
}
reader.Close();
conn.Close();
}
queryStr = "";
reader = null;
cmd = null;
conn = null;
connString = "";
return lst;
}
Looks like you are using named parameters but do not set your parameters correctly.
? is used for non named parameters and # are used for named parameters. Since the database connector you are using does not support non named parameters you have to follow the convention using the # in your query. This will set the parameters by name rather than by index.
queryStr = "SELECT * FROM account.account WHERE id_hashed=#hid LIMIT 1";
...
cmd.Parameters.AddWithValue("#hid", ID);
Explanation from msdn.
The Microsoft .NET Framework Data Provider for SQL Server does not
support the question mark (?) placeholder for passing parameters to a
SQL Statement or a stored procedure called by a command of
CommandType.Text. In this case, named parameters must be used.

How to actually execute a command?

I'm playing around making a POC and I've created the following call.
public string DoStuff()
{
try
{
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
SqlConnection connection = new SqlConnection("Server...");
string command = "insert into Records values (...)";
adapter.InsertCommand = new SqlCommand(command, connection);
}
}
catch (Exception exception)
{
return exception.Message + " " + exception.InnerException;
}
return "WeeHee!";
}
The text I'm seeing returned is the happy one, so I conclude there's no exceptions. Hence, I conclude that the call to the DB is performed as supposed to. However, there's no new lines in the DB being created.
I'm using the same connection string as I have in my config file and the command in pasted in from SQL Manager, where it works.
So my suspicion was that although I create an insert command, I never actually execute it but according to MSDN that's how it's supposed to work.
What stupid thing do I miss here?
You are missing connection.Open(); and adapter.InsertCommand.ExecuteNonQuery();
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
SqlConnection connection = new SqlConnection("Server...");
connection.Open();
string command = "insert into Records values (...)";
adapter.InsertCommand = new SqlCommand(command, connection);
adapter.InsertCommand.ExecuteNonQuery();
}
You should use ExecuteNonQuery instead. Using an SqlDataAdapter for an INSERT query does not make sense.
Also you should Open your connection just before you execute it.
You can:
using(SqlConnection connection = new SqlConnection("Server..."))
{
SqlCommand command = connection.CreateCommand();
command.CommandText = "insert into Records values (...)";
connection.Open();
int craeted = command.ExecuteNonQuery();
}
The example you linked to returned a SQLAdapter for later use.
You don't need one at all:
using (SqlConnection connection = new SqlConnection("Server..."))
{
string command = "insert into Records values (...)";
connection.Open();
var command = new SqlCommand(command, connection);
command.ExecuteNonQuery();
}
Note that there are other execution methods, depending on expected return values and whether you want asynchronous operation: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand(v=vs.110).aspx

How do I retrieve the values of a single column from a database table?

I have a table where I am trying to capture all the values in one column across many rows with a matching identifier. For example my query is similar to:
SELECT
prevHours
FROM
submissions
WHERE
projectCat='Capacity'
I am then trying to pass all of the values in prevHours into a single array that I can perform operations on.
The simplest way to work with a list of values from the database is to use a List<T>; the code is similar to what you have now, see below.
The code is quite simple:
var container = new List<int>();
var dbConnection = "...";
var query = "SELECT [prevHours] FROM [submissions] WHERE [projCat] = #Value";
using(var connection = new SqlConnection(dbConnection))
using(var command = new SqlCommand(query, connection))
{
connection.Open();
command.Parameters.Add("#Value", SqlDbType.VarChar, max).Value = "Capacity";
using(var reader = command.ExecuteReader())
while(reader.Read())
{
if(reader["prevHours"] != DBNull.Value)
container.Add(Convert.ToInt32(reader["prevHours"]));
}
}
Additionally, if you are not using the list for further processing, you could use ExecuteScalar and a query of the form
SELECT SUM(prevHours) FROM submissions WHERE projCat = #Value
for similar results using command.ExecuteScalar()
I write a function to return a database table:
public static DataTable ExecuteDataTable(SqlConnection conn, string cmdText,
params SqlParameter[] parameters)
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = cmdText;
cmd.Parameters.AddRange(parameters);
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}`

Categories

Resources