I need help in reading data from DataTable and adding key, value to a Dictionary object.
//Here is the object:
public class DBName
{
public int ID { get; set; } // key
public string DatabaseName { get; set; } // value
}
My DataTable is getting data, but can't figure out how to add to Dictionary
reading DataTable and adding to Dictionary -
public static Dictionary<int,DBName> ReadToDictionary()
{
string con = #"MyConnectionString;";
SqlConnection sqlconn = new SqlConnection(con);
string sqlQuery = #"SELECT DISTINCT DisplayName AS DatabaseName, RANK() OVER(ORDER BY MIN(id)) AS ID
FROM dbo.MyTable
GROUP BY DisplayName";
Dictionary<int, DBName> dictionaryDBName = new Dictionary<int, DBName>();
SqlCommand sqlcmd = new SqlCommand(sqlQuery, sqlconn);
{
using (SqlDataAdapter da = new SqlDataAdapter(sqlcmd))
{
try
{
DataTable dt = new DataTable();
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
//how to add key and Value?
dictionaryDBName.Add(dr[1], DBName);
}
// anything here?
}
}
catch (Exception ex)
{
//
}
finally
{
sqlconn.Close();
}
}
}
return dictionaryDBName;
}
Please guide.
You are probably much better off just using a DataReader. No need to fill a DataTable and then try looping through it, just loop the reader.
Further points:
Don't swallow exceptions, deal with them properly
You need to have a using on the connection, and then you don't need to call Close
Are you aware of the differences between RANK, DENSE_RANK and ROW_NUMBER?
public static Dictionary<int,DBName> ReadToDictionary()
{
string con = #"MyConnectionString;";
using(SqlConnection sqlconn = new SqlConnection(con))
{
string sqlQuery = #"SELECT DISTINCT
DisplayName AS DatabaseName
,CAST(RANK() OVER(ORDER BY MIN(id)) AS int) AS ID
FROM
dbo.MyTable
GROUP BY
DisplayName";
sqlconn.Open();
Dictionary<int, DBName> dictionaryDBName = new Dictionary<int, DBName>();
using(SqlCommand sqlcmd = new SqlCommand(sqlQuery, sqlconn))
using (var reader = sqlcmd.ExecuteReader())
{
while(reader.Read())
{
DBName n = new DBName { ID = reader.GetInt32(1), DatabaseName = reader[0] as string};
dictionaryDBName.Add(n.ID, n);
}
}
return dictionaryDBName;
}
}
Here's the LINQ solution. The key is to use the powerful ToDictionary extension method.:
var reader = sqlCmd.ExecuteReader();
var dataTable = new DataTable()
dataTable.Load(reader);
var dictionary = dataTable.Rows.Cast<DataRow>().ToDictionary
(
x => (int)x[0], //Convert column 0 into the dictionary's key
x => (string)x[1] //Convert column 1 into the dictionary's value
);
However, since SqlDataReader implements IEnumerable, you can skip the data table entirely like this:
var reader = sqlCmd.ExecuteReader();
var dictionary = reader.Cast<IDataRecord>().ToDictionary
(
x => x.GetInt32(0), //Convert column 0 into the dictionary's key
x => x.GetString(1) //Convert column 1 into the dictionary's value
);
See also this answer.
Related
I have 2 select statements in a stored procedure and getting result set in a data reader.
My objective is to merge the data from 2 selects into a single JSON string.
I am using nested do while and at the end of the while, I am getting an error
Invalid attempt to call Read when reader is closed.
Below is the code:
try
{
con.Open();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "SPWorkMetaData";
rd = cmd.ExecuteReader();
List<Dictionary<String, Object>> tableRow = new List<Dictionary<string, object>>();
List<Dictionary<String, Object>> tableRow1 = new List<Dictionary<string, object>>();
Dictionary<String, Object> rows;
if (rd.HasRows)
{
do
{
dt = new DataTable();
dt.Load(rd);
foreach (DataRow dr in dt.Rows)
{
rows = new Dictionary<string, object>();
int i = 1;
foreach (DataColumn col in dt.Columns)
{
rows.Add(col.ColumnName, dr[col].ToString());
i = i + 1;
}
tableRow.Add(rows);
status = "true";
}
retvalue = serializer.Serialize(tableRow).ToString();
//response = "{\"status\":\"" + status + "\",\"data\":" + retvalue + "}";
do
{
DataTable dt1 = new DataTable();
dt1.Load(rd);
foreach (DataRow dr in dt1.Rows)
{
Dictionary<String, Object> rows1 = new Dictionary<String, Object>();
int i = 1;
foreach (DataColumn col in dt1.Columns)
{
rows1.Add(col.ColumnName, dr[col].ToString());
i = i + 1;
}
tableRow1.Add(rows1);
status = "true";
}
retvalue = serializer.Serialize(tableRow).ToString() + serializer.Serialize(tableRow1).ToString();
response = "{\"status\":\"" + status + "\",\"data\":" + retvalue + "}";
}
while (rd.Read());
}
while (rd.NextResult()) ;
}
}
Does the error means to convey that SQL connection is closed, if so, then it is mentioned that I am closing connection only in the finally block.
Need to understand this, kindly provide some guidance.
Thanks.
Well, you don't note if the two tables returned are the same data structure?
and that then begs the question why a union query is not being used?
Also I don't think (doubt) you want to serialized a data row, since a data row has "extra" things like is the row dirty (been changed) and quite a few more "extra" attributes that I doubt you want to become part of the json result for the client.
So, lets pull the two tables (and ignore WHY a union query is not being used here!!!).
But, say I have two tables. tblHotels, and People - both of them have FirstName, lastname columns.
So, in theory, we need a clean data structure if we going to get a "reasonable" json string.
So, I would suggest this code:
class PersonName
{
public string FirstName = "";
public string LastName = "";
}
protected void Button1_Click(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("Test2", conn))
{
SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
conn.Open();
DataSet1 ds = new DataSet1();
da.Fill(ds);
List<PersonName> MyNames = new List<PersonName>();
// merge records form both tables (FirstName, LastName) into list
foreach (DataRow OneRow in ds.Tables[0].Rows)
{
PersonName OneName = new PersonName();
OneName.FirstName = OneRow["FirstName"].ToString();
OneName.LastName = OneRow["LastName"].ToString();
MyNames.Add(OneName);
}
// now merge 2nd table
foreach (DataRow OneRow in ds.Tables[1].Rows)
{
PersonName OneName = new PersonName();
OneName.FirstName = OneRow["FirstName"].ToString();
OneName.LastName = OneRow["LastName"].ToString();
MyNames.Add(OneName);
}
// at this point, we now have a merge of both tables
JavaScriptSerializer js = new JavaScriptSerializer();
string MyJSON = js.Serialize(MyNames);
}
}
}
I have table
itemID
storeID
qty
103
LAB
20
I want to add qantity of specific item for example:'103' stored in warehouse 'LAB'.
public void addQuantity(string store, string item, int qty)
{
con.Open();
string sql = "SELECT qty,warehouse.storeID,item.itemID FROM Item,warehouse,stocker WHERE stocker.storeID=warehouse.storeID AND stocker.itemID=item.itemID AND warehouse.storeID='"+store+"' AND Item.itemID='"+item+"' ";
using (MySqlDataAdapter adapter = new MySqlDataAdapter(sql, con))
{
using (DataTable tempTable = new DataTable())
{
adapter.Fill(tempTable);
if (tempTable.Rows.Count == 0) throw new Exception("No such product");
foreach (DataRow r in tempTable.Rows)
{
int newQty = (int)r["qty"] + qty;
if (newQty > 0)
{
r["qty"] = newQty;
qty = 0;
break;
}
else
{
MessageBox.Show("error");
}
}
using (MySqlCommandBuilder cb = new MySqlCommandBuilder(adapter))
{
adapter.UpdateCommand = cb.GetUpdateCommand();// there is error
adapter.Update(tempTable);
}
}
}
con.Close();
}
it says:"Dynamic SQL generation is not supported for multiple base tables".
what would you advice me?
If qty is integer column, you may try to:
Increment its current value by some value:
using (var updateCommand = new MySqlCommand())
{
updateCommand.CommandText = "UPDATE mytable t SET t.qty = t.qty + #newQty WHERE *...Your WHERE clause...*`"
updateCommand.Parameters.AddWithValue("#newQty", newQtyValue);`
// ...
}
Or append entire new value:
using (var updateCommand = new MySqlCommand())
{
updateCommand.CommandText = "UPDATE mytable t SET t.qty = #newQtyValue WHERE *...Your WHERE clause...*`"
updateCommand.Parameters.AddWithValue("#newQtyValue", newQtyValue);`
// ...
}
As #sticky bit said, it is preferred to use Command.Parameters.AddWithValue instead of string concat/interpolation.
I want an efficient way to retrieve all info that shares the same Foreign key in a table, and store the data in list/array.
I can read several rows from one column:
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<string> result = new List<string>();
string oString = "Select Column from Table where foreignKey = #fKey";
conn.Open();
SqlCommand oCmd = new SqlCommand(oString, conn);
oCmd.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmd.ExecuteReader())
{
while (oReader.Read())
{
result.Add(oReader.GetString(0));
}
}
conn.Close();
And I can read several columns if I am targeting one specific row:
int sqlData1;
int sqlData2;
int sqlData3;
string oString = "Select * from Table where TableID = #tId";
SqlCommand oCmd = new SqlCommand(oString, conn);
oCmd.Parameters.AddWithValue("#tId", 1001);
conn.Open();
using (SqlDataReader oReader = oCmd.ExecuteReader())
{
while (oReader.Read())
{
sqlData1 = oReader["Row1"].ToString();
sqlData2 = oReader["Row2"].ToString();
sqlData3 = oReader["Row3"].ToString();
}
}
conn.Close();
But I would love to be able to read all/specific data that has the same foreign key. So I want to be able to retrieve several rows, save them to a list, and retrieve several other row data from a different column that shares the same foreign key.
I imagine it to be something like this:
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
List<DateTime> dateList = new List<DateTime>();
string oString = "Select * from Table where ForeignKey = #fKey";
conn.Open();
SqlCommand oCmdSleep = new SqlCommand(oString, conn);
oCmdSleep.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmdSleep.ExecuteReader())
{
while (oReader.Read())
{
intList.Add(oReader["Column1"].GetDateTime(0));
dstringList.Add(oReader["Column3"].GetDateTime(0));
dateList.Add(oReader["Column4"].GetDateTime(0));
}
}
conn.Close();
But this does not working... Please advice me
If you use something like Dapper it would simplify mapping your query result to List<T>.
Add Dapper to your project using nuget.
Install-Package Dapper -Version 1.50.5
Add using for Dapper at the top of your class where you are running the query.
using Dapper;
Add a class that matches the structure of your query results. There are ways to do this using a script or utility app. Here's an app for that.
public class MyClass
{
public int MyId { get; set; }
public string MyName { get; set; }
public DateTime MyDateTime { get; set; }
}
Then where you run the query do so like this.
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
var MyList = conn.Query<MyClass>(#"select * from Table where ForeignKey = #fKey",
new { fKey = "SomeKey" }).ToList();
}
Once the query runs, you can then iterate across MyList.
foreach (var myItem in MyList)
{
// Do something with myItem
}
If you want to bind the results, simply remove .ToList() from the end of the query as its default is IObservable<T>.
No need to give the name of the column, simply use the already established index:
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
List<DateTime> dateList = new List<DateTime>();
string oString = "Select * from Table where ForeignKey = #fKey";
conn.Open();
SqlCommand oCmdSleep = new SqlCommand(oString, conn);
oCmdSleep.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmdSleep.ExecuteReader())
{
while (oReader.Read())
{
intList.Add(oReader.GetDateTime(0));
dstringList.Add(oReader.GetDateTime(3));
dateList.Add(oReader.GetDateTime(4));
}
}
conn.Close();
Here is a breakdown:
listVariable.Add(oReader.GetDataType("Index of column"));
This way you get to retrieve all row data for shared foreign key and the option to do so for as many columns as you want.
I tried to convert a datatable that just has one field (the field's data is primary key) to int , in order to using in sql commands such as Select and etc.
but it fails!
and when i cast it to an object or convert it to string first , the commands gone wrong!
please help me
i want to select * from a table which has a foreign key where the foreign code equals by an int value that has been selected from a table in another sql command and returned as a datatable row with just one field.
here is my code :
class mydata :
public string strsql;
public DataTable showData()
{
SqlConnection Con1 = new SqlConnection("Data Source=.;database=daneshgah;integrated security=true");
Con1.Open();
SqlDataAdapter da = new SqlDataAdapter(strsql, Con1);
DataTable dt = new DataTable();
da.Fill(dt);
Con1.Close();
return (dt);
}
button event :
myData search = new myData();
int aa = int.Parse(txt_stdcourse.Text);
search.strsql = "select tchNo from University where couNo='" + aa + "'";
DataTable a = search.showData();
string b = a.Rows[0][0].ToString();
int c = int.Parse(b);
myData akhz = new myData();
akhz.strsql = "insert into stc (couNo,tchNo,stuNo)values('" + aa + "','" + c + "','" + id + "')";
akhz.Data();
lbl_stdcourseok.Visible = false;
lbl_stdcourseok.Visible = true;
Sounds like you need to use ExecuteScalar on a SqlCommand instead of using a DataAdapter. ExecuteScalar gives the first column of the first row of the dataset returned.
public object RunSQL(string sql)
{
SqlConnection Con1 = new SqlConnection("Data Source=.;database=daneshgah;integrated security=true");
Con1.Open();
SqlCommand command = new SqlCommand(strsql, Con1);
return command.ExecuteScalar();
}
//In some event handler
int myValue = (int)RunSQL("Select Value from Table where ID = " + ID);
That said, please don't do that - it is very bad practice. You almost certainly want to create a class that models whatever data objects you are dealing with, instead of executing arbitrary SQL from event handlers. It is also probably best to manage connections independently of your data class, in a separate data access layer.
An extremely rudimentary example:
public class Student
{
public int StudentID { get; set; }
public bool CurrentlyEnrolled { get; set; }
public string Name { get; set; }
public static Student LoadByID(int ID)
{
DataTable results = DAL.ExecuteSQL("Select * from Students WHERE StudentID = #StudentID", new SqlParameter("#StudentID", ID));
if (results.Rows.Count == 1)
{
return FillFromRow(results.Rows[0]);
}
else
{
throw new DataException("Could not find exactly one record with the specified ID.");
}
}
private static Student FillFromRow(DataRow row)
{
Student bob = new Student();
bob.CurrentlyEnrolled = (bool)row["CurrentlyEnrolled"];
bob.Name = (string)row["Name"];
bob.StudentID = (int)row["StudentID"];
return bob;
}
}
public static class DAL
{
private const string ConnectionString = "SomeConnectionString"; //Should really be stored in configuration files.
public static DataTable ExecuteSQL(string SQL, params SqlParameter[] parameters)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand(SQL))
{
command.Parameters.AddRange(parameters);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataTable result = new DataTable();
adapter.Fill(result);
return result;
}
}
}
}
}
I want to build a custom interface (a separate aspx page) to manage the data that is put into the webforms for marketeers (WFFM) database, and that for just one form. It must be possible to edit the data and select records with particular sortings and pagings. The database is configured to be SQLite.
Is this possible and recommended, or is it just plain xml that is saved into the WFFM database? And how should I go about it?
This is completely doable, though the select query to get data out of WFFM is a bit funky because everything is stored loose in one huge table called "field" with only a trail of GUIDs to tie the stored values back to what form they came from and what field.
Provided below is part of an Export to Excel utility I wrote for WFFM data. It builds a DataTable object from submitted form results. You could adapt it to some other structure without much work though.
public string connectionStringWFFM = "user id=sitecore_admin;password=xxx;Data Source=SitecoreDBServer.com;Database=Sitecore_WebForms";
protected DataTable BuildDataTable(Item formItem)
{
List<FormResult> formResults = FormResults(formItem.ID.Guid);
List<Field> distinctFields = DistinctFields(formItem.ID.Guid);
var dt = new DataTable();
dt.Columns.Add("Submission_DateTime", typeof (string));
foreach (Field field in distinctFields)
{
var dataColumn = new DataColumn("_" + field.id.ToString("N"), typeof (string));
dataColumn.Caption = field.name.Replace(" ", "_");
dt.Columns.Add(dataColumn);
}
foreach (FormResult formResult in formResults)
{
var connection = new SqlConnection();
connection.ConnectionString = connectionStringWFFM;
var command = new SqlCommand();
command.Connection = connection;
command.CommandText = "select fieldid, value from field where formid=#formid order by fieldid";
command.Parameters.Add("#formid", SqlDbType.UniqueIdentifier).Value = formResult.id;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
DataRow dataRow = dt.NewRow();
dataRow["Submission_DateTime"] = formResult.timestamp.ToString("MM/dd/yyyy HH:mm:ss");
while (reader.Read())
{
dataRow["_" + reader.GetGuid(0).ToString("N")] = reader.GetValue(1).ToString().Replace("<item>", "").Replace("</item>", "");
}
dt.Rows.Add(dataRow);
reader.Close();
connection.Close();
}
return dt;
}
public List<Field> DistinctFields(Guid formitemid)
{
var connection = new SqlConnection();
connection.ConnectionString = connectionStringWFFM;
var command = new SqlCommand();
command.Connection = connection;
command.CommandText = "select distinct fieldid from field where formid in (select id from form where formitemid=#formitemid) order by fieldid";
command.Parameters.Add("#formitemid", SqlDbType.UniqueIdentifier).Value = formitemid;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
var results = new List<Field>();
int count = 0;
while (reader.Read())
{
var field = new Field();
field.id = reader.GetGuid(0);
Database database = Factory.GetDatabase("master");
Item i = database.GetItem(new ID(field.id));
if (i != null && i.DisplayName != null)
{
field.name = i.DisplayName;
}
else
{
field.name = "Field" + count;
}
results.Add(field);
count += 1;
}
reader.Close();
connection.Close();
return results;
}
public List<FormResult> FormResults(Guid formitemid)
{
var connection = new SqlConnection();
connection.ConnectionString = connectionStringWFFM;
var command = new SqlCommand();
command.Connection = connection;
command.CommandText = "select id, timestamp from form where formitemid=#formitemid";
command.Parameters.Add("#formitemid", SqlDbType.UniqueIdentifier).Value = formitemid;
connection.Open();
SqlDataReader reader = command.ExecuteReader();
var results = new List<FormResult>();
while (reader.Read())
{
var result = new FormResult();
result.id = reader.GetGuid(0);
result.timestamp = reader.GetDateTime(1);
results.Add(result);
}
reader.Close();
connection.Close();
return results;
}
public class FormResult
{
public Guid id { get; set; }
public DateTime timestamp { get; set; }
}
public class Field
{
public Guid id { get; set; }
public string name { get; set; }
}