How to populate a gridview with table data from sql - c#

I want to populate a gridview with table data from SQL. I tried just returning my SqlDataReader object and using it as my datasource, but I am getting errors that the reader has been closed. I wanted to convert this to a DataSet and just return a dataset, but I couldn't find an easy way to convert the row data to a dataset. I've also read that DataSets are dead in .NET 3.5/4.0, is this true?
Here's my Data Layer method. It would be awesome if I could return something useable as a datasource:
public SqlDataReader GetSites()
{
SqlConnection sqlCon = null;
SqlDataReader rdr = null;
try
{
sqlCon = new SqlConnection(StoredProcedures.conString);
sqlCon.Open();
SqlCommand cmd = new SqlCommand("GetSites", sqlCon);
cmd.CommandType = CommandType.StoredProcedure;
rdr = cmd.ExecuteReader();
return rdr;
}
finally
{
if (sqlCon != null)
{
sqlCon.Close();
}
//if (rdr != null)
//{
// rdr.Close();
//}
}
}

As Carlos Munoz has stated, you closed your sql connection. You need the SQL connection opened in order for the reader to read. Simply comment out
if (sqlCon != null)
{
sqlCon.Close();
}
and you should be fine.
Another alternative is to use SqlDataAdapter which I prefer.
Here is an example...
public static DataSet GetDataSet(string sql, DatabaseType database)
{
using ( var connection = new SqlConnection( GetConnectionString(database) ) )
{
using (var adapter = new SqlDataAdapter(sql, connection))
{
var temp = new DataSet();
adapter.Fill(temp);
return temp;
}
}
}
Just set the data source of your data grid to the Data set's table returned.
DGV.DataSource = DatabaseFunction.GetDataSet(sql, DatabaseType.Outage).Tables[0].DefaultView;

I suggest using a Typed-Dataset with typed table adapters. Add a new Typed-DataSet into your project and drop the tables from the server explorer in visual studio directly into your typed-dataset and configure it. You can then use that type dataset as the datasource of your datagrid

dataAdapter = new SqlDataAdapter(sqlQuery, DatabaseConnectionString);
SqlCommandBuilder builder = new SqlCommandBuilder(dataAdapter);
builder.GetUpdateCommand();
dataSet = new DataSet();
DataAdapter.Fill(dataSet);
dataGridView.DataSource = dataSet.Tables[0];
SqlCommandBuilder should be used if you want to update changes made in dataGridView back to SqlDatabase! Than you can write:
dataAdapter.Update(dataSet);
If you want to use new stuff, you can generate object model that corresponds to you SqlDatabase with SqlMetal tool. This will generate classes that have members as tables is your dataabse. After that you use LINQ query that is used as datasource of you dataGeidView.

Related

How to get the original schema on SQL Server from SELECT that fills DataTable?

I have the following auxiliary function for loading DataTable based on SQL SELECT against a SQL Server database (a bit simplified):
static private DataTable GetData_(string connectionString, string sqlSelect, bool doFillSchema = false)
{
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlDataAdapter adapter = new SqlDataAdapter(sqlSelect, connection))
{
DataTable table = new DataTable();
if (doFillSchema)
adapter.FillSchema(table, SchemaType.Mapped);
adapter.Fill(table);
return table;
}
}
The later goal is to pass the returned table to another function that should export the content into a DBF table (that is to CREATE TABLE and write the content with some corrections related to different capabilities of the engines).
The problem is that the implementation changes the original say numeric(10, 2) column type into Decimal in the schema. I would like to preserve the original.
If I understand it well, I probably need to get another DataTable schemaTable with the original schema (before the column types get changed by by the adapter). My idea was to modify the function like that:
static private DataTable GetData_(string connectionString, string sqlSelect,
bool doFillSchema, ref DataTable schemaTable)
{
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlDataAdapter adapter = new SqlDataAdapter(sqlSelect, connection))
{
DataTable table = new DataTable();
if (doFillSchema)
??? fill the schema table
adapter.Fill(table);
return table;
}
}
How the functionality should be implemented? Should the SqlDataAdapter be used for the purpose? Or should it be done differently?
So fare, I have this self-answer. If you have a better solution, I will be happy to accept it. ;)
static private DataTable GetData_(string connectionString, string sqlSelect, Hashtable arg,
bool doFillSchema, ref DataTable schemaTable)
{
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(sqlSelect, connection))
{
connection.Open();
// If we want the schema, do the extra step.
if (doFillSchema)
{
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo))
schemaTable = reader.GetSchemaTable();
}
// Use the adapter to fill the result table..
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataTable table = new DataTable();
// Set the SELECT arguments.
if (arg != null)
{
foreach (DictionaryEntry item in arg)
{
adapter.SelectCommand.Parameters.AddWithValue(item.Key.ToString(), item.Value);
}
}
// Get the result of the SQL query.
adapter.Fill(table);
return table;
}
}
}

