I am calling a function as:
string judge1 = abs.getjud1(this.HiddenField4, this.TextBox3);
The function being called is:
public string getjud1(HiddenField HiddenField4, TextBox TextBox3)
{
String dbDate = DateTime.ParseExact(TextBox3.Text, "dd/mm/yyyy", null).ToString("yyyy-mm-dd");
try
{
OdbcConnection casetype = new OdbcConnection("Driver={MySQL ODBC 3.51 Driver};Server=10.155.160.130;Database=testcase;User=root;Password=;Option=3;");
casetype.Open();
//*********to get jud1
string jud1query = "select jname from testcase.orddetpabak,testcase.judge where orddetpabak.jud1 = judge.jcode and fil_no=? and orderdate=?;";
//*********to get jud1
OdbcCommand jud1cmd = new OdbcCommand(jud1query, casetype);
jud1cmd.Parameters.AddWithValue("?", HiddenField4.Value);
jud1cmd.Parameters.AddWithValue("?", dbDate);
using (OdbcDataReader jud1MyReader = jud1cmd.ExecuteReader())
{
while (jud1MyReader.Read())
{
judge1 = jud1MyReader["jname"].ToString();
Globals.jjj1= "J";
}
}
}
catch (Exception ep)
{ }
return judge1;
}
I want to return judge1 and Globals.jjj1, is it possible to do that? If so than how to do it?
Return a Tuple
http://msdn.microsoft.com/en-us/library/system.tuple.aspx
Not quite sure what you mean, but if you want to return multiple values, you can use the out or ref keywords.
Out keyword
Ref keyword
void SomeFunction()
{
int value1;
int value2;
value1 = SomeOtherFunction(out value2);
//Value1 now equals 21, value2 equals 25.
//You can use the same thing for strings.
}
int SomeOtherFunction(out int value2)
{
value2 = 25;
return 21;
}
Create an object with the 2 string values that represent the values you want and then return the object.
public MyCustomObject getjud1(HiddenField HiddenField4, TextBox TextBox3)
You can only return one object from your method, but you have two options here:
Make a new object that contains two strings, and return this object from the method instead of the string. This way, your composite object will contain the values you want.
Make a new parameter that you mark as out, and set the value of that parameter inside the method. The variable you send into the method as a parameter will then be updated outside the method as well.
Since C# is an object oriented language, why not simply make an object that contains all the values that you want to return? That way you only have one variable for your return statement, but you have access to all the values that you require.
Related
I have this method:
public void UpdatePhrase(PHRASE phraseColumn, bool value, string phraseId)
{
sql = string.Format("UPDATE Phrase SET " + phraseColumn.Text() + " = {0} WHERE PhraseId = '{1}'", value, phraseId);
App.DB.RunExecute(sql);
}
It's not working correctly as it seems like I need the value of {0} needs to be a 0 or a 1.
Is there a simple way that I can take value and change it to be a 0 or a 1?
Just do value ? 1 : 0, it's that easy!
#Sean has given to you the natural fix to your problem, but, in my view, what you really need to do here is to refactor your App.Db.RunExecute to receive parameters, so you can write
public void UpdatePhrase(PHRASE phraseColumn, bool value, string phraseId)
{
sql = "UPDATE Phrase SET " + phraseColumn.Text() + " = #v WHERE PhraseId = #id";
List<SqlParameter> prms = new List<SqlParameter>
{
new SqlParameter {ParameterName = "#v", SqlDbType = SqlDbType.Boolean, Value = value},
new SqlParameter {ParameterName = "#id", SqlDbType = SqlDbType.NVarChar, Value = phraseId}
};
App.DB.RunExecute(sql, prms);
}
This will partially remove the Sql Injection problem (I say partially because that phraseColumn.Text() is still source of concerns if its value comes from the user input)
Now RunExecute should change to
void RunExecute(string sqlCommand, List<SqlParameter> prms = null)
{
// usual code to open connection and create a command
......
// If the caller passes a parameters list, add them to the command
if(prms != null)
cmd.Parameters.AddRange(prms.ToArray());
// Now execute the command
cmd.ExecuteNonQuery();
}
The change to RunExecute uses an optional argument, so your current code is not affected by the presence of the new argument but you will be able to write better Sql code from now on.
value ? 1 : 0
as in #Sean answer or
Convert.ToInt32(value)
ToInt32(Boolean) Converts the specified Boolean value to the
equivalent 32-bit signed integer.
You can use the int count = updateCommand.ExecuteNonQuery; so if count>0, it means our mission is done 1 (True) , otherwise 0 (False)
updateCommand.Parameters.AddWithValue("#Var" , "Johne");
try
{
connection.Open();
int count = updateCommand.ExecuteNonQuery;
if (count > 0)
return true;
else
return false;
}
catch (SqlException ex)
{
throw ex;
}
finally
{
connection.Close();
}
}
I hope you got the point ^_^.
you can do (GetHashCode on booleans returns a constant 1/0, no calculation):
true.GetHashCode() // 1
false.GetHashCode() // 0
public object VerySimplyCompactFastTrueBestConvertObjectToInt(object whatNeedToConvertBro, object whatTypeIsYoNeedBro)
{
var typeOfConvertingObject = whatNeedToConvertBro.GetType();
var objWhatWillBeaReturnedInOut = new object();
if ((typeOfConvertingObject == whatTypeIsYoNeedBro.GetType()) == true)
{
objWhatWillBeaReturnedInOut = Convert.GetTypeCode(whatTypeIsYoNeedBro);
}
return objWhatWillBeaReturnedInOut;
}
}
public void UpdatePhrase(PHRASE phraseColumn, bool value, string phraseId)
{
sql = string.Format("UPDATE Phrase SET {phraseColumn} = {value ? 1 : 0} WHERE PhraseId = '{phraseId}'");
App.DB.RunExecute(sql);
}
But that can be subject to SQL injection attacks, so better to write it as a parameterised query.
For scenarios needing extreme performance, you can use the System.Runtime.CompilerServices.Unsafe package to perform this conversion directly inline. This will result in native code that contains no branching or function calls.
using System.Runtime.CompilerServices;
/// ...
bool _bool;
byte _byte;
_bool = true;
_byte = Unsafe.As<bool, byte>(ref _bool);
Console.WriteLine(_byte); // --> 1
_bool = false;
_byte = Unsafe.As<bool, byte>(ref _bool);
Console.WriteLine(_byte); // --> 0
You can also use this method to set up a ref local byte variable which existentially represents the instantaneous identity of the Boolean, but viewed as a byte. This might be useful for persistently monitoring a changing { false, true } using a { 0, 1 } interpretation.
class Program
{
static bool _bool;
static void Main()
{
Task.Factory.StartNew(() => { while (true) _bool = !_bool; });
// define a "ref local" that persistently views Boolean value '_bool' as a byte
ref byte _byte = ref Unsafe.As<bool, byte>(ref _bool);
while (true)
Console.Write(_byte + " ");
}
};
If you're unfamiliar with ref, with this technique just shown there is no "machinery" involved in any sort of "updating" or "conversion" whenever the value changes, because the ref byte is actually just a different "view" of the original Boolean entity itself.
The output is somewhat mesmerizing.
I'm using a SQLdatareader to build POCOs from a database. The code works except when it encounters a null value in the database. For example, if the FirstName column in the database contains a null value, an exception is thrown.
employee.FirstName = sqlreader.GetString(indexFirstName);
What is the best way to handle null values in this situation?
You need to check for IsDBNull:
if(!SqlReader.IsDBNull(indexFirstName))
{
employee.FirstName = sqlreader.GetString(indexFirstName);
}
That's your only reliable way to detect and handle this situation.
I wrapped those things into extension methods and tend to return a default value if the column is indeed null:
public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
if(!reader.IsDBNull(colIndex))
return reader.GetString(colIndex);
return string.Empty;
}
Now you can call it like this:
employee.FirstName = SqlReader.SafeGetString(indexFirstName);
and you'll never have to worry about an exception or a null value again.
You should use the as operator combined with the ?? operator for default values. Value types will need to be read as nullable and given a default.
employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);
The as operator handles the casting including the check for DBNull.
employee.FirstName = sqlreader[indexFirstName] as string;
For integers, if you cast to a nullable int, you can use GetValueOrDefault()
employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();
or the null-coalescing operator (??).
employee.Age = (sqlreader[indexAge] as int?) ?? 0;
IsDbNull(int) is usually much slower than using methods like GetSqlDateTime and then comparing to DBNull.Value. Try these extension methods for SqlDataReader.
public static T Def<T>(this SqlDataReader r, int ord)
{
var t = r.GetSqlValue(ord);
if (t == DBNull.Value) return default(T);
return ((INullable)t).IsNull ? default(T) : (T)t;
}
public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
var t = r.GetSqlValue(ord);
if (t == DBNull.Value) return null;
return ((INullable)t).IsNull ? (T?)null : (T)t;
}
public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
var t = r.GetSqlValue(ord);
if (t == DBNull.Value) return null;
return ((INullable)t).IsNull ? null : (T)t;
}
Use them like this:
var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);
if(reader.IsDBNull(ColumnIndex)) {// logic} works as many answers says.
And I want to mention if you working with column names, just comparing types may be more comfortable.
if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
I don't think there's a NULL column value, when rows are returned within a datareader using the column name.
If you do datareader["columnName"].ToString(); it will always give you a value that can be a empty string (String.Empty if you need to compare).
I would use the following and wouldn't worry too much:
employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
You can write a Generic function to check Null and include default value when it is NULL. Call this when reading Datareader
public T CheckNull<T>(object obj)
{
return (obj == DBNull.Value ? default(T) : (T)obj);
}
When reading the Datareader use
while (dr.Read())
{
tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon();
Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]);
Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]);
Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]);
}
One way to do it is to check for db nulls:
employee.FirstName = (sqlreader.IsDBNull(indexFirstName)
? ""
: sqlreader.GetString(indexFirstName));
This Solution is less vendor-dependent and works with an SQL, OleDB, and MySQL Reader:
public static string GetStringSafe(this IDataReader reader, int colIndex)
{
return GetStringSafe(reader, colIndex, string.Empty);
}
public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue)
{
if (!reader.IsDBNull(colIndex))
return reader.GetString(colIndex);
else
return defaultValue;
}
public static string GetStringSafe(this IDataReader reader, string indexName)
{
return GetStringSafe(reader, reader.GetOrdinal(indexName));
}
public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue)
{
return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue);
}
What I tend to do is replace the null values in the SELECT statement with something appropriate.
SELECT ISNULL(firstname, '') FROM people
Here I replace every null with a blank string. Your code won't throw in error in that case.
By influencing from getpsyched's answer, I created a generic method which checks column value by its name
public static T SafeGet<T>(this System.Data.SqlClient.SqlDataReader reader, string nameOfColumn)
{
var indexOfColumn = reader.GetOrdinal(nameOfColumn);
return reader.IsDBNull(indexOfColumn) ? default(T) : reader.GetFieldValue<T>(indexOfColumn);
}
Usage:
var myVariable = SafeGet<string>(reader, "NameOfColumn")
Check sqlreader.IsDBNull(indexFirstName) before you try to read it.
As an addition to the answer by marc_s, you can use a more generic extension method to get values from the SqlDataReader:
public static T SafeGet<T>(this SqlDataReader reader, int col)
{
return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
}
how to about creating helper methods
For String
private static string MyStringConverter(object o)
{
if (o == DBNull.Value || o == null)
return "";
return o.ToString();
}
Usage
MyStringConverter(read["indexStringValue"])
For Int
private static int MyIntonverter(object o)
{
if (o == DBNull.Value || o == null)
return 0;
return Convert.ToInt32(o);
}
Usage
MyIntonverter(read["indexIntValue"])
For Date
private static DateTime? MyDateConverter(object o)
{
return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
}
Usage
MyDateConverter(read["indexDateValue"])
Note: for DateTime declare varialbe as
DateTime? variable;
None of these was quite what i wanted:
public static T GetFieldValueOrDefault<T>(this SqlDataReader reader, string name)
{
int index = reader.GetOrdinal(name);
T value = reader.IsDBNull(index) ? default(T) : reader.GetFieldValue<T>(index);
return value;
}
I think you would want to use:
SqlReader.IsDBNull(indexFirstName)
We use a series of static methods to pull all of the values out of our data readers. So in this case we'd be calling DBUtils.GetString(sqlreader(indexFirstName)) The benefit of creating static/shared methods is that you don't have to do the same checks over and over and over...
The static method(s) would contain code to check for nulls (see other answers on this page).
You may use the conditional operator:
employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
There are a lot of answers here with useful info (and some wrong info) spread about, I'd like to bring it all together.
The short answer to the question is to check for DBNull - almost everyone agrees on this bit :)
Rather than using a helper method to read nullable values per SQL data type a generic method allows us to address this with a lot less code. However, you can't have a single generic method for both nullable value types and reference types, this is discussed at length in
Nullable type as a generic parameter possible? and C# generic type constraint for everything nullable.
So, following on from the answers from #ZXX and #getpsyched we end up with this, 2 methods for getting nullable values and I've added a 3rd for non-null values (it completes the set based on method naming).
public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}
public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}
public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}
I usually use column names, alter these if you use column indexes. Based on these method names I can tell whether I'm expecting the data to be nullable or not, quite useful when looking at code written a long time ago.
Tips;
Not having nullable columns in the database avoids this issue. If you
have control over the database then columns should be non-null by
default and only nullable where necessary.
Don't cast database values
with the C# 'as' operator because if the cast is wrong it will
silently return null.
Using a default value expression will change
database nulls to non-null values for value types like int, datetime,
bit etc.
Lastly, whilst testing the above methods across all SQL Server data types I discovered you can't directly get a char[] from a SqlDataReader, if you want a char[] you will have to get a string and use ToCharArray().
I am using the code listed below to handle null cells in an Excel sheet that is read in to a datatable.
if (!reader.IsDBNull(2))
{
row["Oracle"] = (string)reader[2];
}
private static void Render(IList<ListData> list, IDataReader reader)
{
while (reader.Read())
{
listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null;
//没有这一列时,让其等于null
list.Add(listData);
}
reader.Close();
}
and / or use ternary operator with assignment:
employee.FirstName = rdr.IsDBNull(indexFirstName))?
String.Empty: rdr.GetString(indexFirstName);
replace the default (when null) value as appropriate for each property type...
This method is dependent on indexFirstName which should be the zero-based column ordinal.
if(!sqlReader.IsDBNull(indexFirstName))
{
employee.FirstName = sqlreader.GetString(indexFirstName);
}
If you don't know the column index but wan't to check a name you can use this extension method instead:
public static class DataRecordExtensions
{
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i=0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
}
And use the method like this:
if(sqlReader.HasColumn("FirstName"))
{
employee.FirstName = sqlreader["FirstName"];
}
Old question but maybe someone still need an answer
in real i worked around this issue like that
For int :
public static object GatDataInt(string Query, string Column)
{
SqlConnection DBConn = new SqlConnection(ConnectionString);
if (DBConn.State == ConnectionState.Closed)
DBConn.Open();
SqlCommand CMD = new SqlCommand(Query, DBConn);
SqlDataReader RDR = CMD.ExecuteReader();
if (RDR.Read())
{
var Result = RDR[Column];
RDR.Close();
DBConn.Close();
return Result;
}
return 0;
}
the same for string just return "" instead of 0 as "" is empty string
so you can use it like
int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;
and
string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;
very flexible so you can insert any query to read any column and it'll never return with error
Here is helper class which others can use if they need based on #marc_s answer:
public static class SQLDataReaderExtensions
{
public static int SafeGetInt(this SqlDataReader dataReader, string fieldName)
{
int fieldIndex = dataReader.GetOrdinal(fieldName);
return dataReader.IsDBNull(fieldIndex) ? 0 : dataReader.GetInt32(fieldIndex);
}
public static int? SafeGetNullableInt(this SqlDataReader dataReader, string fieldName)
{
int fieldIndex = dataReader.GetOrdinal(fieldName);
return dataReader.GetValue(fieldIndex) as int?;
}
public static string SafeGetString(this SqlDataReader dataReader, string fieldName)
{
int fieldIndex = dataReader.GetOrdinal(fieldName);
return dataReader.IsDBNull(fieldIndex) ? string.Empty : dataReader.GetString(fieldIndex);
}
public static DateTime? SafeGetNullableDateTime(this SqlDataReader dataReader, string fieldName)
{
int fieldIndex = dataReader.GetOrdinal(fieldName);
return dataReader.GetValue(fieldIndex) as DateTime?;
}
public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName)
{
return SafeGetBoolean(dataReader, fieldName, false);
}
public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName, bool defaultValue)
{
int fieldIndex = dataReader.GetOrdinal(fieldName);
return dataReader.IsDBNull(fieldIndex) ? defaultValue : dataReader.GetBoolean(fieldIndex);
}
}
in c# 7.0 we can do :
var a = reader["ERateCode"] as string;
var b = reader["ERateLift"] as int?;
var c = reader["Id"] as int?;
so it will keep null value if it is.
I did my best to reimplement the Field method from DataTable. https://learn.microsoft.com/en-us/dotnet/api/system.data.datarowextensions.field
It will throw if you try to convert a DBNull.Value to a non-nullable type. Otherwise it will convert DBNull.Value to null.
I haven't fully tested it.
public static T Field<T>(this SqlDataReader sqlDataReader, string columnName)
{
int columnIndex = sqlDataReader.GetOrdinal(columnName);
if (sqlDataReader.IsDBNull(columnIndex))
{
if (default(T) != null)
{
throw new InvalidCastException("Cannot convert DBNULL value to type " + typeof(T).Name);
}
else
{
return default(T);
}
}
else
{
return sqlDataReader.GetFieldValue<T>(columnIndex);
}
}
Usage:
string fname = sqlDataReader.Field<string>("FirstName");
int? age = sqlDataReader.Field<int?>("Age");
int yearsOfExperience = sqlDataReader.Field<int?>("YearsEx") ?? 0;
Convert handles DbNull sensibly.
employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));
neat one-liner:
while (dataReader.Read())
{
employee.FirstName = (!dataReader.IsDBNull(dataReader.GetOrdinal("FirstName"))) ? dataReader["FirstName"].ToString() : "";
}
you can ever check for this as well
if(null !=x && x.HasRows)
{ ....}
I finally get it. It's not just the code I use to execute the ExecuteScalar method but it is mainly the code up stream that is executing the class. It is everything calling your code. That said, can someone please see if the code executing the my SQL class has faults. I still cant pass the scans. First I will show you two examples of the code calling my code, then the calling code, and finally the executing code, which I formulated and displayed from a previous post.
Calling code with three parameters:
public bool isTamAsp(int aspKey, int fy, string accountCode)
{
MyParam myParam;
string sqlQuery = "select isTamMacom = count(macom_key) FROM hier_fy " +
"WHERE hier_key = #aspKey AND fy = #fy AND #accountCode NOT IN (3,4,7,8) AND macom_key IN (select hier_key from lkup_e581_MacomThatRequireTAM) AND is_visible = 1 AND is_active = 1";
QueryContainer Instance = new QueryContainer(sqlQuery);
myParam = new MyParam();
myParam.SqlParam = new SqlParameter("#aspKey", Instance.AddParameterType(_DbTypes.Int));
myParam.SqlParam.Value = aspKey;
Instance.parameterList.Add(myParam);
myParam = new MyParam();
myParam.SqlParam = new SqlParameter("#fy", Instance.AddParameterType(_DbTypes.Int));
myParam.SqlParam.Value = fy;
Instance.parameterList.Add(myParam);
myParam = new MyParam();
myParam.SqlParam = new SqlParameter("#accountCode", Instance.AddParameterType(_DbTypes._string));
myParam.SqlParam.Value = accountCode;
Instance.parameterList.Add(myParam);
if (Convert.ToInt32(ExecuteScaler(Instance)) < 1)
return false;
return true;
}
Calling code with no parameters:
public long GetMarinesUploadNextUploadKey()
{
string query = "SELECT MAX(upload_key) FROM temp_auth_usmc_upload";
QueryContainer Instance = new QueryContainer(query);
string result = Convert.ToString(ExecuteScaler(Instance));
if (string.IsNullOrEmpty(result))
return 1;
else
return Convert.ToInt64(result) + 1;
}
Code calling my previous code with three parameters:
public bool isTamAsp(int aspKey, int fy, string accountCode)
{
return e581provider.isTamAsp(aspKey, fy, accountCode);
}
Method calling the SQL executing my code:
DbCommand command = _provider.CreateCommand();
command.Connection = _connection;
{
command.CommandText = Instance.Query;
command.CommandType = CommandType.Text;
if (Instance.parameterList.Count > 0)
{
foreach (var p in Instance.parameterList)
{
command.Parameters.Add(p.SqlParam);
}
}
if (_useTransaction) { command.Transaction = _transaction; }
try
{
returnValue = command.ExecuteScalar();
}
My Class containing the SQL string and the cmd parameter List
public enum _DbTypes
{
Int = 1, _string = 2, _long = 3, _bool = 4, _DateTime = 5,
_decimal = 6, _float = 7, _short = 8, _bite = 9
}
public class MyParam
{
public SqlParameter SqlParam { get; set; }
}
/// <summary>
/// Summary description for QueryContainer SGH
/// </summary>
public class QueryContainer
{
string _query;
public List<MyParam> parameterList = new List<MyParam>();
public QueryContainer(string query) { _query = query; }
public SqlDbType AddParameterType(_DbTypes id)
{
switch (id)
{
case _DbTypes.Int:
return (SqlDbType)Enum.Parse(typeof(SqlDbType), "int", true);
case _DbTypes._string:
return (SqlDbType)Enum.Parse(typeof(SqlDbType), "NVarChar", true);
case _DbTypes._long:
return (SqlDbType)Enum.Parse(typeof(SqlDbType), "SqlDbType.BigInt", true);
case _DbTypes._bool:
return (SqlDbType)Enum.Parse(typeof(SqlDbType), "SqlDbType.Bit", true);
}
return SqlDbType.VarChar;
}
public string Query
{
get
{
return _query;
}
set { _query = value; }
}
}
I don't see a vulnerability in that code, but I have an idea what the scan may be asking for. The problem could be that this code makes it too easy for developers to ignore the parameterList collection in your class. If I'm a new developer in your organization who hasn't discovered Sql injection yet, I'd be tempted to ignore all that complicated query parameter stuff and just use string concatenation before setting your Query property.
Instead of wrapping this in a class, what I'm more used to seeing is a single method that has a signature like this:
IEnumerable<T> GetData<T>(string query, IEnumerable<Sqlparameter> parameters)
...or some permutation of that method signature that may use arrays or lists instead of IEnumerable. This forces downstream developers to deal with that parameters argument to the method. They can't ignore it, and so the temptation to use a quick, lazy string concatenation call to substitute some user-provided data into the query is reduced.
I have a class with the following code
public cCase(string pCaseNo, string pMode)
{
if (pMode == "new")
{
this._caseNo = Validate_CaseNo(pCaseNo);
}
if (pMode == "existing")
{
try
{
int intValidatedCaseNo = Validate_CaseNo(pCaseNo);
string sqlText = "SELECT * FROM tblCases WHERE CaseNo = #CaseNo;";
string strConnection = cConnectionString.BuildConnectionString();
SqlConnection linkToDB = new SqlConnection(strConnection);
linkToDB.Open();
SqlCommand sqlCom = new SqlCommand(sqlText, linkToDB);
sqlCom.Parameters.Add("#CaseNo", SqlDbType.Int);
sqlCom.Parameters["#CaseNo"].Value = intValidatedCaseNo;
SqlDataReader caseReader = sqlCom.ExecuteReader();
if (caseReader.HasRows)
while (caseReader.Read())
{
this._claimant = caseReader["Claimant"].ToString();
this._defendant = caseReader["Defendant"].ToString();
this._caseType = caseReader["CaseType"].ToString();
this._occupation = caseReader["Occupation"].ToString();
this._doa = (DateTime?)caseReader["DOA"];
this._dateClosed = (DateTime?)caseReader["DateClosed"];
this._dateSettled = (DateTime?)caseReader["DateSettled"];
this._dateInstructed = (DateTime?)caseReader["DateInstructed"];
this._status = caseReader["Status"].ToString();
this._instructionType = caseReader["InstructionType"].ToString();
this._feeEstimate = (decimal?)caseReader["FeeEstimate"];
this._amountClaimed = (decimal?)caseReader["AmountClaimed"];
this._amountSettled = (decimal?)caseReader["AmountSettled"];
this._caseManager = caseReader["CaseManager"].ToString();
}
caseReader.Close();
linkToDB.Close();
linkToDB.Dispose();
}
catch (Exception eX)
{
throw new Exception("Error finding case" + Environment.NewLine + eX.Message);
}
}
}
However the Datetime? casts fail with an 'Invalid Cast'.
I've checked the SQL database and the field is storing valid dates
So I cant work out why, as I extract info via the DataReader into my app, the datetime fields are causing an Invalid Cast.
Please help.
You're going to want to change the line that reads:
this._doa = (DateTime?)caseReader["DOA"];
to:
if (caseReader["DOA"] != DBNull.Value)
this._doa.Value = (DateTime)caseReader["DOA"];
As well as all similar lines.
DBNull values cannot be casted from Nullable types.
Your DateTime fields probably hold a DBNull value which you cannot convert directly.
However, I'd use an extension method on your DataReader for convinience.
public static class DataReaderExtensions
{
public static DateTime? ReadNullableDateTime(this IDataReader reader, string column)
{
return reader.IsDBNull(column) ? (DateTime?)null : reader.GetDateTime(column);
}
}
// Usage
this._dateInstructed = CaseReader.ReadNullableDateTime("DateInstructed");
You should use
DateTime.TryParse Method
this not throw exception, like
var mydate =(DateTime)datetimeString
or
var mydate =DateTime.Parse(datetimeString)
does!!!
Try with the following code part
this._doa = (caseReader["DOA"] == DBNull.Value ? DBNull.Value : Convert.ToDateTime(caseReader["DOA"]);
Try to Convert Date Time as
this._doa = Convert.ToDateTime(caseReader["DOA"]);
I often deal with DBNull.Value...
So I use this method that will return the value of the object or the default value of the given value type if object's value is DBNull.Value.
public static object GetValueOrDefault(object value, Type type)
{
if (value != DBNull.Value)
return value;
if (type.IsValueType == false)
return null;
Array array = Array.CreateInstance(type, 1);
return array.GetValue(0);
}
Usage:
GetValueOrDefault(dataRecord.GetValue(fieldIndex), dataRecord.GetFieldType(fieldIndex)
I have a stored procedure call that goes like this:
using (OracleConnection con = new OracleConnection(ConfigurationManager.AppSettings["Database"]))
using (OracleCommand cmd = new OracleCommand("Package.Procedure", con))
{
Int32 existsCount;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("successCount", OracleDbType.Int32, 0, ParameterDirection.InputOutput);
cmd.Parameters.Add("BusinessId", OracleDbType.Int64, listRec.BusinessId, ParameterDirection.Input);
con.Open();
cmd.ExecuteScalar();
con.Close();
existsCount = Convert.ToInt32(cmd.Parameters["successCount"].Value);
return (existsCount);
}
But on this line:
existsCount = Convert.ToInt32(cmd.Parameters["successCount"].Value);
It throws the Exception "Unable to cast object of type 'Oracle.DataAccess.Types.OracleDecimal' to type 'System.IConvertible'."
Any thoughts? Thanks.
You can also try:
Oracle.DataAccess.Types.OracleDecimal d = (Oracle.DataAccess.Types.OracleDecimal)cmd.Parameters["successCount"].Value;
if( d.IsNull )
existsCount = 0;
else
existsCount = d.ToInt32( );
What about
existsCount = int.Parse(cmd.Parameters["successCount"].Value.ToString());
It is more efficient to use
Convert.ToInt32((decimal)(OracleDecimal)(cmd.Parameters["successCount"].Value))
I don't know the return type at runtime because the execution code is within a cross-platform data access framework under development, so I used a switch on the parameter value type to access the underlying Oracle[type].Value property for the various Oracle managed data access types.
public override object GetValue(IDataParameter parameter)
{
if (parameter == null)
{
throw new ArgumentNullException(nameof(parameter));
}
// https://docs.oracle.com/cd/B19306_01/win.102/b14307/OracleDbTypeEnumerationType.htm
if (parameter is OracleParameter)
{
switch (parameter.Value)
{
case OracleBinary oracleBinary:
// returns byte[]
return oracleBinary.Value;
case OracleBoolean oracleBoolean:
// returns bool
return oracleBoolean.Value;
case OracleDate oracleDate:
// returns DateTime
return oracleDate.Value;
case OracleDecimal oracleDecimal:
// oracleDecimal.Value is Decimal, so we convert to correct type.
return parameter.DbType == DbType.Decimal
? oracleDecimal.Value
: Convert.ChangeType(oracleDecimal.Value, parameter.DbType.ToType());
case OracleIntervalDS oracleIntervalDS:
// returns TimeSpan
return oracleIntervalDS.Value;
case OracleIntervalYM oracleIntervalYM:
// returns Long
return oracleIntervalYM.Value;
case OracleTimeStamp oracleTimeStamp:
// returns DateTime
return oracleTimeStamp.Value;
case OracleTimeStampLTZ oracleTimeStampLTZ:
// returns DateTime
return oracleTimeStampLTZ.Value;
case OracleTimeStampTZ oracleTimeStampTZ:
// returns DateTime
return oracleTimeStampTZ.Value;
default:
throw new NotSupportedException(
parameter.Value != null
? parameter.Value.GetType().Name
: parameter.ParameterName);
}
}
else
{
throw new NotSupportedException(parameter.GetType().Name);
}
}
In my case, I'm using Bulk Insert in Oracle, and meet the same error, let me share my solution here. I solved it by adding
oracleCommand.ArrayBindCount = datas.Count;
that is I forgot to set the ArrayBindCount property.
I suggest you convert to String, and after that you convert from String to Integer.
Dim tmpIdSesiónCalificación As String =
parametroIdSesiónCalificación.Value.ToString
_idSesiónCalificación = Convert.ToInt32(tmpIdSesiónCalificación)