Two queries to two lists? - c#

At the moment, I have this which works fine:
using (connection = new SqlConnection("connection string here"))
{
using (command = new SqlCommand(#"select * from tbl1", connection))
{
connection.Open();
using (reader = command.ExecuteReader())
{
while (reader.Read())
{
int ColIndex1 = reader.GetOrdinal("col_1");
int ColIndex2 = reader.GetOrdinal("col_2");
Console.Write(reader.GetString(ColIndex1);
Console.Write(" - ");
Console.Write(reader.GetString(ColIndex2);
Console.Write(Environment.NewLine);
}
}
}
}
I have another query which I run separately, but that second query needs the first query, which means I end up running the first query twice. To avoid that, if I changed the command line to:
using (command = new SqlCommand(#"select * from tbl1; select * from tbl2", connection))
How do I get each query into a separate list? I understand how to get a single query into a list, i.e:
public class Data
{
public int ColumnIndex1 { get; set; }
public int ColumnIndex2 { get; set; }
}
List<Data> list = new List<Data>();
list.Add(new Data(ColIndex1, ColIndex2));
The first query is used to create directories on the hard drive. The second query then uses the first query and then adds files to the created directories.

using (connection = new SqlConnection("connection string here"))
{
using (command = new SqlCommand(#"select * from tbl1", connection))
{
connection.Open();
using (reader = command.ExecuteReader())
{
while (reader.Read())
{
// read first grid
}
if(reader.NextResult())
{
while (reader.Read())
{
// read second grid
}
}
}
}
}
However, I strongly suggest using helper tools, for example, via "dapper":
List<FirstType> first;
List<FirstType> second;
using(var multi = connection.QueryMultiple(sql, args))
{
first = multi.Read<FirstType>().ToList();
second = multi.Read<SecondType>().ToList();
}

I think you need to investigate the NextResult method on the IDataReader interface. This allows you to move through multiple result sets.
http://msdn.microsoft.com/en-us/library/system.data.idatareader.nextresult(v=vs.110).aspx

Related

Loop on field change C#

I'm trying to write some code to loop through a data range and add new documents to SAP based on a query input. I need the values to be added to the documents based on the supplier field and when the supplier changes create a new document. Currently I am only able to loop through adding items to the document and rather than moving to the next supplier it just loops the items again. I'm pretty new to C# so looping is pretty new to me but hoping someone can help?
int recordCount = oRecordset.RecordCount;
string Supplier = oRecordset.Fields.Item(1).Value.ToString();
string Item = oRecordset.Fields.Item(0).Value.ToString();
Qty = Convert.ToInt32(oRecordset.Fields.Item(3).Value.ToString());
if(recordCount>0)
application.MessageBox("Adding PQ");
System.Threading.Thread.Sleep(2);
{
for(int i = 0; i < recordCount; i++)
{
OPQT.CardCode = Supplier ;
OPQT.DocDate = DateTime.Now;
OPQT.DocDueDate = DateTime.Now;
OPQT.RequriedDate = DateTime.Now;
OPQT.Lines.ItemCode = Item;
OPQT.Lines.RequiredQuantity = Qty;
OPQT.Lines.Add();
oRecordset.MoveNext();
}
OPQT.Add();
application.MessageBox("PQ Added");
}
You'd be much better of starting to learn with SqlDataReader, IMO.
Adapting to your business case you'd get something like this, this is not working code, I don't have enough info for that. However this should help you progress in the right direction.
using System;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = "Data Source=(local);Initial Catalog=...";
ReadOData( connectionString);
}
private static IEnumerable<OPQT> ReadOrderData(string connectionString)
{
string queryString =
#"
SELECT
T0.[ItemCode],
T0.[CardCode],
'10' [Qty]
FROM
[OSCN] T0
JOIN
[OCRD] T1
ON T0.[CardCode] = T1.[CardCode]
WHERE
T1.[CardType] ='S'";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
// Call Read before accessing data.
while (reader.Read())
{
yield return ReadSingleRow((IDataRecord)reader);
}
}
finally
{
// Call Close when done reading.
reader.Close();
}
}
}
private static OPQT ReadSingleRow(IDataRecord dataRecord)
{
return new OPQT
{
Lines.ItemCode = dataRecord[0],
CardCode = dataRecord[1],
Lines.RequiredQuantity = dataRecord[2]
};
}
}

Read all record in sql table using SqlDataReader

I want to read all records from "product" table and create objects from each records.
it only gets one records from the database, any ideas might help ?
public IReadOnlyList<Product> Search(string name)
{
var result = new List<Product>();
using (var conn = new SqlConnection(connectionString))
{
if (name == null)
{
var command = new SqlCommand("SELECT * FROM Product ", conn);
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], (string)reader["Name"],
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
(string)reader["Description"]);
result.Add(prod);
reader.NextResult();
}
reader.Close();
conn.Close();
return result;
};
}
}
If you have several result sets, you should loop over them, i.e. you should put one more outer loop, e.g.
using var reader = command.ExecuteReader();
do {
while (reader.Read()) {
var prod = new Product(
Convert.ToInt32(reader["ID"]),
Convert.ToString(reader["Name"]),
Convert.ToDouble(reader["Price"]), // decimal will be better for money
Convert.ToInt32(reader["Stock"]),
Convert.ToInt32(reader["VATID"]),
Convert.ToString(reader["Description"])
);
result.Add(prod);
}
}
while (reader.NextResult());
Note outer do .. while loop since we always have at least one result set.
You use NextResult which advances the reader to the next result set. This makes sense if you have multiple sql queries and you'd use it after the while-loop. Here it's just unnecessary and wrong.
You are already advancing the reader to the next record with Read.
If I get rid of it, this error occur : Unable to cast object of type
'System.DBNull' to type 'System.String.
You can use IsDBNull:
int nameIndex = reader.GetOrdinal("Name");
string name = reader.IsDBNull(nameIndex) ? null : reader.GetString(nameIndex);
int descIndex = reader.GetOrdinal("Description");
string description = reader.IsDBNull(descIndex) ? null : reader.GetString(descIndex);
var prod = new Product((int)reader["ID"],
name,
(double)reader["Price"],
(int)reader["Stock"],
(int)reader["VATID"],
description);
Use it for every nullable column, for the numeric columns you could use nullable types like int?.
You have an error in your code:
Remove the line reader.NextResult();
NextResult is used for moving to next result set not next record.
Definitely remove the NextResult(). That does NOT move between individual records in the same query. Read() does this for you already. Rather, NextResult() allows you to include multiple queries in the same CommandText and run them all in one trip to the database.
Try this:
public IEnumerable<Product> Search(string name)
{
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT * FROM Product ", conn))
{
if (!string.IsNullOrEmpty(name) )
{
command.CommandText += " WHERE Name LIKE #Name + '%'";
command.Parameters.Add("#Name", SqlDbType.NVarChar, 50).Value = name;
}
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], reader["Name"].ToString(),
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
reader["Description"].ToString());
yield return prod;
}
}
}
}