creating a list which works as inparameter for gridview

I'm working on a school development project and I'm quite new to development. I have been reading online but can't find the answer I'm looking for.
So far I have created a listbox in my Windows Forms application which I want to select all the values from one of my columns, and these should work as a inparameter to display data in my dataGridView based on the parameter.
I have created 70% of my project and this functionality is what is left. My database is in Azure and I can write to it and add new rows, but I can't read anything to my application when I run it.
code for listview, at first I just want to be able to select. Later on somehow write the choosen parameter to a variable that I can use as a condition in my dataGridView.
This is the code for my gridview so far I just want to display all data in it, but it's not showing anything.
namespace MyNamespace
{
public partial class CompanyForm : Form
{
public CompanyForm()
{
InitializeComponent();
}
//Connection String
string cs = ConfigurationManager.ConnectionStrings["ConnectionString"].
ConnectionString;
private void createCompany_Click_1(object sender, EventArgs e)
{
if (textBoxCompanyName.Text == "")
{
MessageBox.Show("Fill information");
return;
}
using (SqlConnection con = new SqlConnection(cs))
{
//Create SqlConnection
con.Open();
SqlCommand cmd = new SqlCommand
(
"insert into dbo.Company (companyName)
values(#companyName)", con);
cmd.Parameters.AddWithValue
(
"#companyName",
textBoxCompanyName.Text);
SqlDataAdapter adapt = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
adapt.Fill(ds);
MessageBox.Show("GJ");
}
}
// The code that is not filling my datagrid
private void dataEmployees_Load()
{
using (SqlConnection con = new SqlConnection(cs))
{
con.Open();
SqlCommand cmd = new SqlCommand
(
"Select fname,ename FROM dbo.Users", con
);
SqlDataReader dr = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
dataEmployees.DataSource = dt;
}
}
}
}
My connection string is working it's already being able to insert data to the tables that I have.
The problem why you Grid isn't shown any data is that you try to bind a SqlDataReader to it. This isn't working, because the Grid doesn't support this as DataSource.
What you need as DataSource is DataTable, IList<T>, IBindingList<T>. In your case the DataTable would be the easiest solution. Try this out:
protected void DataEmployees()
{
using (SqlConnection con = new SqlConnection(cs))
{
con.Open();
SqlCommand cmd = new SqlCommand
(
"Select firstname,lastname FROM employees",con
);
SqlDataReader dr = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
dataEmployees.DataSource = dt;
}
}
Notice that Methods are written Uppercase in C#. Further notice that you don't need to close the connection manually if you use a using-block. On the end of the using-block it's automatically closed/disposed.

Retrieve column names and types from SQL Server to DataTable C#

What would be the best way of building an empty DataTable based on existing table in SQL server? My current try is this one, which is just retyping this manually, so it is not great, especially for large data sets.
private DataTable createEmptyReadingDataTableReadyToSaveToDb()
{
dtbl.Columns.Add("ProductId", typeof(string));
dtbl.Columns.Add("Price", typeof(float));
dtbl.Columns.Add("Revenue", typeof(float));
dtbl.Columns.Add("URL", typeof(string));
// etc ....
return dtbl;
}
I read today about schemas, which seem natural for this task. It got me more confused than I thought this would be. Anyways such approach below returns a datatable returning broad set of information about the dataset, but I do not find there accessors to get information about the interesting DataTable (below). Probably I do something wrong.
private static DataTable getReadingTableFromSchema()
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalDbConnnectionString"].ConnectionString))
{
string sql = "SELECT * FROM [Readings]";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader reader = cmd.ExecuteReader();
DataTable dtbl = reader.GetSchemaTable();
return dtbl;
}
}
I could also just try the approach with DataAdapter reading the data, then filling it (basically copying the DataTable) and then deleting all rows to have the table empty - but this would definitely affect the performance. What would be the right solution?
you can use DataAdapter and fill DataTable with full schema:
private static DataTable getReadingTableFromSchema()
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalDbConnnectionString"].ConnectionString))
{
string sql = "SELECT * FROM [Readings]";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
DbDataAdapter da = new SqlDataAdapter(cmd);
DataTable dtbl = new DataTable();
da.FillSchema(dtbl, SchemaType.Source);
return dtbl;
}
}
And, I suggest you to use "using" for command and adapter too
Using a bit of LINQ
using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalDbConnnectionString"].ConnectionString))
{
conn.Open();
using (var reader = new SqlCommand("SELECT * FROM [Readings] WHERE 1 = 0", conn).ExecuteReader())
{
var dataColumns = Enumerable.Range(0, reader.FieldCount)
.Select(i => new DataColumn(reader.GetName(i), reader.GetFieldType(i)))
.ToArray();
var dataTable = new DataTable("Readings");
dataTable.Columns.AddRange(dataColumns);
}
}

