I am trying to implement prepared staments in my code as a way of adding parameters to sql commands that are retrieved from a table held in any generic server. I cannot seem to get it right. I get the following error:
ORA-00936: missing expression
ORA-00936: missing expression
Prepare Statement: select VALUE from RWOL_CONFIGURATION where ID = #ItemId
My guess is that it just isn't replacing the value but I dont know what I am missing.
I am trying the following to achieve the desired result. I create my object, get a query string out of our table in the database, add that to the command, add parameters to a list object and then use the final method shown below to tie it all together and run the query:
//This function gets me a config item from the database
private string GetConfigurationItem(string itemId)
{
//new database connection object
OleDataBaseConnection oleDataBaseConnection = new OleDataBaseConnection();
//todo get this query from the sql factory
SqlFactory sqlFactory = new SqlFactory();
//This method gets the query string from the database
string sqlQuery = sqlFactory.GetQueryString("GET_CONFIGURATION_ITEM", m_dialect);
if (!String.IsNullOrEmpty(sqlQuery))
{
//add parameter to list
oleDataBaseConnection.AddStoredProcedureParameter("#ItemId", itemId);
//execute the sql command after adding the parameters to the command
oleDataBaseConnection.OleExecutePrepareStatementWithParametersQuery(sqlQuery);
string returnValue = oleDataBaseConnection.NextRecord() ? oleDataBaseConnection.GetFieldById(0) : "Error";
oleDataBaseConnection.Close();
return returnValue;
}
else
{
return "ERROR";
}
}
//adds the parameters to list objects ready for the next method
public void AddParameter(string parameter, object value)
{
m_parameterName.Add(parameter);
m_parameterValue.Add(value);
} // End of void AddParameter()
/// <summary>
/// Executes a command with the parameters passed to AddParameter(parameterName, parameterValue) and creates a recordset.
/// </summary>
///
/// <param name="commandName">The name of the stored procedure to execute.</param>
public bool OleExecutePrepareStatementWithParametersQuery(string commandName)
{
if (String.IsNullOrEmpty(commandName))
{
return false;
}
try
{
PrepareConnection();
m_oleDatabaseCommand.CommandText = commandName;
m_oleDatabaseCommand.CommandType = CommandType.StoredProcedure;
if (m_storedProcedureParameterName.Count != 0)
{
for (int i = 0; i < m_storedProcedureParameterName.Count; i++)
{
m_oleDatabaseCommand.Parameters.AddWithValue(m_storedProcedureParameterName[i], m_storedProcedureParameterValue[i]);
}
m_storedProcedureParameterName.Clear();
m_storedProcedureParameterValue.Clear();
}
m_hasRecordSet = true;
m_oleDatabaseDataReader = m_oleDatabaseCommand.ExecuteReader();
return true;
}
catch (Exception ex)
{
if (QueueErrors)
{
QueuedErrorsList.AppendLine(ex.Message);
QueuedErrorsList.AppendLine("Prepare Statement: " + storedProcedureName);
QueuedErrorsList.AppendLine();
QueuedErrorCount++;
return false;
}
try
{
Close();
}
catch
{
}
throw new Exception(ex.Message + "\r\n\r\nPrepare Statement: " + storedProcedureName);
}
} // End of void OleExecutePrepareStatementWithParametersQuery()
Sorry if there is a lot of code but it is fairly straightforward and I thought it would help with the problem.
Is there anything obvious that would stop this from working?
The problem is that the OleDB provider does not support named parameters in the query.
This:
select VALUE from RWOL_CONFIGURATION where ID = #ItemId
Should be:
select VALUE from RWOL_CONFIGURATION where ID = ?
See OleDbParameter on MSDN for examples.
Related
I have created a simplified SQL Data class, and a class method for returning a ready to use resultset:
public SQL_Data(string database) {
string ConnectionString = GetConnectionString(database);
cn = new SqlConnection(ConnectionString);
try {
cn.Open();
} catch (Exception e) {
Log.Write(e);
throw;
}
}
public SqlDataReader DBReader(string query) {
try {
using (SqlCommand cmd = new SqlCommand(query, this.cn)) {
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
} catch {
Log.Write("SQL Error with either Connection String:\n" + cn + " \nor Query:\n" + query);
throw;
}
}
(I catch any errors, log them, and then catch the error higher up the chain. Also, I did not include the ConnectionString() code for brevity. It just returns the requested connection string. That's all.)
This all works just fine, and with a single line of code, I'm ready to .Read() rows.
SqlDataReader rs = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees");
while (rs.Read()) {
// code
}
rs.Close();
I want to expand this and add a .ColumnReader() method that I want to chain to .DBReader() like this:
string empID = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees).ColumnReader("EmpID");
I attempted this by adding a .ColumnReader() method, but it ends up being a method of SQL_Data() class directly, not a member or extension of .DBReader(). I also tried adding the .ColumnReader() inside the .DBReader() (like a "closure"), but that didn't work either.
Can this be done?
This ended up working for me:
public static class SQLExtentions {
public static dynamic ColumnReader(this SqlDataReader rs, string colName) {
return rs[colName];
}
}
I will have to expand on it a bit to add some error checking, and perhaps return more than just the dynamic value - like return an object with the value and it's SQL data type. But Paul and Bagus' comments got me on the right track.
I am having an issue reading Calls from my Call Table. One of my columns is a DateTime column, and this column I have allowed to be null, if the call has not been closed. When I execute my C# code that is to fetch all calls from the Calls table, I get an error that the data is null, cannot read when no data is present. I thought If I had an If statement that checks if that column is null, that would solve the issue... but that doesn't work.
Here is the code where I am experiencing the problem:
public void GetAllCalls(Connection connectionObject)
{
try
{
if (memoryObject.GetCallCount() != 0)
{
memoryObject.DeleteAllCalls();
}
SqlCommand getCalls = new SqlCommand("sp_getAllCalls", connectionObject.getDatabaseConnection());
getCalls.CommandType = CommandType.StoredProcedure;
dataReader = getCalls.ExecuteReader();
if (dataReader.HasRows)
{
while (dataReader.Read())
{
if (dataReader.GetDateTime(8).Equals(DBNull.Value))
{
Call newCall = new Call(dataReader.GetString(0), dataReader.GetString(1), dataReader.GetString(2),
dataReader.GetString(3), dataReader.GetString(4), dataReader.GetString(5),
dataReader.GetString(6), dataReader.GetDateTime(7), Convert.ToDateTime(null),
dataReader.GetString(9), dataReader.GetString(10), dataReader.GetString(11),
dataReader.GetString(12));
memoryObject.AddCall(newCall);
}
else
{
Call newCall = new Call(dataReader.GetString(0), dataReader.GetString(1), dataReader.GetString(2),
dataReader.GetString(3), dataReader.GetString(4), dataReader.GetString(5),
dataReader.GetString(6), dataReader.GetDateTime(7), dataReader.GetDateTime(8),
dataReader.GetString(9), dataReader.GetString(10), dataReader.GetString(11),
dataReader.GetString(12));
memoryObject.AddCall(newCall);
}
}
dataReader.Close();
}
else
{
dataReader.Close();
functionsObject.displayMessage("No Calls Found", "No calls were found on the system");
}
}
catch (Exception error)
{
logNewError(error, "GetAllCalls", connectionObject);
}
}
The correct way of checking that is using the SqlDataReader.IsDBNull method
if (dataReader.IsDBNull(8))
I've revised my code and question to better reflect what I'm trying to accomplish.
Background: I have different layer interfaces as part of my project.
Service Layer - handles my business logic, validates entries, (the brains)
Data Access Layer - simply executes the methods or functions it is passed
Aspx & aspx.cs files where the methods need to take place (i.e the user interface)
Here is my code for the ConnectionTypeSetup.aspx.cs file, I've also marked the line where there is an error:
protected void uxSaveBtn_Click(object sender, EventArgs e)
{
var accountTrackersvc = new AccountTrackerSvc();
//Insert or update record
var result = ViewState["ConnectionTypeID"] == null ?
accountTrackersvc.InsertConnectionType(uxConnectionTypeDescTxt.Text.Trim(),
CommonSVC.GetUserInfoFormattedFromSession())
/*Error on this line */ : accountTrackersvc.UpdateConnectionType(DataConverter.StringToInteger(ViewState["ConnectionTypeID"].ToString()),
uxConnectionTypeDescTxt.Text.Trim(),
Enums.GetIsDisabledByItemStatusValue(SafeValueAccessor.GetControlValue(uxStatusDdl)),
CommonSVC.GetUserInfoFormattedFromSession(),"Default",false);
//Check result
if(result.Successful)
{
uxInfoMsg.DisplayMessage(result.Message, InfoMessage.InfoMessageType.Success);
BindGridContent();
uxPopupMdl.Hide();
}
else
{
uxModalInfoMsg.DisplayMessage(result.Message, InfoMessage.InfoMessageType.Failure);
uxPopupMdl.Show();
}
// Hide progress indicator
Master.HideProgressIndicator();
The service layer which again handles my business logic is formatted as follows. Please note there are 2 separate methods being used and Insert and Update:
public BO.OperationResult InsertConnectionType(string connectionTypeDesc, string createdBy)
{
var operationResult = new BO.OperationResult();
// connection type description required
if (connectionTypeDesc.Trim().Length <= 0)
{
operationResult.Successful = false;
operationResult.Message += "Connection type description is required";
}
//Createdby required
if (createdBy.Trim().Length <= 0)
{
operationResult.Successful = false;
operationResult.Message += "A record has not been saved in the form this entry was created by";
}
if (operationResult.Successful)
{
operationResult.DBPrimaryKey = new DAL.AccountTrackerDAL().InsertConnectionType(connectionTypeDesc.Trim(), createdBy);
operationResult.Message = "Account Access Level Saved Successfully";
}
return operationResult;
}
2nd Business logic Method and code for update:
public BO.OperationResult UpdateConnectionType(int connectionTypeID, string connectionTypeDesc,bool isDisabled,string lastUpdatedBy)
{
var operationResult = new BO.OperationResult();
if (connectionTypeDesc.Trim().Length <= 0)
{
operationResult.Successful = false;
operationResult.Message += "Connection Type Description has not successfully updated.";
}
if (lastUpdatedBy.Trim().Length <= 0)
{
operationResult.Successful = false;
operationResult.Message += "Last updated by must be entered.";
}
if (operationResult.Successful)
{
operationResult.DBPrimaryKey = new DAL.AccountTrackerDAL().UpdateConnectionType(connectionTypeID, lastUpdatedBy, connectionTypeDesc, isDisabled);
operationResult.Message = "Account Access Level Saved Successfully";
}
return operationResult;
}
Lastly, I'll only include the method signitures for the DAL layer as I think that should be enough and not saturate this question with code.
Update ConnectionType
public int UpdateConnectionType(int connectionTypeID, string lastUpdatedBy, string connectionTypeDesc, bool isDisabled)
Insert ConnectionType
public int InsertConnectionType(string connectionTypeDesc, string createdBy)
My current error reads: No overload for method UpdateConnectionType takes 6 arguments. I've tried to default the values only to receive this error. Any feedback would be appreciated, thanks!
When you call InsertConnectionType, you MUST provide four (4) parameters. That is how the method is written, so that is what you must do:
accountTrackersvc.InsertConnectionType(
uxConnectionTypeDescTxt.Text.Trim(),
CommonSVC.GetUserInfoFormattedFromSession(),
"Default", false)
The parameters above would pass the compiler.
If you absolutely insist on using only two (2) parameters, you could create an overload method:
public BO.OperationResult InsertConnectionType(string connectionTypeDesc, int connectionTypeID)
{
return InsertConnectionType(connectionTypeDesc, connectionTypeID, "Default", false);
}
UPDATE
To add an overload for your UpdateConnectionType method, try something like this:
public BO.OperationResult UpdateConnectionType(int connectionTypeID, string connectionTypeDesc)
{
var operationResult = new BO.OperationResult();
if (connectionTypeDesc.Trim().Length <= 0)
{
operationResult.Successful = false;
operationResult.Message += "Connection Type Description has not successfully updated.";
}
if (operationResult.Successful)
{
operationResult.DBPrimaryKey = new DAL.AccountTrackerDAL().UpdateConnectionType(connectionTypeID, "Default", connectionTypeDesc, false);
operationResult.Message = "Account Access Level Saved Successfully";
}
return operationResult;
}
Of course, make sure you replace the text "Default" and the Boolean value false with whatever is appropriate for your class.
In short I am writing a class handler to handle to database integration of some software I am writing for myself, however as there is not always a connection to the remote database I thought I would use SQLCE to create a local database buffer so when a connection is made the changes can be synchronized.
So far it is going well except for the parameters. The function I am looking to call is shown below however this function is complaining about invalid arguments.
public Object run(string query, List<Object> dbparams = null)
{
if (MyDB.isConnected())
{
return MyDB.run(query, dbparams);
}
else
{
SqlCeCommand sql = _OfflineConnection.CreateCommand();
sql.CommandText = query;
if (dbparams.Count > 0)
{
sql.Parameters.AddRange(dbparams.ToArray());
}
return sql;
}
}
MyDB.run is the exact same code as in the else statement except for mysql, the line that it is moaning about is the return mydb.run as the mydb class is expecting the dbparams list to be of mysqlparameters.
Does anyone know how I can achieve this? I attempted to use LINQ to do a convert but that failed miserably.
EDIT
At present I have the following working but I am sure there is a cleaner solution
public Object run(string query, List<Object> dbparams = null)
{
if (MyDB.isConnected())
{
List<MySqlParameter> mydbparams = null;
for (int i = 0; i < dbparams.Count; i++)
{
mydbparams.Add((MySqlParameter)dbparams[i]);
}
return MyDB.run(query, mydbparams);
}
else
{
SqlCeCommand sql = _OfflineConnection.CreateCommand();
sql.CommandText = query;
if (dbparams.Count > 0)
{
sql.Parameters.AddRange(dbparams.ToArray());
}
return sql;
}
}
A bit cleaner solution would be
mydbparams = dbparams.Cast<MySqlParameters>().ToList();
Also, you should check for and handle the null condition of dbparams.
I want to run a sp from my code. the sp name stored in a db. I pass the parameters via a dictionary. this code is one of my wcf service methods that host in a windows service. my code is :
private DataSet RunReport(int id, ParameterDictionary parametersDic)
{
DataSet result = new DataSet();
try
{
DataSet report = GetReportDataset(id);
if (report.Tables[0].Rows.Count > 0)
{
string reportType = GetReportTypeDataset(Convert.ToInt32(report.Tables[0].Rows[0]["ReportTypeID"]));
if (reportType != ReportType.StoredProcedure.ToString())
{
throw new Exception("your report is not a sp report.");
}
using (DbCommand paramCommand = DatabaseManager.Database.GetStoredProcCommand(report.Tables[0].Rows[0]["SQLQuery"].ToString()))
{
foreach (var parameter in parametersDic)
{
DatabaseManager.Database.SetParameterValue(paramCommand, parameter.Key, parameter.Value);
}
result = DatabaseManager.Database.ExecuteDataSet(paramCommand);
result.AcceptChanges();
}
}
}
catch (Exception ex)
{
throw ex;
}
return result;
}
When I run this code the below error is accured:
An SqlParameter with ParameterName 'ID' is not contained by this
SqlParameterCollection.
In addition, when I call this method via win forms every thing is ok.
what is the problem?
I think you may have to add the parameters before setting them:
foreach param ...
{
DatabaseManager.Database.AddInParameter(paramCommand, <name>, <DbType>);
DatabaseManager.Database.SetParameterValue(paramCommand, parameter.Key, parameter.Value);
}