C# List<int> populated from SQL Stored Procedure results - c#

I'm using C# in VS 2005 (.NET 2.0) and SQL Studio 2005 on an older CMS made in the mid-'00s. I'm tasked with creating a new permission gate that allows only certain users to see certain parts of the site.
I need help populating a List list based on feedback I got when I posted this question: Populate ArrayList from Stored Procedure result set
So, now, how do get get the values from the stored procedure into a List? I realize this is a novice question but I'm a novice...
Any help is greatly appreciated.

Assuming you are getting your results from a DataReader, all you have to do is read each row to add the value to a list.
List<int> ReadList(IDataReader reader)
{
List<int> list = new List<int>();
int column = reader.GetOrdinal("MyColumn");
while (reader.Read())
{
list.Add(reader.GetInt32(column));
}
return list;
}
Remember to dispose of the DataReader when you are done with it.

You can try using the model located on this MSDN page under Using Parameters with a SqlCommand and a Stored Procedure. The example is shown here:
static void GetSalesByCategory(string connectionString, string categoryName)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create the command and set its properties.
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "SalesByCategory"; //Stored Procedure Name
command.CommandType = CommandType.StoredProcedure;
// Add the input parameter and set its properties.
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;
// Add the parameter to the Parameters collection.
command.Parameters.Add(parameter);
// Open the connection and execute the reader.
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
//Instead of displaying to console this is where you would add
// the current item to your list
Console.WriteLine("{0}: {1:C}", reader[0], reader[1]);
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}

it depends on how you have retreived the results
reader?
dataset?
something else?
walk through the results using
foreach (int item in object...) {
List.Add(item);
}
or possibly (I dont remember the exact DataRow syntax off the top of my head...)
foreach (datarow row in object.table[0].rows) {
List.Add(row[0]);
}

IList<int> myInts = new List<int>();
using (IDbConnection connection = new SqlConnection("yourConnectionStringGoesHere"))
{
using (IDbCommand command = new SqlCommand("spName", connection))
{
command.CommandType = CommandType.StoredProcedure;
//command.Parameters.Add(...) if you need to add any parameters to the SP.
connection.Open();
using (IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
myInts.Add(Int32.Parse(reader["someIntField"].ToString()));
}
}
}

Since you already have the table the idea would be to iterate over that table while adding the IDs of the vendor into a list.
List<VendorID_Data_Type> myList = new List<VendorID_Data_Type>();
foreach(DataRow r in GetAllVendors().Rows)
{
myList.Add(r["VendorID"]);
}

What I ended up doing is using a DataTable as an intermediary data type, which is populated by the stored procedure. Then, refactoring the DataTable as the data-source in a foreach loop, I populated the List. I needed to open a second question to get to this conclusion: 2-Column DataTable to List<int> .NET 2.0

Related

How to import Data in csv file into a SQL Server database using C#