Copying a row from one database table to its twin on a different server through C#

I'm trying to design a method to allow me to take a row from a SQL database using a SELECT. I then want to INSERT the returned row into an identical table on a database which resides on a different server.
My server connection and SELECT statement both work; I can view the row I want copied in a console window.
The issue I'm having is figuring out what data structure I need to use in C#, how to populate this data structure with the SELECT, and finally how to use that data structure to INSERT the row.
I'd like to keep the option for multiple rows open, but for now if I could get a single row transferring across it would be fantastic.
Thank you.
Edit: I've been able to work through this myself after a bunch of research. I've included my code below in the heop it helps someone else, but although it works, it is far from ideal. I am an amateur and it is not yet complete.
static public void CopyDatabaseRows(string ConnectionStringDEV, string ConnectionStringLOCAL, string queryString)
{
//Connect to first database table to retreive row/rows and populate dataset + datatable.
DataSet dataSet = new DataSet();
SqlConnection conn = new SqlConnection(ConnectionStringDEV);
conn.Open();
SqlCommand command = new SqlCommand(queryString, conn);
DataTable dataTable = new DataTable();
SqlDataAdapter dataAdapter = new SqlDataAdapter(queryString, conn);
dataAdapter.FillSchema(dataSet, SchemaType.Mapped);
dataAdapter.Fill(dataSet, "dbo.FileRegister");
dataTable = dataSet.Tables["dbo.FileRegister"];
conn.Close();
//Connect to second Database and Insert row/rows.
SqlConnection conn2 = new SqlConnection(ConnectionStringLOCAL);
conn2.Open();
SqlBulkCopy bulkCopy = new SqlBulkCopy(conn2);
bulkCopy.DestinationTableName = "dbo.FileRegister";
bulkCopy.WriteToServer(dataTable);
}
I'd suggest you use something like Simple.Data (https://github.com/markrendle/Simple.Data) to do this really easily. Because it uses C# dynamics, you can just load the dynamic type, and insert it directly into a second Simple.Data connection without worrying about type conversion.
Something like:
var db = Database.OpenConnection("data source=.;initial catalog=Xyz;etc");
var db2 = Database.OpenConnection("data source=somewhereElse;initial catalog=Xyz;etc");
dynamic user = db.Users.FindById(1);
db2.Users.Insert(user);
It works perfectly... I modified this code... Use it...
string local = "Server=destinationservername;Database=destinationserverdb;Uid=sa;Pwd=<Password>;
string dev = "Server=sourceservername;Database=sourceserverdb;Uid=sa;Pwd=Password;
private void btncopy_Click(object sender, EventArgs e)
{
CopyDatabaseRows(dev, local, query);
}
static public void CopyDatabaseRows(string ConnectionStringDEV, string ConnectionStringLOCAL, string queryString)
{
//Connect to first database table to retreive row/rows and populate dataset + datatable.
DataSet dataSet = new DataSet();
SqlConnection conn = new SqlConnection(ConnectionStringDEV);
conn.Open();
SqlCommand command = new SqlCommand(queryString, conn);
DataTable dataTable = new DataTable();
SqlDataAdapter dataAdapter = new SqlDataAdapter(queryString, conn);
dataAdapter.FillSchema(dataSet, SchemaType.Mapped);
dataAdapter.Fill(dataSet, "dbo.DeviceLogs");
dataTable = dataSet.Tables["dbo.DeviceLogs"];
conn.Close();
//Connect to second Database and Insert row/rows.
SqlConnection conn2 = new SqlConnection(ConnectionStringLOCAL);
conn2.Open();
SqlBulkCopy bulkCopy = new SqlBulkCopy(conn2);
bulkCopy.DestinationTableName = "dbo.DeviceLogs";
bulkCopy.WriteToServer(dataTable);
MessageBox.Show(dataTable.Rows.Count.ToString() + " rows copied successfully", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
}

Read SQL Table into C# DataTable

I've read a lot of posts about inserting a DataTable into a SQL table, but is there an easy way to pull a SQL table into a .NET DataTable?
Here, give this a shot (this is just a pseudocode)
using System;
using System.Data;
using System.Data.SqlClient;
public class PullDataTest
{
// your data table
private DataTable dataTable = new DataTable();
public PullDataTest()
{
}
// your method to pull data from database to datatable
public void PullData()
{
string connString = #"your connection string here";
string query = "select * from table";
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
conn.Close();
da.Dispose();
}
}
var table = new DataTable();
using (var da = new SqlDataAdapter("SELECT * FROM mytable", "connection string"))
{
da.Fill(table);
}
Lots of ways.
Use ADO.Net and use fill on the data adapter to get a DataTable:
using (SqlDataAdapter dataAdapter
= new SqlDataAdapter ("SELECT blah FROM blahblah ", sqlConn))
{
// create the DataSet
DataSet dataSet = new DataSet();
// fill the DataSet using our DataAdapter
dataAdapter.Fill (dataSet);
}
You can then get the data table out of the dataset.
Note in the upvoted answer dataset isn't used, (It appeared after my answer)
It does
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
Which is preferable to mine.
I would strongly recommend looking at entity framework though ... using datatables and datasets isn't a great idea. There is no type safety on them which means debugging can only be done at run time. With strongly typed collections (that you can get from using LINQ2SQL or entity framework) your life will be a lot easier.
Edit: Perhaps I wasn't clear: Datatables = good, datasets = evil. If you are using ADO.Net then you can use both of these technologies (EF, linq2sql, dapper, nhibernate, orm of the month) as they generally sit on top of ado.net. The advantage you get is that you can update your model far easier as your schema changes provided you have the right level of abstraction by levering code generation.
The ado.net adapter uses providers that expose the type info of the database, for instance by default it uses a sql server provider, you can also plug in - for instance - devart postgress provider and still get access to the type info which will then allow you to as above use your orm of choice (almost painlessly - there are a few quirks) - i believe Microsoft also provide an oracle provider. The ENTIRE purpose of this is to abstract away from the database implementation where possible.
Vendor independent version, solely relies on ADO.NET interfaces; 2 ways:
public DataTable Read1<T>(string query) where T : IDbConnection, new()
{
using (var conn = new T())
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = query;
cmd.Connection.ConnectionString = _connectionString;
cmd.Connection.Open();
var table = new DataTable();
table.Load(cmd.ExecuteReader());
return table;
}
}
}
public DataTable Read2<S, T>(string query) where S : IDbConnection, new()
where T : IDbDataAdapter, IDisposable, new()
{
using (var conn = new S())
{
using (var da = new T())
{
using (da.SelectCommand = conn.CreateCommand())
{
da.SelectCommand.CommandText = query;
da.SelectCommand.Connection.ConnectionString = _connectionString;
DataSet ds = new DataSet(); //conn is opened by dataadapter
da.Fill(ds);
return ds.Tables[0];
}
}
}
}
I did some performance testing, and the second approach always outperformed the first.
Stopwatch sw = Stopwatch.StartNew();
DataTable dt = null;
for (int i = 0; i < 100; i++)
{
dt = Read1<MySqlConnection>(query); // ~9800ms
dt = Read2<MySqlConnection, MySqlDataAdapter>(query); // ~2300ms
dt = Read1<SQLiteConnection>(query); // ~4000ms
dt = Read2<SQLiteConnection, SQLiteDataAdapter>(query); // ~2000ms
dt = Read1<SqlCeConnection>(query); // ~5700ms
dt = Read2<SqlCeConnection, SqlCeDataAdapter>(query); // ~5700ms
dt = Read1<SqlConnection>(query); // ~850ms
dt = Read2<SqlConnection, SqlDataAdapter>(query); // ~600ms
dt = Read1<VistaDBConnection>(query); // ~3900ms
dt = Read2<VistaDBConnection, VistaDBDataAdapter>(query); // ~3700ms
}
sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());
Read1 looks better on eyes, but data adapter performs better (not to confuse that one db outperformed the other, the queries were all different). The difference between the two depended on query though. The reason could be that Load requires various constraints to be checked row by row from the documentation when adding rows (its a method on DataTable) while Fill is on DataAdapters which were designed just for that - fast creation of DataTables.
Centerlized Model: You can use it from any where!
You just need to call Below Format From your function to this class
DataSet ds = new DataSet();
SqlParameter[] p = new SqlParameter[1];
string Query = "Describe Query Information/either sp, text or TableDirect";
DbConnectionHelper dbh = new DbConnectionHelper ();
ds = dbh. DBConnection("Here you use your Table Name", p , string Query, CommandType.StoredProcedure);
That's it. it's perfect method.
public class DbConnectionHelper {
public DataSet DBConnection(string TableName, SqlParameter[] p, string Query, CommandType cmdText) {
string connString = # "your connection string here";
//Object Declaration
DataSet ds = new DataSet();
SqlConnection con = new SqlConnection();
SqlCommand cmd = new SqlCommand();
SqlDataAdapter sda = new SqlDataAdapter();
try {
//Get Connection string and Make Connection
con.ConnectionString = connString; //Get the Connection String
if (con.State == ConnectionState.Closed) {
con.Open(); //Connection Open
}
if (cmdText == CommandType.StoredProcedure) //Type : Stored Procedure
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = Query;
if (p.Length > 0) // If Any parameter is there means, we need to add.
{
for (int i = 0; i < p.Length; i++) {
cmd.Parameters.Add(p[i]);
}
}
}
if (cmdText == CommandType.Text) // Type : Text
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = Query;
}
if (cmdText == CommandType.TableDirect) //Type: Table Direct
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = Query;
}
cmd.Connection = con; //Get Connection in Command
sda.SelectCommand = cmd; // Select Command From Command to SqlDataAdaptor
sda.Fill(ds, TableName); // Execute Query and Get Result into DataSet
con.Close(); //Connection Close
} catch (Exception ex) {
throw ex; //Here you need to handle Exception
}
return ds;
}
}

Categories

Resources