I updated the code in order to simplify it
I’ve encountered a strange issue that never happened before on this code (run perfectly for 2 years).
I’m running MVC application over multiple IISs, and using MySQL over C# code.
Querying table X receives response from a different table in the same schema.
The issue happens sporadically on some IIS (once a 1-4 days) and solved by 3 iisreset.
Here is the code I’m using (updated):
using (var conn = new MySqlConnection(connectionString))
{
var command = conn.CreateCommand();
command.CommandText = sql;
IDataReader reader;
try
{
reader = command.ExecuteReader();
}
catch (Exception ex)
{
throw new MoranbernateQueryException(sql, ex);
}
using (reader)
{
while (reader.Read())
{
Console.WriteLine(sql, GetTableFromReader(reader));
}
}
}
}
private static string GetTableFromReader(IDataReader reader)
{
var schema = reader.GetSchemaTable();
var rows = schema?.Rows;
if (rows != null && schema.Columns.Count > 11)
{
return rows[0][10].ToString();
}
}
The result of this code is an sql query to table A but the table (and data) from the reader schema is B. Both tables resides in the same DB and Schema.
Has anyone else encountered this type of problem? I’d like to hear any theory or suggestion for investigation.
i don't know how do you access reader even it out of scope, there is may problem with flow of your code. use below code i think this have to work.
using(var command = new MySqlConnection(connectionString))
{
command.CommandText = sql;
try
{
using(SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var t = new T();
var i = 0;
foreach (var property in properties)
{
try
{
property.SetValue(t, reader.GetValue(i++));
}
catch (Exception ex)
{
HandleSetValueException<T>(sql, parameters, reader, connection, ex);
}
}
yield return t;
}
}
}
catch (Exception ex)
{
throw new MoranbernateQueryException(sql, ex);
}
}
So apparently it's a known issue that was fixed by MySql team, after updating the driver all is well for now
Related
I'm wanting to make a small and simple mobile app for a school project, I know connecting to a db from a phone is not good for security reasons but basically only I will touch it.
So to connect my Xamarin app to Mysql I downloaded the extension MysqlConnector (https://www.nuget.org/packages/MySqlConnector/2.1.8?_src=template)
Everything seemed to work at first, but now I think that there is a problem in their library that is not compatible with Xamarin:
I seem to always get a nullreference exception at the second query at line
reader = cmd.ExecuteReader();. I don't know why, nothing is null, I've printed everything.
(I've put a comment on the line where it happens) I seriously doubt it is a problem in their library since they have 37.2M downloads in total. But maybe it is just a compatability conflict, but that makes it odd that the first query works then.
Here is all my current code:
using PuppyChinoBestelling.Views;
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
using MySqlConnector;
using System.Threading.Tasks;
namespace PuppyChinoBestelling.ViewModels
{
public class LoginViewModel : BaseViewModel
{
public Command LoginCommand { get; }
public string Mail { get; set; }
public string Pass { get; set; }
public LoginViewModel()
{
Pass = string.Empty;
Mail = string.Empty;
LoginCommand = new Command(OnLoginClicked);
}
private async void OnLoginClicked(object obj)
{
MySqlConnection conn = new MySqlConnection("private");
try
{
conn.Open();
Console.WriteLine("Conn opened!");
}
catch(Exception ex)
{
Console.WriteLine("Error " + ex.Message);
}
string sql = #"SELECT * FROM users WHERE email = #email;";
var cmd = conn.CreateCommand();
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#email", Mail);
var reader = cmd.ExecuteReader();
if (reader.HasRows)
{
sql = #"SELECT * FROM users WHERE email = #email;";
cmd = conn.CreateCommand();
cmd.Parameters.Clear();
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#email", Mail);
reader = cmd.ExecuteReader(); //null reference happening here idk why
string pwdHashed = reader.GetString(5);
bool validPwd = BCrypt.Net.BCrypt.Verify(Pass, pwdHashed);
conn.Close();
if (validPwd)
{
await Shell.Current.GoToAsync($"//{nameof(AboutPage)}");
}
else
{
Console.WriteLine("Foute logingegevens!");
}
}
else
{
Console.WriteLine("Je bestaat niet!");
}
}
}
}
Thanks in advance!
It's hard to say for certain, but it's likely the issue is because you are not closing the reader and command, and you can't have multiple commands on the same connection.
Also, you need to advance the reader using reader.Read.
In any case there is no need to run the command twice in the first place. You already had all the information on the first run.
You also need to dispose everything with using. This automatically closes the connection.
Don't SELECT *, just select the columns you need.
Ideally, you would calculate the hash for the given password, and send it to the database server to check, rather than pulling out the real password hash from the database (could be a security risk).
Don't store hashes as strings. Instead store them as binary with the varbinary data type, and cast to byte[] on the C# side.
Unclear why you are handling errors only for opening the connection, not for executing the command.
private async void OnLoginClicked(object obj)
{
const string sql = #"
SELECT Pass
FROM users
WHERE email = #email;
";
using (var conn = new MySqlConnection("private"))
using (var cmd = new MySqlCommand(sql, conn))
{
try
{
conn.Open();
Console.WriteLine("Conn opened!");
}
catch(Exception ex)
{
Console.WriteLine("Error " + ex.Message);
return; // no point continuing
}
cmd.Parameters.AddWithValue("#email", Mail);
using (var reader = cmd.ExecuteReader())
{
if (!reader.Read())
{
Console.WriteLine("Je bestaat niet!");
return; // no point continuing
}
string pwdHashed = (string)reader["Pass"];
conn.Close();
bool validPwd = BCrypt.Net.BCrypt.Verify(Pass, pwdHashed);
if (validPwd)
{
await Shell.Current.GoToAsync($"//{nameof(AboutPage)}");
}
else
{
Console.WriteLine("Foute logingegevens!");
}
}
}
}
An alternative method is to remove the reader altogether and use ExecuteScalar
private async void OnLoginClicked(object obj)
{
const string sql = #"
SELECT Pass
FROM users
WHERE email = #email;
";
using (var conn = new MySqlConnection("private"))
using (var cmd = new MySqlCommand(sql, conn))
{
try
{
conn.Open();
Console.WriteLine("Conn opened!");
}
catch(Exception ex)
{
Console.WriteLine("Error " + ex.Message);
return; // no point continuing
}
cmd.Parameters.AddWithValue("#email", Mail);
string pwdHashed = cmd.ExecuteScalar() as string;
conn.Close();
if (pwdHashed is null)
{
Console.WriteLine("Je bestaat niet!");
return; // no point continuing
}
bool validPwd = BCrypt.Net.BCrypt.Verify(Pass, pwdHashed);
if (validPwd)
{
await Shell.Current.GoToAsync($"//{nameof(AboutPage)}");
}
else
{
Console.WriteLine("Foute logingegevens!");
}
}
}
Hi all: I have a program that is running 4 threads that talk to an Oracle database. I have the Oracle connections local to each thread, and I'm employing the USING statement as well as manually closing the recordset and closing the connection. As I understand it, the ORA-01000 error arises when there are more open recordsets than configured cursors on the database. I do not understand why my recordsets are staying open or why I'm getting this error. Here's the code:
static void CheckPaths()
{
int pathcount = paths.Count; //paths is a typed list
Parallel.ForEach(paths, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (p) =>
{
try
{
CheckSinglePathAllHours(p);
}
catch (Exception ex)
{
//there is logging here, this is where the exception hits
}
});
}
static void CheckSinglePathAllHours(Path p)
{
string sqlBase = #"Select * from table ";//this is actually a big SQL statement
using (DBManager localdbm = new DBManager())
{
string sql = sqlBase;
OracleDataReader reader = localdbm.GetData(sql);
while (reader.Read())
{
//process the path, query always returns 24 or less rows
}
reader.Close();
reader = null; //is this even necessary?
localdbm.Close(); //is this necessary in conjunction with the USING statement?
}
}
class DBManager : IDisposable
{
OracleConnection conn;
OracleCommand cmd;
public DBManager()
{
string connStr = "blah blah blah";
conn = new OracleConnection(connStr);
conn.Open();
cmd = conn.CreateCommand();
}
public OracleDataReader GetData(string sql)
{
cmd.CommandText = sql;
cmd.CommandTimeout = 900;
return cmd.ExecuteReader();
}
public void RunSQL(string sql)
{
cmd.CommandText = sql;
cmd.CommandTimeout = 900;
cmd.ExecuteNonQuery();
}
public void Close()
{
conn.Close();
}
public void Dispose()
{
try
{
conn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
The code will usually run for about a minute or two before the exception. The exception message is two-fold: ORA-00604: error occured at recursive SQL level 1; and ORA-01000: maximum open cursors exceeded. Any ideas what I'm doing wrong?
Changed the code to call .Dispose() on OracleDataReader and OracleConnection as suggested by Paul Abbott. Also increased the number of cursors per session from 50 to 150 on the database.
I have to make automatic generate new AccountID on my load windows form app.
So for example when users start windows form "Add new Account" in textbox for "Account id" I have to show latest value from database. If i have two accounts in database on windows form in textbox value will be three.
My code perfectly work if i have at least one account in database, but when my database is empty i got exception.
This is my code:
public int GetLatestAccountID()
{
try
{
command.CommandText = "select Max(AccountID)as maxID from Account";
command.CommandType = CommandType.Text;
connection.Open();
OleDbDataReader reader= command.ExecuteReader();
if (reader.Read())
{
int valueID = Convert.ToInt32(reader["maxID"]);
return valueID + 1;
}
return 1;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (connection!= null)
{
connection.Close();
}
}
}
Also I find answer on stackoverflow:
object aa = DBNull.Value;
int valueID = (aa as int?).GetValueOrDefault();
But this line of code works if my database is empty, but when I have one account in the database, it will always show on my windows form in account id textbox value one. I use Microsoft Access 2007 database.
I appreciate any help.
You may further simplify it like below,
Select isnull(max(accountID),0) as maxID from Account
I'm guessing you want:
public int GetLatestAccountID(string connectionString)
{
using(var dbConn = new OleDbConnection(connectionString))
{
dbConn.Open();
string query = "select Max(AccountID) from Account";
using(var dbCommand = new OleDbCommand(query, dbConn))
{
var value = dbCommand.ExecuteScalar();
if ((value != null) && (value != DBNull.Value))
return Convert.ToInt32(value) + 1;
return 1;
}
}
}
It looks like you're opening your database connection once and leaving it open during your entire program. Don't do that; that leads to race conditions and data corruption. .NET implements database connection pooling so you're not improving performance at all by leaving connections open.
You're also not telling us what you're using GetLatestAccountID for. If you're trying to use that as a primary key you are also going to run into problems with race conditions. If you want a primary key you should let the database create it and return the value after you've created the record.
public int GetLatestAccountID()
{
try
{
int accounts = 0;
command.CommandText = "select Max(AccountID)as maxID from Account";
command.CommandType = CommandType.Text;
connection.Open();
OleDbDataReader reader= command.ExecuteReader();
if (reader.Read())
{
accounts = Convert.ToInt32(reader["maxID"]) + 1;
}
return accounts;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (connection!= null)
{
connection.Close();
}
}
}
Could you use SELECT COUNT(column_name) FROM table_name; to count number of accounts instead of selecting which one is the biggest?
I'm trying to change my program from an SqlConnection to an OleDbConnection but I've hit a little block.
My program works as expected using SqlConnection but I can't get it to work with OleDb.
My program reads the results of a stored procedure (XML), sends it to a web service, then stores the results in a table.
I'm having issues with reading the XML from the stored procedure.
Here's my code for the first part:
public static bool BuildXml()
{
using (OleDbCommand buildXml = new OleDbCommand("usp_BUILD_RISKCALC_XML", SqlOleDbConnection))
{
buildXml.CommandType = CommandType.StoredProcedure;
try
{
OleDbDataReader reader = buildXml.ExecuteScalar();
while (reader.Read())
{
SendXml = reader.GetString(0);
}
}
catch (Exception ex)
{
WriteLog(ex.Message, 101);
return false;
}
}
}
I'm getting an InvalidOperationException the text of which reads
Specified cast is not valid
For reader.GetString(0).
I am 100% sure the stored procedure is working as I have tested using an SqlConnection as well as running in SQL Server Management Studio.
Your code should be;
public static bool BuildXml()
{
using (OleDbCommand buildXml = new OleDbCommand("usp_BUILD_RISKCALC_XML", SqlOleDbConnection))
{
buildXml.CommandType = CommandType.StoredProcedure;
try
{
object value = buildXml.ExecuteScalar();
SendXml = Convert.ToString(value);
}
catch (Exception ex)
{
WriteLog(ex.Message, 101);
return false;
}
}
}
The code buildXml.ExecuteScalar() returns an object. So casting it to an OleDbDataReader will end up in the InvalidOperationException.
ExecuteScalar return type's is object and you can't convert it to OleDbDataReader. When you have used ExecuteScalar means that you are sure that you want to return only one value from your stored procedure. You don't need OleDbDataReader just try following:
SendXml = buildXml.ExecuteScalar().ToString;
I'm looking to write a C# SQL Server wrapper to call some stored procedures. If I was writing a single function I'd do something like the following (which I think is correct/proper):
void RunStoredProc1(object arg1)
{
using(SqlConnection conn = new SqlConnection(connStr)){
try{
SqlCommand cmd = new SqlCommand("storedProc1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#input1", arg1);
conn.Open();
cmd.ExecuteNonQuery();
} catch (Exception ex){
//handle the exception appropriately.
}
}
}
The problem I'm having is that it seems like a lot of repeated code... every function will have the same using/try(open/execute)/catch code, and it'd be nice to have it all in only one place. Is there a clean way of doing this? How about for queries that I'd want to use a data reader on?
Something like this should do:
void RunStoredProc(string storedProcName, IDictionary<string, object> args)
{
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = storedProcName;
cmd.CommandType = CommandType.StoredProcedure;
foreach (KeyValuePair<string, object> kvp in args)
{
cmd.Parameters.AddWithValue(kvp.Key, kvp.Value);
}
conn.Open();
cmd.ExecuteNonQuery();
}
}
The connection object itself would probably also be better off as a parameter to this helper method, so you could make it static. It might be interesting to write it as an extension method on SqlConnection.
I would keep the exception handling in your RunStoredProc1 method or even more likely: in the methods that call RunStoredProc1, because exception handling will likely differ on a case by case basis.
void RunStoredProc1(object input1)
{
var args = new Dictionary<string, object>()
{
{ "input1", input1 }
};
try
{
RunStoredProc("storedProc1", args);
}
catch (Exception ex)
{
// Handle exception properly
}
}
Just a fun exercise for me, and not necessarily the way you'd want to implement it. I wrote a quick fluent interface for building and executing SqlCommands.
A couple of sample usages:
int i = Sql.UsingConnection("sample")
.GetTextCommandFor("Select Top 1 ActorID From Actor Where FirstName = #fname")
.AddParameters(new {fname = "Bob"})
.OnException(e => Console.WriteLine(e.Message))
.ExecuteScalar<int>();
var q = Sql.UsingConnection("sample")
.GetTextCommandFor("Select * From Actor Where FirstName=#fname and ActorID > #id")
.AddParameters(new {id = 1000, fname = "Bob"});
using(var reader = q.ExecuteReader())
{
while(reader.Read())
{
// do something
}
}
The actual class(es) and Interfaces are below:
public class Sql
{
public static ISqlCommandTypeSelector UsingConnection(string connection)
{
return new SqlBuilder(connection);
}
private class SqlBuilder : ISqlCommandTypeSelector, ISqlParameterManager, ISqlExecutor
{
private string _connection;
private string _sqltext;
private CommandType _commandtype;
private Action<Exception> _exceptionBehavior = DefaultExceptionBehavior;
private IList<SqlParameter> _inParams;
public SqlBuilder(string connection)
{
_connection = ConfigurationManager.ConnectionStrings[connection].ConnectionString;
_inParams = new List<SqlParameter>();
}
public ISqlParameterManager GetTextCommandFor(string text)
{
_sqltext = text;
_commandtype = CommandType.Text;
return this;
}
public ISqlParameterManager GetProcCommandFor(string proc)
{
_sqltext = proc;
_commandtype = CommandType.StoredProcedure;
return this;
}
public ISqlExecutor OnException(Action<Exception> action)
{
_exceptionBehavior = action;
return this;
}
public void ExecuteNonQuery()
{
try
{
using (var connection = new SqlConnection(_connection))
using (var cmd = connection.CreateCommand())
{
ConfigureCommand(cmd);
PopulateParameters(cmd);
connection.Open();
cmd.ExecuteNonQuery();
}
}
catch(Exception ex)
{
_exceptionBehavior(ex);
}
}
public T ExecuteScalar<T>()
{
T result = default(T);
try
{
using (var connection = new SqlConnection(_connection))
using (var cmd = connection.CreateCommand())
{
ConfigureCommand(cmd);
PopulateParameters(cmd);
connection.Open();
result = (T) cmd.ExecuteScalar();
return result;
}
}
catch(InvalidCastException ex)
{
// rethrow?
}
catch(Exception ex)
{
_exceptionBehavior(ex);
}
return result;
}
public IDataReader ExecuteReader()
{
try
{
var connection = new SqlConnection(_connection);
var cmd = connection.CreateCommand();
ConfigureCommand(cmd);
PopulateParameters(cmd);
connection.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
catch(Exception ex)
{
_exceptionBehavior(ex);
}
return null;
}
public ISqlExecutor AddParameters(object #params)
{
var type = #params.GetType();
var props = type.GetProperties();
foreach (var propertyInfo in props)
{
var param = new SqlParameter("#" + propertyInfo.Name, propertyInfo.GetValue(#params, null));
param.Direction = ParameterDirection.Input;
_inParams.Add(param);
}
return this;
}
public ISqlExecutor WithoutParams()
{
return this;
}
private void ConfigureCommand(SqlCommand cmd)
{
cmd.CommandText = _sqltext;
cmd.CommandType = _commandtype;
}
private void PopulateParameters(SqlCommand cmd)
{
cmd.Parameters.AddRange(_inParams.ToArray());
}
private static void DefaultExceptionBehavior(Exception e)
{
// do something
}
}
}
public interface ISqlCommandTypeSelector
{
ISqlParameterManager GetTextCommandFor(string text);
ISqlParameterManager GetProcCommandFor(string proc);
}
public interface ISqlExecutor
{
ISqlExecutor OnException(Action<Exception> action);
void ExecuteNonQuery();
T ExecuteScalar<T>();
IDataReader ExecuteReader();
}
public interface ISqlParameterManager
{
ISqlExecutor AddParameters(object #params);
ISqlExecutor WithoutParams();
}
There is some repeated code that could probably be refactored some more if you really hate repeated code. This is just a fun exercise, and probably not how you want to do your data access however. This also doesn't support out parameters as it is written.
The Microsoft Enterprise Library Data Access Application Block can help to reduce redundant code like that, if you're sticking to pure ADO.NET for your data layer. See http://msdn.microsoft.com/en-us/library/ff664408(v=PandP.50).aspx. There are lots of code samples online and in the download as well, i.e. http://msdn.microsoft.com/en-us/library/ff664702(v=PandP.50).aspx.
I'm a big fan of letting computers do the rote, repetitive work. They're very good at it. I only have to teach them to do it once. So I wrote a code generator that uses a reference database to generate strongly typed access code. The advantage of this technique is that if you change the stored procedure's signatures, all you have to do it re-gen your data access layer. Any breaking changes will cause compile errors.
The code generate I wrote reads an XML file identifying the stored procedures of interest and retrieves their metadata from the specified reference database(s).
The XML file contains flags identifying whether each stored procedure returns
multiple result sets (dataset)
a single result set (datatable)
a single row (datarow)
a single row with a single column (a scalar value)
a DataReader
an XmlReader
or nothing (nonquery)
From that it generates appropriate code, 1 class per stored procedure. The generated code provides access to the stored procedure's return code as well as the returned value for any output parameters.
It also parses the declaration for the stored procedure in the stored procedure's source code to identify any optional arguments (those with default values): the generated code allows those to be omitted in the call to execute the stored procedure.
Invoking the generated code goes like this:
public DataTable GetRiskFactorsForPatient( int patientID )
{
dbo_GetRiskbyPatient sp = new dbo_GetRiskbyPatient( CONNECT_STRING_ID ) ;
int rc = sp.Exec( patientID ) ;
DataTable dt = sp.ResultSet ;
if ( dt == null ) throw new InvalidOperationException( "nothing returned from stored procedure" ) ;
return dt ;
}
Personally, i prefer
void RunStoredProc1(object arg1)
{
try
{
using(SqlConnection conn = new SqlConnection(connStr))
{
using SqlCommand cmd = new SqlCommand("storedProc1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#input1", arg1);
conn.Open();
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
//handle the exception appropriately.
}
}
Over the traditional try catch finally that you would need to do to manage your resources.
But overall, I like doing it with separate methods, so that you can custom tailor your catch blocks for the sproc.
Also, You might need more than one parameter down the road, and you would just be making a mess of a fairly straight-forward function