I want to create a general function in c# for sql requests at my oracle db.
I want to save all columns to strings. I tried to use the reader.GetString()method but an exception is thrown, saying it is unconvertable...
How can I make a function which does that ?
string [] request (OracleConnection con, String Table, String condition, String columns, String sort){
//The magic code
return "A string-array with all information";
}
Can you use the Item property on DBReader? Also, this method is a serious security risk -- using a condition string instead of params invites sql injection.
public string[] Request(IDBConnection conn, string table, string condition, string columns, string sort)
{
List<string> output = new List<string>();
string[] cols = columns.Split(',');
string sql = string.Format("select * from {0} etc", table);
using (IDBCommand cmd = new OracleCommand(conn, sql))
{
conn.Open();
IDBReader reader = cmd.ExecuteReader();
while (reader.Read())
{
foreach (string col in cols)
{
object field = reader.Item[col];
output.Add(field.ToString());
}
}
}
return output.ToArray();
}
Related
I have a table with three columns
MyDate : DateiIme
MyBlob: blob
Id: String
I want to return MyDate and MyBlob as Json. There can be multiple records in the table.
public class MyData
{
public string? MyDate { get; set; };
public string? MyBlob { get; set; };
}
public async Task<string> GetQueryResult(string Id)
{
MyData data = new MyData();
List<MyData> MyList = new List<MyData>();
string sqlSelect = string.Format("Select MyDate, MyBlob from MyTablee WHERE Id = '{0}'", Id);
try
{
MySqlCommand sqlcmd = new MySqlCommand();
MySqlConnection connetcion = new MySqlConnection(connectionString);
sqlcmd.Connection = connetcion;
sqlcmd.CommandTimeout = 0;
sqlcmd.CommandType = CommandType.Text;
sqlcmd.CommandText = sqlSelect;
connetcion.Open();
using (connetcion)
{
int count = 0;
using (MySqlDataReader reader = sqlcmd.ExecuteReader())
{
while (reader.Read())
{
data.MyDate = reader.GetString(0);
data.MyBlob = reader.GetString(1);
MyList.Add(data);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
var json = JsonSerializer.Serialize(results);
return json;
}
The output in the Postman is:
"{\"MyDate\":\"1/30/2023 9:16:40 PM\",\"MyBlob\":\"#MyBlob\"}"
I am not sure the blob data to JSON conversion is correct. Thank you.
It's probable that your table doesn't contain any BLOB data, but instead contains the literal string #MyBlob in that column.
The cause of this would be using a SQL statement like INSERT INTO MyTablee(MyDate, MyBlob) VALUES(NOW, '#MyBlob');, which inserts the literal text #MyBlob.
Make sure your insert code is constructed as follows:
using var command = connection.CreateCommand();
command.CommandText = "INSERT INTO MyTablee(MyDate, MyBlob) VALUES(NOW(), #blob);";
command.Parameters.AddWithValue("#blob", yourBlobDataHere);
command.ExecuteNonQuery();
In particular, there are no quotes around the parameter name in the INSERT statement, and command parameters are being used to send values, instead of string concatenation.
I am passing a long list of employeeIds to employeeIdlist and I split them into a List. Using this list I am adding parameters to my query.
I am getting the following error
{"Must declare the scalar variable \"#EmployeeId\"."}
public List<versionInfo> GetVersion(string employeeIdlist)
{
DbHelper helper = new DbHelper();
List<versionInfo> empVerInfo = new List<versionInfo>();
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand getVersion = new SqlCommand())
{
getVersion.Connection = conn;
getVersion.CommandText = #"SELECT EmployeeId,Version
FROM [dbo].[EmployeeVersion]
WHERE EmployeeId in (#EmployeeId)";
getVersion.CommandType = CommandType.Text;
List<int> empIds = employeeIdlist.Split(',').Select(int.Parse).ToList();
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (var emp in empIds)
{
// IN clause
sb.Append("#EmployeeId" + i.ToString() + ",");
// parameter
getVersion.Parameters.AddWithValue("#EmployeeId" + i.ToString(), emp);
i++;
}
// getVersion.Parameters.AddWithValue("#EmployeeId", employeeIdlist);
SqlDataReader rdr = getVersion.ExecuteReader();
while (rdr.Read())
{
versionInfo vi = new versionInfo();
vi.employeeId = helper.GetDb<int>(rdr, "EmployeeId");
vi.version = helper.GetDb<decimal>(rdr, "Version");
empVerInfo.Add(vi);
}
rdr.Close();
}
conn.Close();
}
return empVerInfo;
}
Remove the text after the IN
getVersion.CommandText = #"SELECT EmployeeId,Version
FROM [dbo].[EmployeeVersion]
WHERE EmployeeId in (";
then the foreach could build the full list of parameters and texts
foreach (var emp in empIds)
{
sb.Append("#EmployeeId" + i.ToString() + ",");
getVersion.Parameters.AddWithValue("#EmployeeId" + i.ToString(), emp);
i++;
}
after exiting the loop remove the last comma from the StringBuilder
sb.Length--;
finally, complete the command text appending the content of the StringBuilder and do not forget the closing parenthesys for the IN clause.
getVersion.CommandText += sb.ToString() + ")";
Now you can run the command with the correct IN clause and a matching list of parameters
If fails because your string query has one parameter named #EmployeeId and your Command object has many parameters with different names ("#EmployeeId1" is not equal to "#EmployeeId")
It seems like you are trying to apply this approach, which is a good idea.
You are two lines away of getting it to work:
Add this lines:
sb.Lenght--;
getVersion.CommandText = getVersion.CommandText.Replace("#EmployeeId",sb.ToString())
just before:
SqlDataReader rdr = getVersion.ExecuteReader();
After doing that your added parameters will match those #parameters existing in the sql string.
This is just another option. You can achieve the same result in 3 lines of code using Dapper ORM used in Stack Overflow.
You can download via NuGet.
public class VersionInfo
{
public int EmployeeId { get; set; }
public decimal Version { get; set; }
}
class Program
{
public static string connString = "...";
static void Main(string[] args)
{
var result = GetVersion(new List<int> {1, 2});
Console.ReadLine();
}
public static List<VersionInfo> GetVersion(IList<int> employeeIds)
{
using (IDbConnection conn = new SqlConnection(connString))
{
conn.Open();
var entities = conn.Query<VersionInfo>(
#"SELECT EmployeeId, Version from EmployeeVersion WHERE EmployeeId IN #EmployeeIds",
new {EmployeeIds = employeeIds});
return entities.ToList();
}
}
}
On your select statement you have to declare a value for your variable. I have made it an Integer. If it is a text value, then you can use varchar(25).
#"DECLARE #EmployeeId INT
SELECT EmployeeId,Version
FROM [dbo].[EmployeeVersion]
WHERE EmployeeId in (#EmployeeId)";
I ran into another issue again. I was trying to get data from the database using DataReader but I got the error when i was testing my code. Can anyone help me out? The error occurred at this line:
chkAssess = readAssess[columnName].ToString();
Below is the code snippet:
public string CheckAssess(string emailAddress, string columnName)
{
string chkAssess = "";
SqlDataReader readAssess;
//readAssess = new SqlDataReader();
string MgrAssessQry = "SELECT '"+columnName+"' FROM tblAllUsers";
//MgrAssessQry += " WHERE email ='" + emailAddress + "'";
SqlCommand cmdReadAssess = new SqlCommand(MgrAssessQry, cn);
cn.Open();
readAssess = cmdReadAssess.ExecuteReader();
while(readAssess.Read())
{
// Add the rows
chkAssess = readAssess[columnName].ToString();
}
return chkAssess;
}
try to use column name without ''
select something from table
instead of
select 'something' from table
for security reasons, don't create sql queries in that way (by concatenating strings) - use #parameters instead
2. close the reader at the end
Try this:
public string CheckAssess(string emailAddress, string columnName)
{
string chkAssess = "";
SqlDataReader readAssess;
//readAssess = new SqlDataReader();
string MgrAssessQry = "SELECT #Column_Name FROM tblAllUsers";
SqlCommand cmdReadAssess = new SqlCommand(MgrAssessQry, cn);
cmdReadAssess.Parameters.AddWithValue(new SqlParameter("Column_Name", columnName));
cn.Open();
readAssess = cmdReadAssess.ExecuteReader();
while(readAssess.Read())
{
// Add the rows
chkAssess = readAssess.GetString(0);
}
return chkAssess;
}
You have got several problems here.
Check whether your readAssess has rows like below.
if(readAssess.HasRows)
If it doesn't have rows then trying
chkAssess = readAssess.GetString(0);
would throw this error, as Arrays are index-based.
So your code should be like below
if(readAssess.HasRows)
{
while(readAssess.Read())
{
chkAssess = readAssess.GetString(0);
}
}
Other problem is you need to close both the reader & the connection afterwards.
readAssess.Close();
cn.Close();
Also your code is potentially vulnerable to SQL Injection.
if (reader.HasRows)
{
while (reader.Read())
{
int result = Convert.ToInt32(reader.GetString(0));
Console.WriteLine(result);
}
}
The most important thing is check the query first by executing in SQL Server and see if any result is coming or not.
Secondly based on the type of output you are receiving cast it to that particular data type (important).Mostly everyone is saving the data in varchar so.
I am trying to display a column from my local database into a dropdown list. The problem is that I would need to split the data so that they are not displayed all in one line. I have used the ";" to separate the data and then using the split(";") method to split them. I have tried the code that I've wrote below but it's not working. Any help will be appreciated.
public string DisplayTopicNames()
{
string topicNames = "";
// declare the connection string
string database = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|/Forum.accdb;Persist Security Info=True";
// Initialise the connection
OleDbConnection myConn = new OleDbConnection(database);
//Query
string queryStr = "SELECT TopicName FROM Topics";
// Create a command object
OleDbCommand myCommand = new OleDbCommand(queryStr, myConn);
// Open the connection
myCommand.Connection.Open();
// Execute the command
OleDbDataReader myDataReader = myCommand.ExecuteReader();
// Extract the results
while (myDataReader.Read())
{
for (int i = 0; i < myDataReader.FieldCount; i++)
topicNames += myDataReader.GetValue(i) + " ";
topicNames += ";";
}
//Because the topicNames are seperated by a semicolon, I would have to split it using the split()
string[] splittedTopicNames = topicNames.Split(';');
// close the connection
myCommand.Connection.Close();
return Convert.ToString(splittedTopicNames);
}
You are returning just one column from the table.
There is no reason to use a for loop over a field count (it is always 1)
Instead you could use a List(Of String) to save the values returned by the rows found.
Then return this list to use as datasource for your DropDownList
List<string> topicNames = new List<string>();
// Extract the results
while (myDataReader.Read())
{
topicNames.Add(myDataReader.GetValue(0).ToString();
}
....
return topicNames;
However it is not clear if the field TopicName contains itself strings separated by semicolon.
In this case you could write:
List<string> topicNames = new List<string>();
// Extract the results
while (myDataReader.Read())
{
string[] topics = myDataReader.GetValue(0).ToString().Split(';')
topicNames.AddRange(topics);
}
...
return topicNames;
if you prefer to return an array of strings then it is just a matter to convert the list to an array
return topicNames.ToArray();
EDIT
Of course returning an array or a List(Of String) requires changes to the return value of your method
public List<string> DisplayTopicNames()
{
......
}
or
public string[] DisplayTopicNames()
{
......
}
if you still prefer to return a string separated by semicolons then change the return statement in this way
return string.Join(";", topicNames.ToArra());
Unless I've lost my mind, something like this should work:
while (myDataReader.Read())
{
for (int i = 0; i < myDataReader.FieldCount; i++)
ddl.Items.Add(myDataReader.GetValue(i))
}
where ddl is the name of your DropDownList. If your ddl isn't available here, then add them to a List<string> collection instead and return that. And then this code may now become irrelevant:
//Because the topicNames are seperated by a semicolon, I would have to split it using the split()
string[] splittedTopicNames = topicNames.Split(';');
// close the connection
myCommand.Connection.Close();
return Convert.ToString(splittedTopicNames);
but, on top of all this I want to restructure the code for you a little because you need to be leveraging things like using.
public string DisplayTopicNames()
{
string topicNames = "";
// declare the connection string
string database = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|/Forum.accdb;Persist Security Info=True";
// Initialise the connection
using (OleDbConnection myConn = new OleDbConnection(database))
{
myConn.Open();
// Create a command object
OleDbCommand myCommand = new OleDbCommand("SELECT TopicName FROM Topics", myConn);
// Execute the command
using (OleDbDataReader myDataReader = myCommand.ExecuteReader())
{
// Extract the results
while (myDataReader.Read())
{
for (int i = 0; i < myDataReader.FieldCount; i++)
{
ddl.Items.Add(myDataReader.GetValue(i));
}
}
}
}
// not sure anything needs returned here anymore
// but you'll have to evaluate that
return "";
}
The reason you want to leverage the using statement is to ensure that unmanaged resources that exist in the DataReader and Connection get disposed properly. When leaving the using statement it will automatically call Dispose on the object. This statement is only used for objects that implement IDisposable.
I think this should work:
public List<string> DisplayTopicNames()
{
List<string> topics = new List<string>();
// Initialise the connection
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|/Forum.accdb;Persist Security Info=True");
OleDbCommand cmd = new OleDbCommand("SELECT TopicName FROM Topics");
using(conn)
using(cmd)
{
cmd.Connection.Open();
// Execute the command
using(OleDbDataReader myDataReader = cmd.ExecuteReader())
{
// Extract the results
while(myDataReader.Read())
{
topics.Add(myDataReader.GetValue(0).ToString());
}
}
}
return topics;
}
I'm using .NET Connector to connect to MySQL.
In my application there are few threads using the same connection, so if a MySQLDataReader is not closed yet and some thread is trying execute a query it gives that error:
There is already an open DataReader associated with this Connection which must be closed first.
Will there ever be support in MySQL for multiple result sets or however it's called?
My database management class:
public class DatabaseConnection
{
private MySqlConnection conn;
public void Connect(string server, string user, string password, string database, int port = 3306)
{
string connStr = String.Format("server={0};user={1};database={2};port={3};password={4};charset=utf8",
server, user, database, port, password);
conn = new MySqlConnection(connStr);
conn.Open();
}
private MySqlCommand PrepareQuery(string query, object[] args)
{
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
for (int i = 0; i < args.Length; i++)
{
string param = "{" + i + "}";
string paramName = "#DBVar_" + i;
query = query.Replace(param, paramName);
cmd.Parameters.AddWithValue(paramName, args[i]);
}
cmd.CommandText = query;
return cmd;
}
public List<Dictionary<string, object>> Query(string query, params object[] args)
{
MySqlCommand cmd = PrepareQuery(query, args);
MySqlDataReader reader = cmd.ExecuteReader();
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
while (reader.Read())
{
Dictionary<string, object> row = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
row.Add(reader.GetName(i), reader.GetValue(i));
}
rows.Add(row);
}
reader.Close();
return rows;
}
public object ScalarQuery(string query, params object[] args)
{
MySqlCommand cmd = PrepareQuery(query, args);
return cmd.ExecuteScalar();
}
public void Execute(string query, params object[] args)
{
MySqlCommand cmd = PrepareQuery(query, args);
cmd.ExecuteNonQuery();
}
public void Close()
{
conn.Close();
}
}
An example of how I use this:
DatabaseConnection Conn = new DatabaseConnection();
Conn.Connect("localhost", "root", "", "foogle");
var rows = conn.Query("SELECT * FROM `posts` WHERE `id` = {0}", postID);
foreach (var row in rows)
{
Console.WriteLine(row["title"]); // Writes the post's title (example)
}
Multiple result sets refers to a single query or query batch returning multiple row sets. Those results are accessed through the one and only DataReader for that connection.
What you're asking for is something quite different. You need the ability to performing multiple simultaneous queries of a single connection. Afaik .NET does not support that, not for SQL Server or any other driver.
Sharing a connection between multiple threads is a bad idea and totally unnecessary. .NET will use a connection pool to limit the total number of connections so It's perfectly safe to get a new connection for each (set of) queries you want to execute. Limit the scope of a connection to a thread and your problem will go away.