I'm trying to import data into a SQL Server database from a .csv file. I have just one problem: for the money row, I am throwing Format.Exception due to wrong format of money variable.
I tried to convert to double I change the period instead of comma, I change in split(); method also semicolon ; instead of comma , but the exception didn't go away. Does anyone know what to do about this?
It is just an experiment.
My .csv file looks like this:
Database table's columns are:
name, second_Name, nickname, money
Code:
public void Import()
{
SqlCommand command = null;
var lineNumber = 0;
using (SqlConnection conn = DatabaseSingleton.GetInstance())
{
// conn.Open();
using (StreamReader reader = new StreamReader(#"C:\Users\petrb\Downloads\E-Shop\E-Shop\dataImport.csv"))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (lineNumber != 0)
{
var values = line.Split(',');
using (command = new SqlCommand("INSERT INTO User_Shop VALUES (#name, #second_Name, #nickname, #money", conn))
{
command.Parameters.Add(new SqlParameter("#name", values[0].ToString()));
command.Parameters.Add(new SqlParameter("#second_Name", values[1].ToString()));
command.Parameters.Add(new SqlParameter("#nickname", values[2].ToString()));
command.Parameters.Add(new SqlParameter("#money", Convert.ToDecimal(values[3].ToString())));
command.Connection = conn;
command.ExecuteNonQuery();
}
}
lineNumber++;
}
}
conn.Close();
}
Console.WriteLine("Products import completed");
Console.ReadLine();
}
I maintain a package Sylvan.Data.Csv that makes it very easy to bulk import CSV data into SQL Server, assuming the shape of your CSV file matches the target table.
Here is some code that demonstrates how to do it:
SqlConnection conn = ...;
// Get the schema for the target table
var cmd = conn.CreateCommand();
cmd.CommandText = "select top 0 * from User_Shop";
var reader = cmd.ExecuteReader();
var tableSchema = reader.GetColumnSchema();
// apply the schema of the target SQL table to the CSV data.
var options =
new CsvDataReaderOptions {
Schema = new CsvSchema(tableSchema)
};
using var csv = CsvDataReader.Create("dataImport.csv", options);
// use sql bulk copy to bulk insert the data
var bcp = new SqlBulkCopy(conn);
bcp.BulkCopyTimeout = 0;
bcp.DestinationTableName = "User_Shop";
bcp.WriteToServer(csv);
On certain .NET framework versions GetColumnSchema might not exist, or might throw NotSupportedException. The Sylvan.Data v0.2.0 library can be used to work around this. You can call the older GetSchemaTable API, then use the Sylvan.Data.Schema type to convert it to the new-style schema IReadOnlyCollection<DbColumn>:
DataTable schemaDT = reader.GetSchemaTable();
var tableSchema = Schema.FromSchemaTable(schemaDT);
Try this:
SqlParameter moneyParam = new SqlParameter("#money", SqlDbType.Money);
moneyParam.Value = SqlMoney(Convert.ToDecimal(values[3].ToString()))
command.Parameters.Add(moneyParam)
Not sure if it'll work, but it seems to make sense to me.
The problem being that when you use the constructor for the SQL parameter, I think it assumes the type of your variable, so in this case, as you're passing a double or whatever, the equivalent DB type is 'decimal', however your DB schema will be using the DB type 'money', so explicitly setting the DB type in your parameter constructor should help.

Pass GUID into stored procedure method C#

I am mostly new to c# so i am looking for some guidance here. I am testing a method that i need to pass a list of guids to and run a stored procedure that returns values based on the guids i pass to it which i can then print to the console. I can get the method to work when i pass only one guid but when i pass a list of guids it seems to not work.
I feel like i am lacking some understanding here around how i should pass the list of guids and return it. I get conversion errors trying to return List.
Here is how far i have got but i feel like i am stuck now and cant progress anymore from any info i have found online.
class Program
{
static void Main(string[] args)
{
List<Guid> tempguid = new List<Guid>();
tempguid.Add(Guid.Parse("472USFA0-B705-9A73-ABD4-3B1870AF1409"));
tempguid.Add(Guid.Parse("FA97E6BB-0875-5UB9-967A-87ECC396F9F0"));
GetValue(tempguid);
Console.WriteLine(GetValue);
}
public void GetValue(List<Guid> tempguid)
{
using (SqlConnection conn = new SqlConnection("connection string here"))
{
conn.Open();
SqlCommand cmd = new SqlCommand("stored procedure here", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#id", tempguid));
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((string)rdr["value"]);
}
Console.ReadLine();
}
}
}
}
Should i be passing the list like this GetValue(List tempguid)?
EDIT
ok so if i use a TVP.
Something like:
CREATE TYPE [dbo].[Identity] AS TABLE(
[Id] [uniqueidentifier] NOT NULL
)
GO
Then my procedure will look something along the lines of:
CREATE OR ALTER PROCEDURE [dbo].[procedure_name]
#id dbo.Identity READONLY
as
SELECT t.[id]
,t.[value]
FROM [dbo].[table1] t
Inner Join #id i on i.Id = t.id
How do i use this TVP in c# for my stored procedure?
you need a foreach loop on GUID list. Try like:
foreach (var g in tempguid)
{
cmd.Parameters.Add(new SqlParameter("#id", g));
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((string)rdr["value"]);
}
Console.ReadLine();
}
cmd.Parameteres.Clear();
}
You can't pass a list to sp. You need to convert your guids into a csv string example:
var param = string.Join(", ", tempguid);
Then
cmd.Parameters.Add(new SqlParameter("#id", param));
Then after receiving your parameter on the sp pass into String_Split. Goodluck!
Passing values to TVPs from ADO .NET is very straightforward, and requires very little extra code compared to passing data to regular parameters.
For the data type you specify SqlDbType.Structured.
You specify the name of the table type in the TypeName property of the parameter.
You set the Value property of the parameter to something suitable.
As mentioned in the above link System.Data.SqlClient supports populating table-valued parameters from DataTable, DbDataReader or IEnumerable<T> \ SqlDataRecord objects.
If you already have the list of Guids coming from other sources in your case tempguid then you could use a datatable to pass the details to stored procedure.
DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("Id", typeof(Guid)));
// populate DataTable from your List here
foreach (var id in tempguid)
tvp.Rows.Add(id);
And change the ADO.NET code like below -
conn.Open();
SqlCommand cmd = new SqlCommand("dbo.tvpProcedure", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#id";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "dbo.testTVP";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((string)rdr["value"]);
}
Console.ReadLine();
}
Side notes:
Looks like the GUIDs you have shown in the code seems invalid as they contain non-
hexadecimal values.
472USFA0-B705-9A73-ABD4-3B1870AF1409
^^
FA97E6BB-0875-5UB9-967A-87ECC396F9F0
^
Change the Type name something meaningful instead of dbo.Identity in my case I used
dbo.testTVP
Further information -
http://www.sommarskog.se/arrays-in-sql-2008.html

