I've created a stored procedure (SP) and integrated that in the following way that works just fine:
Edited:
private DataTable GetSPResult()
{
int m = Convert.ToInt32(DropDownList1.SelectedValue);
int k = Convert.ToInt32(DropDownList2.SelectedValue);
DataTable ResultsTable = new DataTable();
var context = new DemoEntities();
var con = context.Database.Connection;
var connectionState = con.State;
try
{
using (context)
{
con.Open();
using (var cmd = con.CreateCommand())
{
cmd.CommandText = "MonthlyConsumption"; //Here is the SP
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Para1", SqlDbType.Int));
cmd.Parameters["#Para1"].Value = m;
cmd.Parameters.Add(new SqlParameter("#Para2", SqlDbType.Int));
cmd.Parameters["#Para2"].Value = k;
using (var reader = cmd.ExecuteReader())
{
ResultsTable.Load(reader);
}
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (con != null)
{
con.Close();
}
}
return ResultsTable;
}
Finally done the below: On button click, I am able to see data in the report
protected void Button1_Click(object sender, EventArgs e)
{
DataTable dt = GetSPResult();
ReportViewer1.Visible = true;
ReportViewer1.LocalReport.ReportPath = Server.MapPath("Report1.rdlc");
ReportViewer1.LocalReport.DataSources.Clear();
ReportViewer1.LocalReport.DataSources.Add(new ReportDataSource("DataSet1", dt));
}
Output:
But when I try to convert a list to DataTable with ORM, it throws no exception but no data in the report as follows:
Output:
This is the code that I've done so far with ORM - Entity Framework that also works: By the way, I put breakpoint for debugging purpose and it gets the value but doesn't return data in the report
public DataTable GetSPResult()
{
int m = Convert.ToInt32(DropDownList1.SelectedValue);
int k = Convert.ToInt32(DropDownList2.SelectedValue);
DataTable ResultsTable = new DataTable();
var context = new DemoEntities();
using (context)
{
var query = context.MonthlyConsumption(m, k).ToList();
foreach (var item in query)
{
ResultsTable.Columns.Add("Store");
ResultsTable.Columns.Add("Product");
ResultsTable.Columns.Add("Jan");
ResultsTable.Columns.Add("Feb");
ResultsTable.Columns.Add("Mar");
ResultsTable.Columns.Add("Apr");
ResultsTable.Columns.Add("May");
ResultsTable.Columns.Add("Jun");
ResultsTable.Columns.Add("Jul");
ResultsTable.Columns.Add("Aug");
ResultsTable.Columns.Add("Sep");
ResultsTable.Columns.Add("Oct");
ResultsTable.Columns.Add("Nov");
ResultsTable.Columns.Add("Dec");
ResultsTable.Rows.Add(item.StoreName);
ResultsTable.Rows.Add(item.ItemName);
ResultsTable.Rows.Add(item.M1.Value);
ResultsTable.Rows.Add(item.M2.Value);
ResultsTable.Rows.Add(item.M3.Value);
ResultsTable.Rows.Add(item.M4.Value);
ResultsTable.Rows.Add(item.M5.Value);
ResultsTable.Rows.Add(item.M6.Value);
ResultsTable.Rows.Add(item.M7.Value);
ResultsTable.Rows.Add(item.M8.Value);
ResultsTable.Rows.Add(item.M9.Value);
ResultsTable.Rows.Add(item.M10.Value);
ResultsTable.Rows.Add(item.M11.Value);
ResultsTable.Rows.Add(item.M12.Value);
}
}
return ResultsTable;
}
Note and Updated: Could I convert the List to a IDataReader to load it or is there any simple way to make it done? I've seen some of the tutorials where the author has used foreach loop to iterate the list and then bind it to the DataTable. But I am just trying to simply load the list to the DataTable.
Finally got it done. Just converted the list to DataTable using the following method:
public DataTable ToDataTable<T>(List<T> items)
{
DataTable ResultsTable = new DataTable(typeof(T).Name);
//Gets all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Sets column names as Property names
ResultsTable.Columns.Add(prop.Name);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//Inserts property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
ResultsTable.Rows.Add(values);
}
return ResultsTable;
}
Related
I'm trying to pass a variable to my DBContext extension. It works fine with a fixed string but when I try to use a variable I get the error "Must declare the scalar variable #p_linq_0"
var pn = "6-10064-01";
// put query results into datatable
var query = db.SWD_0004.Where(p => p.Part_Number == pn) as DbQuery<SWD_0004>;
var numResults = query.Count();
DataTable dt = db.DataTable(query.ToString());
// gets column names and put into array
List<DataRow> list = dt.AsEnumerable().ToList();
string[] columnList = dt.Columns.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToArray();
var rowCount = dt.Rows.Count;
var columnCount = dt.Columns.Count;
for (var j = 0; j < rowCount; j++)
{
for (var i = 1; i < columnCount; i++) // skip ID column
{
var columnName = columnList[i];
var data = list[j][i].ToString();
lstName.Items.Add(columnName);
lstData.Items.Add(data);
}
}
public static class DbContextExtensions
{
public static DataTable DataTable(this DbContext context, string query)
{
DbProviderFactory dbFactory = DbProviderFactories.GetFactory(context.Database.Connection);
using (var cmd = dbFactory.CreateCommand())
{
cmd.Connection = context.Database.Connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
{
adapter.SelectCommand = cmd;
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}
}
I've tried a few conversion methods to ObjectQuery, DBQuery, etc with no result. I'm so confused need some direction please.
I finally found the answer I was looking for!
https://www.c-sharpcorner.com/UploadFile/0c1bb2/convert-linq-query-result-to-datatable/
I read this question: BackgroundWorker to read database and update GUI
And thinks, that this is a good way to solve this problem.
The only thing is, that I create my Columns dynamically.
I read schemaInfo from DataBase to get the ColumnNames and create a DataGridColumn foreach Column in DataBase.
Now i want to do this with a BackgroundWorker, but u can't update the UI with a BackgroundWorker.
For now (without BackgroundWorker) I have 2 Methods to get the DataBase values.
To load the columns:
using (MySqlDataReader reader = cmd.ExecuteReader())
{
using (DataTable schema = reader.GetSchemaTable())
{
foreach (DataRow col in schema.Rows)
{
DataGridTextColumn column = new DataGridTextColumn();
string columnstring = col.Field<String>("ColumnName");
comboFilter.Items.Add(UppercaseFirst(columnstring));
column.Binding = new Binding(col.Field<String>("ColumnName"));
column.Header = UppercaseFirst(col.Field<String>("ColumnName"));
column.IsReadOnly = true;
Style textStyle = new Style(typeof(TextBlock));
textStyle.Setters.Add(new Setter(TextBlock.TextWrappingProperty, TextWrapping.Wrap));
column.ElementStyle = textStyle;
dataGridPrivatecustomers.Columns.Add(column);
}
}
}
To load values
using (MySqlDataAdapter dataAdapter = new MySqlDataAdapter(string.Format("SELECT * FROM {0}", privateCustomerTablename), connection))
{
using (DataTable dt = new DataTable())
{
dataAdapter.Fill(dt);
dataGridPrivatecustomers.ItemsSource = dt.DefaultView;
}
}
So I tried to make both in one and struggled at creating the result List for the DataGrad's ItemSource:
using (MySqlDataReader reader = cmd.ExecuteReader())
{
int i = 0;
while (reader.Read())
{
using (DataTable schema = reader.GetSchemaTable())
{
foreach (DataRow row in schema.Rows)
{
results.Add(new
{
});
i++;
}
}
}
}
The thing is, that I need to know how I can put the ColumnName + Value in the result List.
In the linked question he did
Id = rdr.GetInt32(0),
But i can't do row.Field<String>("ColumnName") = reader[i].ToString()
The code also don't know how much Columns to add so i guess i need another for or foreach loop ? And how I can set the ColumnName ?
I think that some people want to know this too because (for me) it looks really tricky for beginning Developers who wan't Columns and Values from DataBase to show in a DataGrid with a BackgroundWorker.
So i will show you, how I solved my question.
First I created a List to store all ColumnNames.
private List<string> columnList = new List<string>();
After that I edited the worker_DoWork Event as follows:
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
using (MySqlConnection connection = new MySqlConnection(conf.connection_string))
{
if (OpenConnection(connection))
{
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = connection;
cmd.CommandText = string.Format("SELECT * FROM {0}", privateCustomerTablename);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
using (DataTable schemaTable = reader.GetSchemaTable())
{
columnList.Clear();
foreach (DataRow row in schemaTable.Rows)
{
columnList.Add(row.Field<String>("ColumnName"));
}
}
}
using (MySqlDataAdapter dataAdapter = new MySqlDataAdapter(cmd.CommandText, connection))
{
using (DataTable dt = new DataTable())
{
dataAdapter.Fill(dt);
e.Result = dt;
}
}
}
connection.Close();
}
}
}
First you can see i made a DataReader to read the DataBase schema and store it in a DataTable so I can loop it trough a foreach loop. In this Loop I add each Item from the schemaTable to my List so the List contains all ColumnNames of the selected DataBase Table.
After that I created a DataAdapter and store the content to another DataTable. And i pass this DataTable via e.Result = dt to the worker_RunWorkerCompleted Event. Now I want to access this data in my worker_RunWorkerCompleted Event.
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (var item in columnList)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Binding = new Binding(item);
column.Header = UppercaseFirst(item);
column.IsReadOnly = true;
dataGridPrivatecustomers.Columns.Add(column);
}
DataTable dt = e.Result as DataTable;
dataGridPrivatecustomers.ItemsSource = dt.DefaultView;
}
I loop all items in my columnList and create a DataGridTextColumn for each item in this list and add it to my DataGrid.
After that all Columns are added into my DataGrid and I set the ItemsSource of my DataGrid to the DataTable i passed from worker_DoWork to worker_RunWorkerCompleted.
I wrote this function for updating one single row (I store the information in a class T):
public void Update(T model)
{
if (!Exist(model))
{
throw new Exception("Object Not Found");
}
DataRow row = Convert(model);
row.Table.Rows.Add(row);
row.AcceptChanges();
row.SetModified();
_dataAdapter.Update(new[] { row });
}
(The DataRow comes detached)
the SqlDataAdapter is configured as so:
SqlDataAdapter da = new SqlDataAdapter("Select * From " + tableName,
ConnectedDAL.GetConnection());
SqlCommandBuilder builder = new SqlCommandBuilder(da);
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
For some reason my insert and delete work, but the update does not.
The call to Update() completes without errors but the database does not receive the updates.
Help?
Update
The update command text from the command builder is wierd:
For cities (CityID, CityName, CountryID)
"Update [Cities] Set [CityName] = #p1, [CountryID] = #p2 WHERE (([CityID] = #p3 AND ([CityName] = #p4) AND ([CountryID] = #p5))
The first condition makes sense but the rest don't. Also the call returns 1, meaning the it thinks it made the changes.
I have a way to make it work.
public void Update(T model)
{
if (!Exist(model))
{
throw new Exception("Object Not Found");
}
DataRow tmp = GetRow(model);
_dataAdapter.Update(new []{ tmp });
}
private DataRow GetRow(T model)
{
DataRow row = Convert(model);
var schem = Schema;
string[] keys = new string[schem.PrimaryKey.Length];
for (var i = 0; i < schem.PrimaryKey.Length; i++)
{
DataColumn column = schem.PrimaryKey[i];
keys[i] = row[column.ColumnName].ToString();
}
DataRow dr = Convert(Select(keys));
dr.Table.Rows.Add(dr);
dr.AcceptChanges();
for (var i = 0; i < row.ItemArray.Length; i++)
{
if (!schem.Columns[i].AutoIncrement)
{
dr[i] = row.ItemArray[i];
}
}
return dr;
}
This works.
I took over a project from a former coworker and I am trying to populate a listview from a datatable that pulls from a mysql database.
Here is what I am using to pull from MySQL:
internal static DataTable GetDataSPT3(string StoredProcedure, Dictionary<string, string> Parameters)
{
DataTable _DT = new DataTable();
MySqlCommand cmd = new MySqlCommand(StoredProcedure, new MySqlConnection(Creds.GetConnectionStringT3()));
cmd.CommandType = CommandType.StoredProcedure;
foreach (KeyValuePair<string, string> Parameter in Parameters)
cmd.Parameters.Add(new MySqlParameter(Parameter.Key, Parameter.Value));
cmd.Connection.Open();
MySqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
_DT.Load(dr);
dr.Close();
return _DT;
}
This is what I am using to populate the listview:
private void GetMetrics()
{
string begin = "2015-05-26 00:00:00";
string end = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Dictionary<string, string> Parameters = GetParameters();
Parameters.Add("p_daily_metrics_date_Date1", begin);
Parameters.Add("p_daily_metrics_date_Date2", end);
DataTable dt = new DataTable();
foreach (DataRow row in RRCStoredProcedures.GetDataSPT3("spGetDailyMetrics", Parameters).Rows)
{
ListViewItem item = new ListViewItem(row[0].ToString());
for (int i = 0; i < row.Table.Rows.Count;i++)
{
item.SubItems.Add(row[i].ToString());
}
lvMetrics.Items.Add(item);
}
}
Watching the process work with breakpoints and the SQL is finding data and returning data, but not in any useful way.
What am I doing wrong?
This is what I came up with, I'm still learning, to solve my issue. A DatagridView did not work for me since it fires a selectionchanged event when it loads and due to the needs of the form I could not have that.
My solution:
private void GetMetrics()
{
string[] array = new string[11];
string begin = "2015-05-26 00:00:00";
string end = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Dictionary<string, string> Parameters = GetParameters();
Parameters.Add("p_daily_metrics_date_Date1", begin);
Parameters.Add("p_daily_metrics_date_Date2", end);
foreach (DataRow row in (RRCStoredProcedures.GetDataSPT4("spGetDailyMetrics", Parameters)).Rows)
{
for (int item = 0; item < row.ItemArray.Count(); item++)
{
array[item] = row.ItemArray[item].ToString();
}
ListViewItem thing = new ListViewItem
(new[] { array[0], array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8], array[9], array[10] });
Metricslstvw.Items.Insert(0, thing);
Array.Clear(array, 0, 10);
}
foreach(ColumnHeader ch in Metricslstvw.Columns) { ch.Width = -2; }
}
Since the DataRow exposes ItemArray, I just rolled that into an array I know how to use.
I have a DataTable and I want to select multiple columns on the DataTable that matches the input in the textbox. The code below only selects 1 column.
var result = from data in mDataTable.AsEnumerable ()
where data.Field<string>("Code") == txtCode.Text
select data.Field<string> ("Description");
foreach (var res in result) {
txtxDescription.Text = res.ToString ();
}
How can I select 2 or more columns on DataTable using LINQ?
why not select full rows (DataRow object) and then take all necessary values from them?
var rows = mDataTable.AsEnumerable()
.Where(data => data.Field<string>("Code") == txtCode.Text);
foreach(DataRow r in rows)
{
txtxDescription.Text = r.Field<string>("Description");
}
another option is to project data to anonymous objects:
var result = from data in mDataTable.AsEnumerable ()
where data.Field<string>("Code") == txtCode.Text
select new
{
Description = data.Field<string> ("Description"),
Code = data.Field<string> ("Code")
};
foreach (var res in result)
{
// last value always replace `txtxDescription.Text` ??
txtxDescription.Text = res.Description;
txtxCode.Text = res.Code;
}
public void GridviewBinding()
{
DataSet ds = new DataSet();
string constr = ConfigurationManager.ConnectionStrings["SQLMSDB"].ConnectionString;
string sql = "select * from tbl_users";
using (SqlConnection conn = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand(sql))
{
cmd.Connection = conn;
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
sda.Fill(ds);
gridviewcontrol.DataSource = ds;
gridviewcontrol.DataBind();
ViewState["GridViewBindingData"] = ds.Tables[0];
}
}
}
}
protected void btn_searching_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(txt_search.Text.Trim().ToString()) || !String.IsNullOrWhiteSpace(txt_search.Text.Trim().ToString()))
{
DataTable dt = (DataTable)ViewState["GridViewBindingData"];
var dataRow = dt.AsEnumerable().Where(x => x.Field<dynamic>("UserName") == txt_search.Text);
DataTable dt2 = dataRow.CopyToDataTable<DataRow>();
gridviewcontrol.DataSource = dt2;
gridviewcontrol.DataBind();
}
else
{
GridviewBinding();
}
}