SqlDataReader returns only one row

I am using a SqlDataReader to fetch data from a stored procedure. Even though the records are being fetched, the while (reader.Read()) gets executed only once, and so in my list only one row is added.
List<Student> tablelist = new List<Student>();
using (SqlConnection con = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("SP_ReadPromotedStudents"))
{
cmd.Connection = con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = Data[0];
cmd.Parameters.Add("#Email", SqlDbType.VarChar).Value = Data[1];
cmd.Parameters.Add("#Class", SqlDbType.VarChar).Value = Data[2];
con.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.HasRows)
{
while (reader.Read())
{
tablelist.Add(new Student
{
Name = (string)(reader[0]),
Email = (string)(reader[1]),
Class = (string)(reader[2]),
});
reader.NextResult();
}
}
}
}
}
return tablelist;
My Student class:
public class Student
{
public string Name { get; set; }
public string Email { get; set; }
public string Class { get; set; }
}
I have about 46 records being fetched. But in the list only one record gets added. What is the mistake here?
You need to move your call to NextResult outside the reader.Read() loop. Otherwise after the first read the code encounters the NextResult call and tries to load a second sets of data returned by the stored procedure.
Also the loop over HasRows is an infinite loop. If the property reader.HasRows is true it will be true also when you finish to read the rows.
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
tablelist.Add(new Student
{
Name = (string)(reader[0]),
Email = (string)(reader[1]),
Class = (string)(reader[2]),
});
}
// This should be called only if your stored procedure returns
// two or more sets of data otherwise you can remove everything
reader.NextResult();
// If there is another set of data then you can read it with a
// second while loop with
while(reader.Read())
{
.....
}
}
The ideal scenario would to have a new sql statement to get just what you want instead the get a list and need just the first access. Imagine if you have a table with millions of records, would you need to execute a query to get all and read just the first one? No, you execute a query to get the you need.
The NextResult method from DataReader moves the pointer to the next result if you have it on the result. Remove it.
After you chanfge the sql statement to get what you need, you are looping the result set. You could read just the first line (changing the while to if):
if (reader.Read())
{
tablelist.Add(new Student
{
Name = (string)(reader[0]),
Email = (string)(reader[1]),
Class = (string)(reader[2]),
});
}