Need to select a value from table and store it into variable

Hello everyone I am currently working on some testing project and I am having a little problem. Using selenium, I need to SendKey in specific element but instead of fixed value i need to use value (data) from my database. Can anyone help me with how to retrieve single value from database and store it in a variable so i can use it later.
Thank you and sorry for a noobish question - see code below:
SqlConnection conn = new SqlConnection();
SqlCommand command;
SqlDataReader dataReader;
conn.ConnectionString = "Server=******;Database=****;User ID=sqlserver;password=****;MultipleActiveResultSets=true;");
string query = "select RequestID, from AutomaticPayment where RequestID ='1230322'";
DataTable dt = new DataTable();
command = new SqlCommand(query, conn);
conn.Open();
dataReader = command.ExecuteReader();
dt.Load(dataReader);
driver.FindElement(By.Id("requestID")).SendKeys(VALUE FROM DATABASE);
You can use the following code
using (SqlConnection connection = new SqlConnection(_connectionString))
{
SqlDataAdapter sda = new SqlDataAdapter(query, connection);
connection.Open();
SqlCommand cmd = new SqlCommand(query, connection);
try
{
result = cmd.ExecuteScalar().ToString();
}
catch(NullReferenceException n)
{
result = "";
}
}
ExecuteScaler gets you the first column of the first row and additional columns are ignored. Use the value from result in your SendKeys()
Use conditions to limit the result:
Select data
SELECT TOP 1 RequestID FROM AutomaticPayment // Always returns 1 row
Or
SELECT RequestID FROM AutomaticPayment WHERE Id = 123 // Id must be unique to return 1 row
And maybe other ways.
Get value
var value = dt.Rows[0][1];
Or
var value = dt.Rows[0]["RequestID"];
From what i worked on with SqlCommand just do the following :
int yourId = 0;
dataReader = command.ExecuteReader()
while(dataReader.Read())
{
yourId = dataReader.GetInt32(0);
}
With that, you should have your value set to the first column of the dataReader. (that is selected thanks to your query, since you are requesting on a specific id, i guess it will return only one column
there is many other type available for Reader : Reader Microsoft Doc
And if you have in the futur many data to collect, use the ORM entity framework, work well for me
Source
EDIT :
Since you are only querying one data, maybe the solution of #work_ishaan is better than mine in this case, check it out.

How to execute a generic SQL Command in C#?

I am trying to write a Method that simply accepts a string which is an SQL Command and runs it against the pre-defined Database/Server ... This is what I have so far:
public class TSqlConnector : IDbConnector
{
private readonly string _connectionString;
public TSqlConnector(string conn)
{
_connectionString = conn;
}
public IEnumerable<object[]> ExecuteCommand(string query)
{
var res = new List<object[]>();
try
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
sql.Open();
SqlCommand cmd = new SqlCommand(query, sql);
var reader = cmd.ExecuteReader();
DataTable tbl = new DataTable();
while (reader.Read())
{
var dr = tbl.NewRow();
dr.ItemArray = new object[reader.FieldCount];
reader.GetValues(dr.ItemArray);
res.Add(dr.ItemArray);
}
}
return res;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
}
This code, however, gives me an error saying that
Input array is longer than the number of columns in this table.
I googled the error message, apparently I first have to define the DataTable's columns, using tbl.Add("ColumnName", typeof(type));
This however, completely undermines what I was trying to do - writing a generic version. All I wanted was some kind of construct which contains the information I would get from the SqlServer if I typed the same command into SSMS, I don't really care what hoops I have to jump through to read the data in C#; Using an object-Array for each row, having to manually cast each object into a string, int or whatever is perfectly acceptable, even a CSV-like string would be just fine
The only thing I don't want to do is add a definition for the table or a fixed amount of row. Each method that uses ExecuteCommand() will have to know what type of object-array is returned and that's fine but adding some complex data structure containing types and column names in addition to the SQL Commands seems like overkill.
Is there some easier way to achieve this?
What you have is an IDataReader from your cmd.ExecuteReader();
To load the results into a DataTable, you can use the Load method as follows:
var reader = cmd.ExecuteReader();
DataTable tbl = new DataTable();
tbl.Load(reader);
// now tbl contains the corresponding columns and rows from your sql command.
// Then you can return the ItemArrays from each row;
return tbl.Rows.Cast<DataRow>().Select(row => row.ItemArray);
I've used code like this to input a generic SQL query and return the results as a datatable. Of course, you'll have to parse out the results how you need them.
private DataTable QueryToTable(string sql, string cs)
{
var ds = new DataSet();
using (var adapter = new SqlDataAdapter(sql, cs))
{
adapter.Fill(ds);
}
return ds.Tables(0);
}

C# getAll Function advice

Hi I am trying to create CRUD functions in C# but am stuck on my first one which is FetchALL, as so far it says not all code path returns a value.
Heres my code so far
public SqlDataReader FetchAll(string tableName)
{
using (SqlConnection conn = new SqlConnection(_ConnectionString,))
{
string query = "SELECT * FROM " + tableName;
SqlCommand command = new SqlCommand(query, conn);
using (SqlDataReader reader = command.ExecuteReader())
conn.Open();
conn.Close();
}
}
}
}
I can give you more information, thanks
You have a return type of SqlDataReader, but you aren't returning anything anywhere in your code. At the very least you should declare your data reader and return it like this:
public SqlDataReader FetchAll(string tableName)
{
SqlDataReader reader;
using (SqlConnection conn = new SqlConnection(_ConnectionString))
{
string query = "SELECT * FROM " + tableName;
// added using block for your command (thanks for pointing that out Alex K.)
using (SqlCommand command = new SqlCommand(query, conn))
{
conn.Open(); // <-- moved this ABOVE the execute line.
reader = command.ExecuteReader(); // <-- using the reader declared above.
//conn.Close(); <-- not needed. using block handles this for you.
}
}
return reader;
}
Note, I've noted a few other problems I saw as well, which you can see by my comments.
Also, I want to point out something very important: you should always avoid string concatenation in queries as this opens you up to the risk of a SQL injection attack (as gmiley has duly pointed out). In this case, you should create an enum which contains values associated with all the possible table names, and then use a dictionary to look up the table names based on their enum values. If a user provides an invalid/unknown value, you would then thrown an argument exception.
This isn't the end of your problems, though (as Default has pointed out). You can't create the connection in a using block, which disposes and closes as soon as it exits the block, and then use the SqlDataReader that is returned from the method. If I were you, I'd return a DataSet instead of a SqlDataReader. Here's how I'd do it:
First, create your enum of possible table values:
public enum Table
{
FirstTable,
SecondTable
}
And a dictionary that maps table enum values to the table names (which you will populate in your static constructor):
private static Dictionary<Table, string> _tableNames = new Dictionary<Table, string>(); // populate this in your static constructor.
And then here is your method to fetch the data:
public static System.Data.DataSet FetchAll(Table fromTable)
{
var ret = new System.Data.DataSet();
using (var conn = new System.Data.SqlClient.SqlConnection(_connectionString))
{
string tableName = "";
if (!_tableNames.TryGetValue(fromTable, out tableName)) throw new ArgumentException(string.Format(#"The table value ""{0}"" is not known.", fromTable.ToString()));
string query = string.Format("SELECT * FROM {0}", tableName);
using (var command = new System.Data.SqlClient.SqlCommand(query, conn))
{
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
{
adapter.Fill(ret);
}
}
}
return ret;
}
One final note, I'd advise you name your class-level variables with lower camel case per convention, e.g. _connectionString.
Firstly you aren't returning anything from the method. I'd add, are you sure you want to return a SqlDataReader? It is declared within a using block, so it will be closed by the time you return it anyway. I think you should re-evaluate what this function should return.
You need a return statment for the method to return a value.

Categories

Resources