After connecting to the database, can I get the name of all the columns that were returned in my SqlDataReader?
var reader = cmd.ExecuteReader();
var columns = new List<string>();
for(int i=0;i<reader.FieldCount;i++)
{
columns.Add(reader.GetName(i));
}
or
var columns = Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).ToList();
There is a GetName function on the SqlDataReader which accepts the column index and returns the name of the column.
Conversely, there is a GetOrdinal which takes in a column name and returns the column index.
You can get the column names from a DataReader.
Here is the important part:
for (int col = 0; col < SqlReader.FieldCount; col++)
{
Console.Write(SqlReader.GetName(col).ToString()); // Gets the column name
Console.Write(SqlReader.GetFieldType(col).ToString()); // Gets the column type
Console.Write(SqlReader.GetDataTypeName(col).ToString()); // Gets the column database type
}
Already mentioned. Just a LINQ answer:
var columns = reader.GetSchemaTable().Rows
.Cast<DataRow>()
.Select(r => (string)r["ColumnName"])
.ToList();
//Or
var columns = Enumerable.Range(0, reader.FieldCount)
.Select(reader.GetName)
.ToList();
The second one is cleaner and much faster. Even if you cache GetSchemaTable in the first approach, the querying is going to be very slow.
If you want the column names only, you can do:
List<string> columns = new List<string>();
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly))
{
DataTable dt = reader.GetSchemaTable();
foreach (DataRow row in dt.Rows)
{
columns.Add(row.Field<String>("ColumnName"));
}
}
But if you only need one row, I like my AdoHelper addition. This addition is great if you have a single line query and you don't want to deal with data table in you code. It's returning a case insensitive dictionary of column names and values.
public static Dictionary<string, string> ExecuteCaseInsensitiveDictionary(string query, string connectionString, Dictionary<string, string> queryParams = null)
{
Dictionary<string, string> CaseInsensitiveDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
// Add the parameters for the SelectCommand.
if (queryParams != null)
foreach (var param in queryParams)
cmd.Parameters.AddWithValue(param.Key, param.Value);
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable dt = new DataTable();
dt.Load(reader);
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
CaseInsensitiveDictionary.Add(column.ColumnName, row[column].ToString());
}
}
}
}
conn.Close();
}
}
catch (Exception ex)
{
throw ex;
}
return CaseInsensitiveDictionary;
}
Use an extension method:
public static List<string> ColumnList(this IDataReader dataReader)
{
var columns = new List<string>();
for (int i = 0; i < dataReader.FieldCount; i++)
{
columns.Add(dataReader.GetName(i));
}
return columns;
}
For me, I would write an extension method like this:
public static string[] GetFieldNames(this SqlDataReader reader)
{
return Enumerable.Range(0, reader.FieldCount).Select(x => reader.GetName(x)).ToArray();
}
I use the GetSchemaTable method, which is exposed via the IDataReader interface.
You sure can.
protected void GetColumNames_DataReader()
{
System.Data.SqlClient.SqlConnection SqlCon = new System.Data.SqlClient.SqlConnection("server=localhost;database=northwind;trusted_connection=true");
System.Data.SqlClient.SqlCommand SqlCmd = new System.Data.SqlClient.SqlCommand("SELECT * FROM Products", SqlCon);
SqlCon.Open();
System.Data.SqlClient.SqlDataReader SqlReader = SqlCmd.ExecuteReader();
System.Int32 _columncount = SqlReader.FieldCount;
System.Web.HttpContext.Current.Response.Write("SqlDataReader Columns");
System.Web.HttpContext.Current.Response.Write(" ");
for ( System.Int32 iCol = 0; iCol < _columncount; iCol ++ )
{
System.Web.HttpContext.Current.Response.Write("Column " + iCol.ToString() + ": ");
System.Web.HttpContext.Current.Response.Write(SqlReader.GetName( iCol ).ToString());
System.Web.HttpContext.Current.Response.Write(" ");
}
}
This is originally from: http://www.dotnetjunkies.ddj.com/Article/B82A22D1-8437-4C7A-B6AA-C6C9BE9DB8A6.dcik
It is easier to achieve it in SQL
var columnsList = dbContext.Database.SqlQuery<string>("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'SCHEMA_OF_YOUE_TABLE' AND TABLE_NAME = 'YOUR_TABLE_NAME'").ToList();
Related
I have this code which shows the database I created using SqlClient, but I don't know how I could check if a column was added as nullable or not nullable in the table.
string[] defaultTables = { "AspNetUsers", "AspNetRoleClaims", "AspNetUserClaims", "AspNetUserLogins", "AspNetUserRoles", "AspNetUserTokens", "AspNetRoles", "__EFMigrationsHistory" };
dblist.ForEach((string DbName) => {
strConnection = $#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog={DbName};Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
using (SqlConnection conn = new SqlConnection(strConnection)) {
conn.Open();
using (cmdSql = new SqlCommand(strConnection, conn)) {
DataTable dt = conn.GetSchema("Tables");
foreach (DataRow row in dt.Rows) {
string tablename = row[2].ToString();
if (!defaultTables.Any(x => x == tablename)) {
tables.Add(tablename);
using (cmdSql = new SqlCommand($"Select * FROM {tablename}", conn)) {
SqlDataReader reader = cmdSql.ExecuteReader();
if (reader.Read()) {
for (int i = 0; i < reader.FieldCount; i++)
Console.Write(reader.GetName(i) + " " + reader.GetDataTypeName(i) + $"" + ',');
}
}
}
}
}
}
});
Your approach seems a bit complicated to extract that kind of information. We have the INFORMATION_SCHEMA views in the database that contains exactly the information required.
So for example:
using(SqlConnection con = new SqlConnection(.....))
{
con.Open();
DataTable dt = new DataTable();
// Get info on all tables...
SqlCommand cmd = new SqlCommand(#"SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'", con);
dt.Load(cmd.ExecuteReader());
foreach (DataRow r in dt.Rows)
{
// Get info on the current table's columns
string sqltext = $"SELECT * FROM INFORMATION_SCHEMA.Columns
WHERE TABLE_NAME = '{r.Field<string>("TABLE_NAME")}'";
SqlCommand cmd1 = new SqlCommand(sqltext, con);
DataTable dt1 = new DataTable();
dt1.Load(cmd1.ExecuteReader());
foreach (DataRow x in dt1.Rows)
{
Console.WriteLine($"Table:{x.Field<string>("TABLE_NAME")}");
Console.WriteLine($"Column:{x.Field<string>("COLUMN_NAME")}");
Console.WriteLine($"Data type:{x.Field<string>("DATA_TYPE")}");
Console.WriteLine($"Nullable:{x.Field<string>("IS_NULLABLE")}");
}
}
It isn't 100% reliable, but GetSchemaTable() on the data-reader can show this, if it knows (which isn't always); specifically, you're looking for the AllowDBNull property. It isn't a great API to look at, though - it returns a DataTable that you need to investigate, so it might need a bit of playing to find what you are after.
citation
C# noob here. I am trying to get some data from a SQL Data Base using the code below, however i get the error: System.InvalidOperationException: 'Invalid attempt to call Read when reader is closed.'
After searching for the answer online, i found nothing, maybe someone can see what is wrong?
The error happens at: while (rdr.Read())
Thank you!
internal List<string> GetInflationByYearData(string Country, int StartYear,
int EndYear)
{
string CS =
ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("SpCountryData", con)
{
CommandType = System.Data.CommandType.StoredProcedure
};
cmd.Parameters.AddWithValue("#act",
"getInflationByYearData");
cmd.Parameters.AddWithValue("#Country", Country);
cmd.Parameters.AddWithValue("#startYear", StartYear);
cmd.Parameters.AddWithValue("#endYear", EndYear);
var table = new DataTable();
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
{
table.Load(rdr);
};
}
//con.Close();
List<string> labelList = new List<string>();
List<string> valueList = new List<string>();
if (table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
string label = row["Year"].ToString();
string value = row["Percentage"].ToString();
labelList.Add(label);
valueList.Add(value);
}
}
List<string> list = new List<string>();
StringBuilder sbLabels = new StringBuilder();
foreach (string lbl in labelList)
{
sbLabels.Append(lbl + ",");
}
StringBuilder sbValues = new StringBuilder();
foreach (string val in valueList)
{
sbValues.Append(val + ",");
}
list.Add(sbLabels.ToString().Substring(0, sbLabels.Length -
1));
list.Add(sbValues.ToString().Substring(0, sbValues.Length -
1));
return list;
}
}
Load consumes the reader to completion; you should either have a while (rdr.Read()) loop, or call table.Load(rdr), but: not both. Load basically does that same loop internally.
So: remove the loop - just use Load.
However! If all you want is the data as List<string>, loading it into a DataTable seems like an unnecessary step (DataTable is not lightweight) - it seems like you could do that in the reader, or perhaps use other tools (like "dapper") to remove that.
It isn't clear what data types Year and Percentage are, but as a "dapper" example...
class MyRowThing {
public int Year {get;set;}
public decimal Percentage {get;set;}
}
var rawData = con.Query<MyRowThing>("SpCountryData",
new { // params here
act = "getInflationByYearData", Country,
startYear = StartYear, endYear = EndYear
}, commandType: CommandType.StoredProcedure).AsList();
// now loop over rawData, which is a List<MyRowThing> with the data
Replacing:
var table = new DataTable();
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
{
table.Load(rdr);
};
}
//con.Close();
List<string> labelList = new List<string>();
List<string> valueList = new List<string>();
if (table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
string label = row["Year"].ToString();
string value = row["Percentage"].ToString();
labelList.Add(label);
valueList.Add(value);
}
}
With:
con.Open();
List<string> labelList = new List<string>();
List<string> valueList = new List<string>();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
labelList.Add(rdr["Year"].ToString());
valueList.Add(rdr["Percentage"].ToString());
}
Will get rid of the DataTable for a more optimized code.
I am having a hard time getting the codes that I need.
The code that I have created can get ALL the column names that I have listed regardless of having a value or not. What I only wanted to happen was to get the column names that has value(1).
I have this code:
internal static List<string> GetAllRoleTypeFromDatabase()
{
List<string> rolelist = new List<string>();
using (var con = new SQLiteConnection(sqliteConnectionMain.connectionString))
using (var cmd = new SQLiteCommand(con))
{
con.Open();
var _command = con.CreateCommand();
var queryq = string.Format("SELECT * FROM tblUserRoleAccess");
string commandText = queryq;
String sSQL;
sSQL = "SELECT * from tblUserRoleAccess";
cmd.CommandText = sSQL;
var dr = cmd.ExecuteReader();
for (var i = 1; i < dr.FieldCount; i++)
{
if (dr != null)
{
rolelist.Add(dr.GetName(i));
}
}
con.Close();
}
return rolelist;
}
Read a line in the reader (as i understand, there's only one row as a result).
For each column in the row check if its value is 1 (I used int, you should use the real type i.e. bool, string ...):
dr.Read();
for (var i = 1; i < dr.FieldCount; i++)
{
if (dr.GetFieldValue<int>(i) == 1)
{
rolelist.Add(dr.GetName(i));
}
}
I need to do this in my codes;
Get data from 2 columns in a database (laborer and trx_date)
Place the extracted data to a DataTable
Explode the first column in the DataTable
Place exploded_laborer and trx_date to an array with
key->exploded_laborer
exploded=>trx_date
I am able to get to number 3 I just need to do number 4. My code is below:
private void GetLocalData()
{
const string sql = #"SELECT laborer, trx_date from tbl_jobs WHERE trx_date BETWEEN #fromDate AND #toDate";
var laborerDataTable = new DataTable();
using (var conn = new SqliteAccess().ConnectToSqlite())
{
using (var cmd = new SQLiteCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#fromDate", dtpFrom.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#toDate", dtpTo.Value.ToString("yyyy-MM-dd"));
laborerDataTable.Load(cmd.ExecuteReader());
}
}
var exploded = new List<string>();
foreach (DataRow row in laborerDataTable.Rows)
{
exploded.Add(row["laborer"].ToString().Split('|')[0]);
}
}
Your help is very much appreciated.
I think you are supposed to create Dictionary<TKey,TValue> which represents a collection of keys and values. This might do the trick for you
laborerDataTable.AsEnumerable()
.Select(row => laborerDataTable.Columns.Cast<DataColumn>()
.ToDictionary(column => row[laborer] as string
column => row[trx_date] as string))
Thus the complete code might look like
private void GetLocalData()
{
const string sql = #"SELECT laborer, trx_date from tbl_jobs WHERE trx_date BETWEEN #fromDate AND #toDate";
var laborerDataTable = new DataTable();
using (var conn = new SqliteAccess().ConnectToSqlite())
{
using (var cmd = new SQLiteCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#fromDate", dtpFrom.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#toDate", dtpTo.Value.ToString("yyyy-MM-dd"));
laborerDataTable.Load(cmd.ExecuteReader());
}
}
var LabDict = laborerDataTable.AsEnumerable()
.Select(row => laborerDataTable.Columns.Cast<DataColumn>()
.ToDictionary(column => row[laborer] as string
column => row[trx_date] as string))
}
Edit
This is just to create a dummy table.
static DataTable GetTable()
{
// Here we create a DataTable with four columns.
DataTable table = new DataTable();
table.Columns.Add("laborer", typeof(string));
table.Columns.Add("trx_date", typeof(string));
// Here we add five DataRows.
table.Rows.Add("Indocin", "12/12/2010");
table.Rows.Add("Enebrel", "12/1/2011");
table.Rows.Add("Hydralazine", "1/12/2012");
table.Rows.Add("Combivent", "11/12/2013");
table.Rows.Add("Dilantin", "12/11/2014");
return table;
}
normal core C# way
DataTable laborerDataTable = GetTable();
Dictionary<string, string> exploded = new Dictionary<string, string>();
foreach(DataRow row in laborerDataTable.Rows)
{
exploded.Add(row.Field<string>(0), row.Field<string>(1));
}
Also, how do I print each of the row to a console?
foreach(var dct in exploded)
{
Console.WriteLine(dct.Key + " Date is " + dct.Value);
}
If you are expecting the result to a Key-Value pair then why an array? why not a Dictionary<string, string>? You can try something like this :
Dictionary<string, string> laborerDict = laborerDataTable.AsEnumerable()
.ToDictionary(x => x.Field<string>("laborer"),
x => x.Field<string>("trx_date"));
Use in built HashMap in java refer the code below
HashMap map = new HashMap<>();
Map.put(key,value);
And to get data from map use
Map.get(key)
It has a lot of function, maybe you can refer them by searching Java API
when you want to have Key=>Value better you use Dictionary instead of List
var exploded = new Dictionary<string,string>();
foreach (DataRow row in laborerDataTable.Rows)
{
exploded.Add(row["laborer"].ToString(),(row["trx_date"].ToString());
}
I am exporting a table from SQL in a text file, data is exporting fine but column names are not coming in text file.
here is the code:
public static void getfiles()
{
SqlDataReader reader;
string query = "Select * from tTable";
string connStr = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
string strDelimiter = "\"|\"";
string strFilePath = #"path";
StringBuilder sb = new StringBuilder();
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (reader = new SqlCommand(query, conn).ExecuteReader())
{
if (reader.HasRows)
{
Object[] items = new Object[reader.FieldCount];
while (reader.Read())
{
reader.GetValues(items);
foreach (var item in items)
{
sb.Append(item.ToString());
sb.Append(strDelimiter);
}
sb.Append("\n");
}
}
}
conn.Close();
File.WriteAllText(strFilePath, sb.ToString());
}
}
I don't know what changes to make in this method
You simply don't write the column names. This can be achieved by reader.GetName() method like this:
using (reader = new SqlCommand(query, conn).ExecuteReader())
{
for (int i=0; i<reader.FieldCount; i++)
{
sb.Append(reader.GetName(i));
sb.Append(strDelimiter);
}
sb.Append(Environment.NewLine);
// your code here...
if (reader.HasRows)
{
// etc...
}
}
You could include column names in your select statement ....
Select Column1 from(select Sorter = 1, Convert(VARCHAR(max), Column1) as Column1
from TableName union all
select 0, 'Column1') X
order by Sorter, Column1