DbDataReader, NextResult() and filling more than one table - c#

This question is continuation of my previous one. Without going into too much details, I'm filling dataset with 2 related 1-to-many tables.
So, my question now is - why this code works good
public DataAgencyR_DataSet SelectOne(int id)
{
DataAgencyR_DataSet result = new DataAgencyR_DataSet();
using (DbCommand command = Connection.CreateCommand())
{
try
{
command.CommandText = SqlStrings.SelectDataAgencyR_SelectOne();
var param = ParametersBuilder.CreateByKey(command, "ID_DeclAgenc", id, "ID_DeclAgenc");
command.Parameters.Add(param);
Connection.Open();
using (DbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
System.Diagnostics.Trace.WriteLine(String.Format("{0}-{1}", reader[0], reader[1]));
}
System.Diagnostics.Trace.WriteLine("-------------");
reader.NextResult();
while (reader.Read())
{
System.Diagnostics.Trace.WriteLine(String.Format("{0}-{1}", reader[0], reader[1]));
}
}
}
catch (DbException e)
{
Logger.Error(e.Message, e);
throw new DataAccessException("Error occurs while SelectOne method porcessed", e);
}
finally
{
if (Connection.State != ConnectionState.Closed) Connection.Close();
}
}
return result;
}
public static string SelectDataAgencyR_SelectOne()
{
return "SELECT a.* FROM t0_DataAgency_R a WHERE a.SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc;" +
"SELECT c.* FROM t01_ChoiceParam_R c JOIN t0_DataAgency_R a on a.ID_DeclAgenc = c.ID_DeclAgenc WHERE SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc";
}
and this is not
public DataAgencyR_DataSet SelectOne(int id)
{
DataAgencyR_DataSet result = new DataAgencyR_DataSet();
using (DbCommand command = Connection.CreateCommand())
{
try
{
command.CommandText = SqlStrings.SelectDataAgencyR_SelectOne();
var param = ParametersBuilder.CreateByKey(command, "ID_DeclAgenc", id, "ID_DeclAgenc");
command.Parameters.Add(param);
Connection.Open();
using (DbDataReader reader = command.ExecuteReader())
{
result.t0_DataAgency_R.Load(reader);
reader.NextResult();
result.t01_ChoiceParam_R.Load(reader);
}
}
catch (DbException e)
{
Logger.Error(e.Message, e);
throw new DataAccessException("Error occurs while SelectOne method porcessed", e);
}
finally
{
if (Connection.State != ConnectionState.Closed) Connection.Close();
}
}
return result;
}
public static string SelectDataAgencyR_SelectOne()
{
return "SELECT a.* FROM t0_DataAgency_R a WHERE a.SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc;" +
"SELECT c.* FROM t01_ChoiceParam_R c JOIN t0_DataAgency_R a on a.ID_DeclAgenc = c.ID_DeclAgenc WHERE SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc";
}
After second example, I have filled only result.t0_DataAgency_R table - but not result.t01_ChoiceParam_R. Why can it be so?
Thanks in advance

DataTable.Load automatically advances the reader to the next result. So you should remove your explicit call to NextResult.
Meaning:
using (DbDataReader reader = command.ExecuteReader())
{
result.t0_DataAgency_R.Load(reader);
result.t01_ChoiceParam_R.Load(reader);
}

Adding a DataSet to the mix... we used to use SqlDataAdapter and returned a DataSet but didn't take advantage of any of the offline features, etc., so a SqlDataReader is a better fit. Here's code to fill a DataSet. Found this was about 10% faster overall.
Dim s As DataSet = New DataSet()
Using reader As SqlDataReader = command.ExecuteReader()
Dim tables As New List(Of DataTable)
Do
Dim table As New DataTable()
table.Load(reader)
tables.Add(table)
s.Tables.Add(table)
Loop While Not reader.IsClosed
s.Load(reader, LoadOption.OverwriteChanges, tables.ToArray())
End Using

Related

SqlDataReader only reads partial data from JSON column

I have the following code but it only reads the last part of the JSON value:
public string GetUsersJson(long systemOrgId)
{
var query = #"DECLARE #OrgId bigint = #systemOrgId
SELECT e.OrgId, e.Id, e.FirstName, e.LastName
FROM [Internal].[Employee] e
WHERE OrgId = #OrgId and IsActive=1
FOR JSON PATH, ROOT('Users');";
var json = ExecuteSqlCommandWithJsonResponse(query, systemOrgId);
return json;
}
private string ExecuteSqlCommandWithJsonResponse(string queryString, long systemOrgId)
{
var result = "";
using (SqlConnection connection = new SqlConnection(_systemConnectionString))
{
using (var cmd = connection.CreateCommand())
{
connection.Open();
cmd.CommandText =queryString;
cmd.Parameters.AddWithValue("#systemOrgId", systemOrgId);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result = reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
}
}
}
return result;
}
If I use if instead I get the first part.
if (reader.Read())
{
result = reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
According to the SqlDataReader Class documentation a while (reader.Read()) should be used.
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqldatareader?view=dotnet-plat-ext-7.0#examples
Adapting the code to look more like the MS example also gives the same result:
private string ExecuteSqlCommandWithJsonResponse(string queryString, long systemOrgId)
{
var result = "";
using (SqlConnection connection = new SqlConnection(_systemConnectionString))
{
var cmd = connection.CreateCommand();
connection.Open();
cmd.CommandText = queryString;
cmd.Parameters.AddWithValue("#systemOrgId", systemOrgId);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
result = reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
reader.Close();
}
return result;
}
Using a standard concatenate strings with += easily solved it. I hope this can help someone else since this information is missing in the examples I have seen.
https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings#-and--operators
private string ExecuteSqlCommandWithJsonResponse(string queryString, long systemOrgId)
{
var result = "";
using (SqlConnection connection = new SqlConnection(_systemConnectionString))
{
using (var cmd = connection.CreateCommand())
{
connection.Open();
cmd.CommandText =queryString;
cmd.Parameters.AddWithValue("#systemOrgId", systemOrgId);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result += reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
}
}
}
return result;
}

