I want to return the value so on the caller i can use for example asd["columnname"] but im getting one error, example/code below.
I have this code
public static MySqlDataReader QueryResultadoMultString(string Query)
{
using (var conn = new MySqlConnection(myConnectionString))
{
try
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = Query;
MySqlDataReader myReader = null;
myReader = cmd.ExecuteReader();
if (myReader.HasRows)
{
while (myReader.Read())
{
Console.WriteLine(myReader.GetString(0));
return myReader;
}
}
return myReader;
}
catch (MySqlException ex)
{
NAPI.Util.ConsoleOutput($"[BaseDados][Erro] {ex.Message}");
return null;
}
}
}
Below is the caller
var asd = BaseDadosSQL.QueryResultadoMultString($"SELECT `socialclub`,`username`,`password` FROM contas WHERE socialclub = '{player.SocialClubName}'");
Console.WriteLine("Result "+asd["username"]);
I'm getting this error
System.Exception: 'No current query in data reader'
The method as written will force you to write code that is horribly vulnerable to sql injection issues. You need a separate set of arguments for parameters.
You want something more like this (which should also fix the issue in your question):
public static class BaseDadosSQL
{
private static string connectionString = "connection string here";
public static IEnumerable<IDataRecord> QueryResult(string Query, params MySqlParameter[] parameters)
{
using (var conn = new MySqlConnection(connectionString))
using (var cmd = new MySqlCommand(Query, conn))
{
if (parameters is object && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return reader;
}
}
}
}
}
And then call it like this:
//Guessing at type and length here. Use the actual type and length from the database
var p = new MySqlParameter("#SocialClub", MySqlDbType.VarString, 20);
p.Value = player.SocialClubName;
try
{
var asd = BaseDadosSQL.QueryResult($"SELECT `socialclub`,`username`,`password` FROM contas WHERE socialclub = #SocialClub", p);
foreach(var result in asd)
{
Console.WriteLine("Result " + result["username"]);
}
}
catch (MySqlException ex)
{
NAPI.Util.ConsoleOutput($"[BaseDados][Erro] {ex.Message}");
}
This code will let you use Social Club names which includes apostrophes. The original would have blown up. Notice I also moved the exception handling out of the DB code.
Ideally, even the QueryResult() method should also be private, with the BaseDadosSQL class having a separate public method for each query you need to run. So it would look more like this:
public static class BaseDadosSQL
{
private static string connectionString = "connection string here";
private static IEnumerable<IDataRecord> QueryResult(string Query, params MySqlParameter[] parameters)
{
using (var conn = new MySqlConnection(connectionString))
using (var cmd = new MySqlCommand(Query, conn))
{
if (parameters is object && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return reader;
}
}
}
}
public static IEnumerable<IDataRecord> GetClubLogin(string clubName)
{
//Still guessing at type and length here.
var p = new MySqlParameter("#SocialClub", MySqlDbType.VarString, 20);
p.Value = clubName;
return QueryResult($"SELECT `socialclub`,`username`,`password` FROM contas WHERE socialclub = #SocialClub", p);
}
}
And then called like this:
try
{
foreach(var result in BaseDadosSQL.GetClubLogin(player.SocialClubName))
{
Console.WriteLine("Result " + result["username"]);
}
}
catch (MySqlException ex)
{
NAPI.Util.ConsoleOutput($"[BaseDados][Erro] {ex.Message}");
}
Finally, it's really Really REALLY BAD to store passwords like that. So bad, it's not even okay for testing/learning/proof of concept code. NEVER DO THAT! It's not even okay to store passwords encrypted. Encryption is not good enough.
Passwords should only ever be stored as a fixed-length, salted, cryptographic (not-MD5) hash value. When someone tries to login, you salt and hash the attempted credential, and then compare the hash values, not the actual password. Anything else is just begging to end up on the front page of your newspaper of choice as the latest big data breach.
I am having problem with SQLDataReader when it comes into situation that I have one row of data, and one or tho columns (fields) have null value.
Can anybody help with this? Null values are related to string, float and datetime datatype.
First method is:
public class SpajanjeCollection:ObservableCollection<Spajanje>
{
public static SpajanjeCollection GetAllSpajanjes()
{
SpajanjeCollection spajanjes = new SpajanjeCollection();
Spajanje spajanje = null;
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["ConnString"].ToString();
conn.Open();
SqlCommand command = new SqlCommand("SELECT Ponude.IdPonude, Ponude.SifraProizvoda, Proizvodi.Naziv, Proizvodi.Opis, Proizvodi.PocetnaCijena, Ponude.PocetakAukcije, Users.UserName, Ponude.PonudjenaCijena, Ponude.ZavrsetakAukcije, Ponude.Status FROM Ponude LEFT OUTER JOIN Users ON Ponude.Kupac = Users.UserName INNER JOIN Proizvodi ON Ponude.SifraProizvoda = Proizvodi.Id", conn);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
spajanje = Spajanje.GetSpajanjeFromResultSet(reader);
spajanjes.Add(spajanje);
}
}
}
return spajanjes;
}
}
Second method for data reading is:
public static Spajanje GetSpajanjeFromResultSet(SqlDataReader reader)
{
Spajanje spajanje = new Spajanje(Convert.ToInt16(reader["IdPonude"]), Convert.ToInt16(reader["SifraProizvoda"]), Convert.ToString(reader["Naziv"]), Convert.ToString(reader["Opis"]), float.Parse(reader["PocetnaCijena"].ToString()), (DateTime?)reader["PocetakAukcije"], Convert.ToString(reader["UserName"]), float.Parse(reader["PonudjenaCijena"].ToString()), (DateTime?)reader["ZavrsetakAukcije"], Convert.ToString(reader["Status"]));
return spajanje;
}
First post here. I'm trying to create a website that fetches data from an Oracle database and returns some tables. I was able to connect my database fine and made a DataConnector that returns a list of CodeDesc objects. My main problem right now is simply displaying that data to the screen, preferably in the form of a drop down list but I'm using a GridView for now.
Here's my front end:
protected void Button1_Click(object sender, EventArgs e)
{
DataConnector dc = new DataConnector();
GridView2.DataSource = dc.getCodeTypes();
GridView2.DataBind();
}
When I click the button, nothing is generated and the debugger only says "Exception thrown: 'System.ArgumentException' in Oracle.DataAccess.dll" Any help would be appreciated. This is my first time doing web development and it's been a struggle to get even this far. I'm using Visual Studio 2015
Back End:
//Create datatable to get info from db & return results
public List<CodeDesc> getCodeTypes()
{
try
{
OracleConnection con = new OracleConnection(connString);
con.Open();
string query = "select id, descr from code_desc where code_type_id = 0";
// Create the OracleCommand
OracleCommand cmd = new OracleCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
// Execute command, create OracleDataReader object
OracleDataReader reader = cmd.ExecuteReader();
List<CodeDesc> L = new List<CodeDesc>();
while (reader.Read())
{
CodeDesc c = new CodeDesc();
c.id = reader.GetInt32(0);
c.description = reader.GetString(1);
L.Add(c);
}
// Clean up
reader.Dispose();
cmd.Dispose();
con.Dispose();
System.Diagnostics.Debug.WriteLine(L);
return L;
}
catch (Exception ex)
{
// catch clause here...
}
}
CodeDesc:
public class CodeDesc
{
public int id { get; set; }
public string description { get; set; }
}
Any help would be great.
You never set the query string to the CommandText property of the OracleCommand. Of course this can only result in an exception when you try to execute your command.
Said that, remember that every disposable object should be enclosed in a using statement. This is very important in case of exceptions because the correct closing and disposing is executed automatically exiting from the using block
public List<CodeDesc> getCodeTypes()
{
try
{
List<CodeDesc> L = new List<CodeDesc>();
string query = "select id, descr from code_desc where code_type_id = 0";
using(OracleConnection con = new OracleConnection(connString))
using(OracleCommand cmd = new OracleCommand(query, con))
{
con.Open();
// Execute command, create OracleDataReader object
using(OracleDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
CodeDesc c = new CodeDesc();
c.id = reader.GetInt32(0);
c.description = reader.GetString(1);
L.Add(c);
}
}
}
System.Diagnostics.Debug.WriteLine(L);
return L;
}
I'm populating my List like this:
public List<Functions> ListFunctions(int proj)
{
List<Functions> lFunctions = new List<Functions>();
try
{
string sql = "SELECT id, description FROM functios WHERE project = #project ORDER BY description ASC";
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new MySqlParameter("#project", MySqlDbType.Int32)).Value = proj;
using (MySqlDataReader reader = _dal.ExecutaReader(cmd))
{
while (reader.Read())
{
lFunctions.Add(new Function
{
ID = Convert.ToInt32(reader["id"]),
Descripton = reader["description"].ToString()
});
}
}
return lFunction;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
How may I loop through the list and get those values now ? The Description and the ID
I'd like to know a way to get exactly the fieldName, like:
string name = ListFunction["FieldName"].ToString()
You just enumerate over the returned list. For example:
var functions=ListFunctions(0); // For example
foreach(var function in functions)
{
Console.WriteLine("{0} = {1}",function.ID, function.Description);
}
When calling the same query method twice in a session of the app, I get "DBCommandExcept"
As an experiment, I decided to dispose of the connection object at the end of the method, to see if that was the problem.
I no longer get the DBCommandExcept err msg, but instead get, "the connectionstring property has not been initialized."
IOW, it's sort of a Catch-22 situation at the moment. The pertinent code is:
string query = "SELECT Bla FROM Blah";
SqlCeCommand cmd = new SqlCeCommand(query);
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection();
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn;
SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow);
try
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
catch (Exception ex)
{
RRDR.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound(): {0}", ex.Message));
}
finally
{
myReader.Close();
//if (null != conn)
//{
// conn.Dispose();
//}
}
// Re: the commented-out block above: When it is active, the DBCommandExcept problem is not seen; however, I then get, "the connectionstring property has not been initialized"
I think the only non-SQL-CE-standard bit above is the dbConn.GetConnection(). Here's some of that code:
SqlCeConnection objCon = null;
. . .
public SqlCeConnection GetConnection()
{
return objCon;
}
private DBConnection() // class constructor
{
try
{
. . .
objCon = new SqlCeConnection(conStr);
objCon.Open();
. . .
Again, the error (either one, whichever one I "choose" to have) is seen only the second time through this method during one run of the app. The first time works fine.
UPDATE
I added the code below, and the comments tell the tale of woe:
// With conn check only, still get two consecutive DBCommandExcepts
// With cmd check only, still get two consecutive DBCommandExcepts
// With both, still get two consecutive DBCommandExcepts; IOW, all have the same effect
if (null != conn)
{
conn.Close();
}
if (null != cmd)
{
cmd.Dispose();
}
UPDATE 2
Based on unicron's suggestion, I tried using "using."
In two of the three cases (SqlCeCommand and SqlCeDataReader), converting to "using" made no diff; in the other one (SqlCeConnection), it raised the err msgs, "The ConnectionString property has not been initialized."
Still, though, the code is cleaner with the two usings, so thanks for that nudge in the best practices direction.
Here's what it looks like now:
private bool PopulateControlsIfPlatypusItemsFound()
{
const int ITEMID_INDEX = 0;
const int PACKSIZE_INDEX = 1;
bool recordFound = false;
try
{
string PlatypusId = txtPlatypus.Text.ToString().Trim();
string PlatypusItemId = txtUPC.Text.ToString().Trim();
string itemID = string.Empty;
string packSize = string.Empty;
string query = string.Format("SELECT ItemID, PackSize FROM PlatypusItems WHERE PlatypusID = {0} AND PlatypusItemID = {1}", PlatypusId, PlatypusItemId);
using (SqlCeCommand cmd = new SqlCeCommand(query))
{
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection();
if ((null != conn) && (!conn.State.Equals(ConnectionState.Open)))
{
conn.Open();
TTBT.LogMsgs.Append("Connection opened");
}
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn;
using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
txtID.Text = itemID;
txtSize.Text = packSize;
return recordFound;
}
}
catch (Exception ex)
{
TTBT.LogMsgs.Append(string.Format("Exception in PopulateControlsIfPlatypusItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
return recordFound;
}
}
UPDATE 3
I've come even closer to normalcy by replacing the custom connection code with the generic sort, adding another "using" to the mix:
private bool PopulateControlsIfVendorItemsFound()
{
const int ITEMID_INDEX = 0;
const int PACKSIZE_INDEX = 1;
bool recordFound = false;
DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
try
{
string vendorId = txtVendor.Text.ToString().Trim();
string vendorItemId = txtUPC.Text.ToString().Trim();
string itemID = string.Empty;
string packSize = string.Empty;
if ( dbconn.isValidTable( "VendorItems" ) == -1 )
{
DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
return false;
}
string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
using (SqlCeCommand cmd = new SqlCeCommand(query))
{
cmd.CommandType = CommandType.Text;
using (SqlCeConnection conn = new SqlCeConnection())
{
string filename = "\\badPlace2B\\CCRDB.SDF";
conn.ConnectionString = string.Format("Data Source = {0}", filename);
cmd.CommandType = CommandType.Text;//probably unnecessary/moot
cmd.Connection = conn;
conn.Open();
using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
}
txtID.Text = itemID;
txtSize.Text = packSize;
return recordFound;
}
}
catch (Exception ex)
{
DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
return recordFound;
}
}
...yet I still get "DBCommandExcept"...
As to "stop futzing around with opening the connection," isn't it necessary to do so? How could/should the code above be different?
UPDATE 4
What is even more bizarre is that now my debug log file has stopped being written. I have been writing it out both in the global exception handler AND in the main form's Closed event(), and it always has (until now) at least a few entries, but within the last couple of updates to the code, it is no longer being written...????
Both places global exception handler and main form's Closed event(), the code is like so:
public static bool inDebugMode = true;
. . .
if (CCR.inDebugMode)
{
DateTime dt = DateTime.Now;
string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
using (StreamWriter file = new StreamWriter(timeAsStr))
{
// If the app closes normally, this is how the file is written; if it doesn't,
// (it crashed) it's written in PDAClient.ExceptionHandler()
file.WriteLine(SSCS.LogMsgs.ToString());
}
}
Since you are making several calls to a database file (that isn't going to change), I'd start out by defining your connection string and your SQL statements at the top of your class as global values:
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize " +
"FROM VendorItems " +
"WHERE VendorID=#VendorID AND VendorItemID=#VendorItemID";
These never change, so there is no reason to define them again each time you call your routine.
Personally, I do not like inserting values into SQL statements, like you have shown. Rather, try to use Parameters.
To use Parameters, you'll need to look into your database to see what type of columns VendorID and VendorItemID are. My guess is that they are both int values, but these could be GUID like values, requiring VarChar type strings. If these are strings, you should write down what sizes the columns are defined as.
For example: Below, my Serial_Number column is the SqlDbType.NVarChar and the size is 50. An SqlCeParameter for this column would be:
cmd.Parameters.Add("#Serial_Number", SqlDbType.NVarChar, 50).Value = txtSerial_Number.Text.Trim();
Since I did not know what type of data you use, I created an enumerated type to show how each method would be used. If you do not have access to the table's design, the last resort is "AddWithValue" (I personally hate that one, because it makes me look like I don't know what my database has inside).
enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
To use this enumerated type, I modified the signature of your method to pass in that value:
private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
Obviously, you will not need this, because you should know what technique you are going to be coding with.
I wasn't able to figure out what your dbconn object was. Initially, I thought this was your SqlCeConnection, but that does not have an isValidTable method, so I just commented it out:
//if (dbconn.isValidTable("VendorItems") == -1) {
// DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
// return false;
//}
Speaking of SqlCeConnection...
I combined your SqlCeCommand instance with your SqlCeConnection instance. Less code typically means fewer errors:
using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
The CommandType, by default, is CommandType.Text, so this line is unnecessary:
// cmd.CommandType = CommandType.Text; (this is the default)
I moved most of your variable reading outside of the try/catch routine, as none of that should ever cause an exception to be generated.
Also, I used the more targeted SqlCeException instead of the general Exception. The only thing that could fail in the block is something SqlCe related, and the SqlCeException will give you better/more specific error messages than the general Exception object will.
} catch (SqlCeException err) {
So, what does it look like all put together?
Code:
enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize FROM VendorItems WHERE VendorID=#VendorID AND VendorItemID=#VendorItemID";
private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
bool recordFound = false;
//DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
string itemID = null;
string packSize = null;
//string vendorId = txtVendor.Text.Trim();
//string vendorItemId = txtUPC.Text.Trim();
//string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
//if (dbconn.isValidTable("VendorItems") == -1) {
// DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
// return false;
//}
using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
// cmd.CommandType = CommandType.Text; (this is the default)
if (style == ParamStyle.AddIntegers) { // Adding Integers:
cmd.Parameters.Add("#VendorID", SqlDbType.Int).Value = Convert.ToInt32(txtVendor.Text.Trim());
cmd.Parameters.Add("#VendorItemID", SqlDbType.Int).Value = Convert.ToInt32(txtUPC.Text.Trim());
} else if (style == ParamStyle.AddVarChar) { // Adding String Values
// NOTE: Here, you should look in your database table and
// use the size you defined for your VendorID and VendorItemID columns.
cmd.Parameters.Add("#VendorID", SqlDbType.VarChar, 25).Value = txtVendor.Text.Trim();
cmd.Parameters.Add("#VendorItemID", SqlDbType.VarChar, 50).Value = txtUPC.Text.Trim();
} else if (style == ParamStyle.AddWithValue) { // Adding as Objects (only if you don't know what the data types are)
cmd.Parameters.AddWithValue("#VendorID", txtVendor.Text.Trim());
cmd.Parameters.AddWithValue("#VendorItemID", txtUPC.Text.Trim());
}
try {
cmd.Connection.Open();
using (var myReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) {
if (myReader.Read()) {
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
} catch (SqlCeException err) {
//DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0}\r\n", err.Message));
// (I never return from a 'catch' statement) return recordFound;
} finally {
if (cmd.Connection.State == ConnectionState.Open) {
cmd.Connection.Close();
}
}
}
if (recordFound) { // set these last, and set them OUTSIDE of the try/catch block
txtID.Text = itemID;
txtSize.Text = packSize;
}
return recordFound;
}
Happy Coding!