Let Me first tell you what i am doing right now and what problem i am facing.
Right now i am using SqlDataReader for fetching data from database my function looks like
public List<TOPIC_REPORT> gettopicreports()
{
query = "SELECT * FROM [Topic Reports]";
List<TOPIC_REPORT> rpl = new List<TOPIC_REPORT>();
try
{
con.Open();
com = new SqlCommand(query, con);
sdr = com.ExecuteReader();
while (sdr.Read())
{
rt = new TOPIC_REPORT();
rt.ContentId = sdr.GetString(0);
rt.TimesReported = sdr.GetInt64(1);
rt.IsBanned = sdr.GetInt32(2);
rpl.Add(rt);
}
con.Close();
return rpl;
}
catch (Exception e)
{
con.Close();
throw e;
}
}
Problems with above code
Need to assign values to class variables on by one and problematic with multiple select query.
Need to take care of ResultSet , while loop etc.
Above Problem solution use SqlDataAdapter.
public DataSet getdata()
{
com.CommandText = "GetMasterPageData";
com.Connection = con;
com.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataAdapter adapter = new SqlDataAdapter(com);
DataSet ds = new DataSet();
adapter.Fill(ds);
con.Close();
return ds;
}
This solves above stated problems but
Decrease my prog readability because my front end person don't know in which order i am sending DataTables in DataSet.
Don't know order of selected values in DataTable.
Front end person need to work on column index which is at end will be problematic.
Suggest me what should i do to solve above stated problems.
You can change your query to return multiple result sets, by combining all your queries. For example:
query = "SELECT * FROM [Topic Reports]; SELECT * FROM [Other table]";
After iterating the first result set with SqlDataReader.Read, use SqlDataReader.NextResultSet() method to get to the second result, then use SqlDataReader.Read to iterate the second set.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.nextresult(v=vs.110).aspx
As a side note, if you do this, then you will have to figure out a way of returning collections of two types of objects from your method. It would probably be better to just use 1 method to get a collection of object A, and a different method to get a collection of object B.
Related
I don't know my title is understandable but actually I want to know which one is better?
1-Creating object class and get data from mssql db with loop
2-Getting data from sql db with json format
3-Something else..
I think, loop can be slow when working with big datas. However maybe using json path can be slower than loop.
Example for 1 (CREATING OBJECT IN LOOP)
List<objExample > retVal = new List<objExample >();
objExample item;
SqlConnection con = new SqlConnection("CONNECTION STRING");
SqlDataAdapter da;
SqlCommandcmd;
da = new SqlDataAdapter("Select a,b from table", con);
con.Open();
DataTable dt = new DataTable();
da.Fill(dt);
con.Close();
foreach (DataRow itemdr in dt.Rows)
{
item = new objExample();
item.A= itemdr["a"].ToString();
item.B= itemdr["b"].ToString();
item.HasError = false;
retVal.Add(item);
}
return retVal;
Example for 2 (FOR JSON PATH)
List<objExample > retVal;
SqlConnection con = new SqlConnection("CONNECTION STRING");
SqlDataAdapter da;
SqlCommandcmd;
da = new SqlDataAdapter("Select a,b from table for json path", con);
con.Open();
DataTable dt = new DataTable();
da.Fill(dt);
con.Close();
string _json = dt.Rows[0][0].ToString();
retVal = JsonConvert.DeserializeObject<List<objExample>>(_json);
return retVal;
I tried both of them with small data but it didn't satisfy me.
PS : I wrote codes in my mind. Sorry about wrong codes and bad English.
Please guide to me. Thanks.
Just to be clear, are you reading the complete table?
If the goal is to get data that is stored within the json, and maybe perform some queries on it a NoSQL DB seems more appropriate that a SQL one.
Count the number of commands that are dependent on the number of rows. Kind of how we do the Big O Complexity.
I would do something like:
using (var sqlCommand = new SqlCommand("Select a,b from table for json path", con))
{
try
{
using (var reader = sqlCommand.ExecuteReader())
{
while (reader.Read())
{
item = new objExample();
item.A= reader["a"].ToString();
item.B= reader["b"].ToString();
item.HasError = false;
retVal.Add(item);
}
}
}
}
Keep in mind that the sql connection will remain open while you are doing this so its judgement call if you want to do this after loop or in the loop.
And the da.Fill or using the above is same internally, still going to iterate over all rows.
As far as using json is concerned, I wouldn't do that unless you are asking if storing as Json file is a better option than in sql. That's a whole diff question.
You can use PLINQ to process the data in parallel once you have it in the list, even for your above deserialization you can try PLINQ.
I have a datagridview (dgvSelectedItem)and I want it to display some values from textboxes but I have this error
Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound
My code is:
DataTable dt = new DataTable();
string conn = "server=.;uid=sa;pwd=123;database=PharmacyDB;";
Okbtn()
{
SqlDataAdapter da = new SqlDataAdapter("select UnitPrice from ProductDetails where prdctName like '"+txtSelectedName.Text + "'", conn);
da.Fill(dt);
dgvSelectedItem.DataSource = dt;
//this code work but when I add these lines the Error appear
dgvSelectedItem.Rows.Add(txtSelectedName.Text);
dgvSelectedItem.Rows.Add(txtSelectedQnty.Text); }
Thanks in advance
UPDATED Per OP Comment
So you want a user to enter the product name and quantity, then to run a query against the database to retrieve the unit price information. Then you want to display all of that information to your user in a DataGridView control.
Here is what I suggest:
Include the prdctName field in your SELECT clause.
If you want to bind all relevant data to a DataGridView including the Quantity variable and your TotalPrice calculation, then include them in your SELECT statement. When you bind data to the control from an SQL query like this, the result set is mapped to the grid. In other words, if you want information to be displayed when setting the DataSource property then you need to include the information in your SQL result set.
Don't use LIKE to compare for prdctName as it slightly obscures the purpose of your query and instead just use the = operator.
In addition from a table design perspective, I would add a UNIQUE INDEX on the prdctName column if it is indeed unique in your table - which I sense it likely is. This will improve performance of queries like the one you are using.
Here is what your SQL should look like now:
SELECT prdctName, UnitPrice, #Quantity AS Quantity,
UnitPrice * #Quantity AS Total_Price
FROM ProductDetails
WHERE prdctName = #prdctName
A few other suggestions would be to use prepared SQL statements and .NET's using statement as already noted by Soner Gönül. I'll try to give you motivation for applying these techniques.
Why should I use prepared SQL statements?
You gain security against SQL Injection attacks and a performance boost as prepared statements (aka parameterized queries) are compiled and optimized once.
Why should I use the using statement in .NET?
It ensures that the Dispose() method is called on the object in question. This will release the unmanaged resources consumed by the object.
I wrote a little test method exemplifying all of this:
private void BindData(string productName, int quantity)
{
var dataTable = new DataTable();
string sql = "SELECT prdctName, UnitPrice, #Quantity AS Quantity, " +
"UnitPrice * #Quantity AS Total_Price " +
"FROM ProductDetails " +
"WHERE prdctName = #prdctName";
using (var conn = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#prdctName", productName);
cmd.Parameters.AddWithValue("#Quantity", quantity);
using (var adapter = new SqlDataAdapter(cmd))
{
adapter.Fill(dataTable);
dataGridView1.DataSource = dataTable;
}
}
}
}
Here's a sample input and output of this method:
BindData("Lemon Bars", 3);
I searched over the internet and looks like there is no way to add new rows programmatically when you set your DataSource property of your DataGridView.
Most common way is to add your DataTable these values and then bind it to DataSource property like:
da.Fill(dt);
DataRow dr = dt.NewRow();
dr["UnitPrice"] = txtSelectedName.Text;
dt.Rows.Add(dr);
dt.Rows.Add(dr);
dgvSelectedItem.DataSource = dt;
Also conn is your connection string, not an SqlConnection. Passing SqlConnection as a second parameter in your SqlDataAdapter is a better approach in my opinion.
You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Finally, don't forget to use using statement to dispose your SqlConnection and SqlDataAdapter.
I assume you want to use your text as %txtSelectedName.Text%, this is my example;
DataTable dt = new DataTable();
using(SqlConnection conn = new SqlConnection("server=.;uid=sa;pwd=123;database=PharmacyDB;"))
using(SqlCommand cmd = new SqlCommand("select UnitPrice from ProductDetails where prdctName like #param"))
{
cmd.Connection = conn;
cmd.Parameter.AddWithValue("#param", "%" + txtSelectedName.Text + "%");
using(SqlDataAdapter da = new SqlDataAdapter(cmd, conn))
{
da.Fill(dt);
DataRow dr = dt.NewRow();
dr["UnitPrice"] = txtSelectedName.Text;
dt.Rows.Add(dr);
dt.Rows.Add(dr);
dgvSelectedItem.DataSource = dt;
}
}
I can store my SQL result set into a ADODB record set or ADO.NET recordset. In .NET
I can use a .NET connection manager, get .NET DataSet, extract the DataTable from
this DataSet and use it. The problem is that I am getting about 30-40K rows in memory.
I don't want to do that. I want to get a SqlDataReader into "some kind of .NET recordset"
so that I can read row by row instead of all at once. How do I do this in ? I want to use C# and .NET only.
Thanks.
.NET has several types of collections List, HashSet ....
All of those will hold the records in memory
DataTable has many features and memory overhead
Create a class with proper properties
List<MyClass> MyClasses = new List<MyClass>();
String strSQL = "select * from Table01";
SqlCommand cmd = new SqlCommand { Connection = Cn, CommandText = strSQL };
try
{
Cn.Open();
SqlDataReader sdr = cmd.ExecuteReader();
while (sdr.Read())
{
MyClasses.Add(new MyClass(sdr.GetValue(0));
}
sdr.close();
}
catch (Exception Ex) {}
finally
{
Cn.Close();
}
30-40K is not all that big unless your row is really big.
I have used collections over 1 million.
If you want to process your data row by row and not load it all into memory then why don't you code a custom transformation: Here is something to give you an idea: http://technet.microsoft.com/en-us/library/ms136027.aspx
Use your existing connection managers for the source and destination and put a script component in between for your custom row by row processing.
This will process data row by row with the advantage that it uses all of SSIS native ability to shift only the optimal amount of rows into memory buffers (and back out again when not needed)
SqlDataReader sdr;
var strSQL = "select * from Table01";
var cmd = new SqlCommand { Connection = Cn, CommandText = strSQL };
try
{
sdr = cmd.ExecuteReader();
sdr.Read();
if (sdr.HasRows)
{
var field = sdr.GetValue(0);
// and so on....
}
dtrContID.Close();
}
catch (Exception err)
{
// .......
}
I am using the following code :
EmpInfoDS = new DataSet();
con.Open(); // My connection name
string sqlRecords = "Select * FROM tbl_EmpInfo";
EmpInfoDA = new OleDbDataAdapter(sqlRecords, con);
EmpInfoDA.Fill(EmpInfoDS, "EmpInfo");
var sortedRows = (from myRow in EmpInfoDS.Tables["EmpInfo"].AsEnumerable()
orderby myRow["EmpID"] ascending
select myRow).ToArray();
EmpInfoDS.Tables.Remove("EmpInfo");
DataTable EmpInfo = sortedRows.CopyToDataTable();
EmpInfo.TableName = "EmpInfo";
EmpInfoDS.Tables.Add(EmpInfo);
con.Close();
to sort the values in a datatable. Then removing that datatable and filling the sorted rows into a datatable with the same name.
Can anyone tell me how much efficient this process is. ie. Performance drawbacks ???
If there's a better way to accomplish this; Please tell me.
Any help will be much appreciated. :)
Why don't you just order by in the database? That will always be way more efficient.
i.e.
EmpInfoDS = new DataSet();
con.Open(); // My connection name
string sqlRecords = "Select * FROM tbl_EmpInfo ORDER BY EmpID";
EmpInfoDA = new OleDbDataAdapter(sqlRecords, con);
EmpInfoDA.Fill(EmpInfoDS, "EmpInfo");
con.Close();
Always let the database do work that it's made to do. Sorting is a database function and not a C# function (when it comes to fetching data)
You can try
EmpInfoDS.Tables["EmpInfo"].Sort = "EmpID ASC";
DataTable EmpInfo = EmpInfoDS.Tables["EmpInfo"].DefaultView.ToTable();
EmpInfoDS.Tables.Add(EmpInfo);
//To remove sorting --- EmpInfoDS.Tables["EmpInfo"].Sort = string.Empty;
with this approach you may leave your old DataTable and change sorting expression
This is basically a search tool. When I type some thing in a combobox, the combobox drops down and will show me suggestions (something like Google search bar)
I created a procedure which does some complex calculations, which take one parameter and returns some rows. Then I created a combobox event (On Update Text).
And in the event handler I wrote this code:
private void combobox_TextUpdate(object sender, EventArgs e)
{
this.combobox.Items.Clear();
DataTable List = new DataTable();
if (this.combobox.Text.Length > 0)
{
List = searchIt(combobox.text);
foreach (DataRow Row in List.Rows)
{
this.combobox.Items.Add(Row.ItemArray.GetValue(0).ToString());
}
this.combobox.DroppedDown = true;
}
}
static public DataTable searchIt(string STR)
{
string connectionString = McFarlaneIndustriesPOSnamespace.Properties.Settings.Default.McFarlane_IndustriesConnectionString;
SqlConnection con = new SqlConnection(connectionString);
DataTable DT = new DataTable();
con.Open();
SqlDataAdapter DA = new SqlDataAdapter("USE [McFarlane Industries] " +
"EXEC search " +
STR, connectionString);
DA.Fill(DT);
con.Close();
return DT;
}
The function searchIt executes the stored procedure and it returns a DataTable. The stored procedure is working fine in SQL Server Management Studio.
But in the application it is not working correctly in some cases.
When I type [space], then it throws an exception and it says stored procedure needs parameter which is not provided.
There are many other characters when I type them it throws exception that invalid character at end of string "my string".
Any suggestion how could I achieve my goal.
Call your stored procedure with sqlcommand to fill your datatable
using (SqlConnection scn = new SqlConnection(connect)
{
SqlCommand spcmd = new SqlCommand("search", scn);
spcmd.Parameters.Add("#blah", SqlDbType.VarChar, -1); //or SqlDbType.NVarChar
spcmd.CommandType = System.Data.CommandType.StoredProcedure;
using (SqlDataAdapter da = new SqlDataAdapter(spcmd))
{
da.Fill(dt);
}
}
static public DataTable searchIt(string STR)
{
string connectionString = McFarlaneIndustriesPOSnamespace.Properties.Settings.Default.McFarlane_IndustriesConnectionString;
SqlConnection con = new SqlConnection(connectionString);
DataTable DT = new DataTable();
con.Open();
SqlCommand command = new SqlCommand("Name_of_Your_Stored_Procedure",con);
command.CommandType=CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#parameter_name",SqlDbType.NVarChar));
command.Parameters[0].Value="Your Value in this case STR";
SqlDataAdapter DA = new SqlDataAdapter(command);
DA.Fill(DT);
con.Close();
return DT;
}
Important :
'parameter_Name' and 'Name_of_Your_Stored_Procedure' should be replaced by yours which you have in database. And value of parameter could be like "abc" (combox.Text)
Command and its type, its text are necessary.
Adding parameters depends upon your stored procedure. They can be 0,1 or more but once they are added their values must be given. conn(connection) can be passed to new SqlCmmand() or new SqlDataAdapter()
No need of things like 'use' and 'exec'
Following me and this link might be helpful in future for stored procedures
http://www.codeproject.com/Articles/15403/Calling-Stored-procedures-in-ADO-NET
Two optional Suggestions for you
use variable name 'list' instead of 'List' (you used) however you will not get problem with this name until you add a namespace using System.Collections.Generic; but you may need to use this namespace in future.
Use only list.Rows[0].ToString(); no need to get itemarray then get value when you are working with data in strings;