How to get data in INT from SQLDataReader in .NET

I am trying to get int value from the database but It is throwing an error
Unable to cast object of type 'System.Byte' to type 'System.Int32'.
In the database, Active field is tinyint.
Also, how to return both values from this method.
private string CheckData(string firstValue, string SecondValue, int Active)
{
string Data = "";
StringBuilder sb = new StringBuilder();
string query = #"select M.ident Mi, mmp.active Active
from Iden.Iden M
inner join PtM.MPt MMP on MMP.mPat = M.id
where M.ident = 'firstValue'
and Mi.ident = 'SecondValue'";
sb.Append(query);
sb.Replace("firstValue", firstValue);
sb.Replace("SecondValue", SecondValue);
SqlConnection connection = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(sb.ToString());
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
try
{
connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Data = reader.GetString(reader.GetOrdinal("Mi"));
Active = reader.GetInt32(reader.GetOrdinal("Active"));
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
finally
{
connection.Close();
connection.Dispose();
}
return Data;
}
Here's a stab at it. I can't debug it (since I don't feel like creating a database).
First I create a type to hold the results. You could just use a Tuple, but this seems clearer:
public class DataActive
{
public string Data { get; set; }
public byte Active { get; set; }
}
I make your function return a collection of these - it's not obvious from your code that there is only one.
You'll also notice that I use SqlParameters to add firstValue and secondValue to your query. Look up SQL Injection (and Little Bobby Tables).
If you are using a recent version of C# (which I don't), there's a new syntax for using that requires less indenting. The using statements stick a call to Dispose in a finally statement at the end of the block. Also note that I'm disposing the SqlCommand and the DataReader
public static IEnumerable<DataActive> CheckData(string firstValue, string secondValue)
{
var results = new List<DataActive>();
const string query = #"select M.ident Mi,mmp.active Active from Iden.Iden M
Inner join PtM.MPt MMP on MMP.mPat =M.id
where M.ident = #firstValue and Mi.ident = #secondValue";
using (var connection = new SqlConnection(connString))
{
using (var cmd = new SqlCommand(query))
{
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.Parameters.Add("#firstValue", SqlDbType.NVarChar, 50).Value = firstValue;
cmd.Parameters.Add("#secondValue", SqlDbType.NVarChar, 50).Value = secondValue;
try
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
var dataOrdinal = reader.GetOrdinal("Mi");
var activeOrdinal = reader.GetOrdinal("Active");
if (reader.HasRows)
{
while (reader.Read())
{
results.Add(new DataActive
{
Data = reader.GetString(dataOrdinal),
Active = reader.GetByte(activeOrdinal),
});
}
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
}
}
return results;
}
If your TINY_INT Active represents a boolean value, figure out what the rule is, and do a conversion after you get the value using reader.GetByte.
One final note, it's often better to log ex.ToString() rather than ex.Message. You get the message and the stack that way.

SQLDataReader issue with reading null values

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;
}

C# asp.NET displaying output of a query to screen. Error: 'System.ArgumentException' in Oracle.DataAccess.dll

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;
}

execute reader doesn't respond

i have a php code.here is the code,i gonna to translate it to .NET but in some point i'm getting some trouble.
function processInput($conn, $MessageArray, $mobilenumber, $date, $odd)
{
$strSQLUSER="SELECT * FROM tbl_tiduser WHERE username='".addslashes($MessageArray[0])."' AND stat!='1' AND stat!='4'";
$result_user=odbc_exec($conn,$strSQLUSER) or die("Could not connect to database");
here is the converted .NET code
public class ProcessInput
{
private string msg_arr;
private string MooseSeenInput(string MobileNo,string Date,string odd,params Array[] msg_arr)
{
SqlCommand com = new SqlCommand("SELECT * FROM tbl_tiduser WHERE username=#username AND stat!='1' AND stat!='4'", mycon);
com.Parameters.AddWithValue("#username",username);
using (SqlDataReader reader = com.ExecuteReader())
// whats the next part need to come here ???
}
this is incomplete.i'm not going to compile it....
private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
// Call Close when done reading.
reader.Close();
}
}
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx
I would use something like this to get the column(s) you're after:
string username = null;
using (SqlDataReader reader = com.ExecuteReader()) {
if (reader.read()) {
username = (string)reader["mydbcolumnname"];
}
reader.Close();
}
Note that if you want to pull all the result rows (as opposed to stepping through them) then you'd normally use a SqlDataAdapter to fill a DataSet (instead of the reader), eg:
string username;
using (SqlDataAdapter adapter = new SqlDataAdapter(com))
{
using (DataSet ds)
{
adapter.Fill(ds);
username = (string)ds.Tables[0].Rows[0]["mycolumnname"];
}
}
I'm all for easy; I would write a class that mirrors the record I'm reading, i.e.
public class User {
public int Id {get;set;}
public string Name {get;set;}
}
and use "dapper":
var user = myCon.Query<User>(
"SELECT * FROM tbl_tiduser WHERE username=#username AND stat not in ('1','4')",
new {username}).SingleOrDefault();
if(user == null) { /* not found, presumably throw an exception */ }
string name = user.Name; // etc
Then you don't need to mess with commands, readers, parameters etc (see how the username is being made into a db parameter cleanly?).

Categories

Resources