Refering to view values one by one

I have the following code which establishing an SQL connection inside of a project I am working on. What I want to do is to create a for loop which contains a method and every time the loop repeats the method runs with a different value until all views of the returned query are used.
I can't figure out how to refer to every value of the view without saving the view to a list or an array first. Any ideas?
SqlConnection Con = new SqlConnection(#"Data Source=localhost\**;Initial Catalog=ML;User Id=sa;Password='**'");
string sql = #"select product_id,Name from E_PRODUCT_PROPERTY";
var mylist = new List<WineRating>();
using (var command = new SqlCommand(sql, Con))
{
Con.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
new WineRating { product_id = reader.GetInt32(0), Name = reader.GetString(1) };
///Here goes the code I suppose
method_name(reader.GetInt32(0), reader.GetString(1));
}
}
public static int method_name(int product_id, string Name)
{
int num = x *2;
Console.WriteLine(num + Name);
}
Perhaps like this:
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
MyMethodToPrintToScreen(reader.GetInt32(0), reader.GetString(1));
}
}
With the method to print to screen
private static void MyMethodToPrintToScreen(int id, string product)
{
//Do whatever you wish with the data: example
Console.WriteLine($"My id: {id} | Product: {product}");
}
Edit
Let me make it even more obvious(using your exact code):
SqlConnection Con = new SqlConnection(#"Data Source=localhost\**;Initial Catalog=ML;User Id=sa;Password='**'");
string sql = #"select product_id,Name from E_PRODUCT_PROPERTY";
var mylist = new List<WineRating>();
using (var command = new SqlCommand(sql, Con))
{
Con.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
method_name(reader.GetInt32(0), reader.GetString(1));
}
}
}

C# ExecuteReader fails if condition

Why only the else condition runs? Postalcode column is float, City column is nvarchar. I think the fail is the string may be mistake.
private void txt_st_postalcode_Leave(object sender, EventArgs e)
{
using (var connection = new SqlConnection("Data Source=mypublicip\\SQLEXPRESS2017;Initial Catalog=studentreg; User = myusername; Password=mypassword;"))
{
connection.Open();
using (var command = new SqlCommand("SELECT City FROM Cities WHERE Postcode=#Postcode", connection))
{
command.Parameters.AddWithValue("#Postcode", "10101");
using (var reader = command.ExecuteReader())
{
string txt_st_postalcode = reader.Read() ?
reader[1] as string : ("City");
if (reader.Read())
{
txt_st_city.Text = reader.GetString(reader.GetOrdinal("City"));
}
else
{
MessageBox.Show("Sh*t!");
}
}
}
}
}
Not too sure how these postcodes work (different in the UK), but given that you're using ExecuteReader you appear to be expecting multiple results. As the comments have pointed out, you're currently reading the results twice; however, you should probably have some form of loop; for example:
using (var command = new SqlCommand("SELECT City FROM Cities WHERE Postcode=#Postcode", connection))
{
command.Parameters.AddWithValue("#Postcode", "10101");
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
string txt_st_postalcode = reader[0] as string;
//txt_st_city.Text = reader.GetString(reader.GetOrdinal("City"));
// Depends what you're doing here?
}
}
}
If you only expect a single result, try using ExecuteScalar() which will return a single result; for example:
using (var command = new SqlCommand("SELECT City FROM Cities WHERE Postcode=#Postcode", connection))
{
command.Parameters.AddWithValue("#Postcode", "10101");
txt_st_city.Text = command.ExecuteScalar();
}
Firstly, if you need to read PostCode from the reader, you have to select first. So, change your query.
using (var command = new SqlCommand("SELECT City, PostCode FROM Cities WHERE Postcode=#Postcode", connection))
Secondly, calling read once per row before getting data.
if (reader.Read())
{
txt_st_postalcode .Text = reader.GetString(reader.GetOrdinal("PostCode"));
txt_st_city.Text = reader.GetString(reader.GetOrdinal("City"));
}
else
{
MessageBox.Show("Sh*t!");
}

Categories