I have a result of IEnumerable from a stored procedure and i am looping through the results inorder to get the value of a column(GUID). I am unsure of how to go about on getting the Guid column from my results set in the foreach loop
this is what i have:
var results = GetGuids(instId);
foreach (var item in results)
{
}
public IEnumerable GetGuids(int id)
{
using (SqlCommand _command = new SqlCommand("StoredProc"))
{
_command.Connection = new SqlConnection(conString);
_command.Connection.Open();
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.AddWithValue("#ItemID", id);
return _command.ExecuteReader();
}
}
You can't use most of the normal linq extension methods directly on the non-generic IEnumerable... but you can call .Cast<T>() to make it an IEnumerable<T>. At that point, things get easier:
public IEnumerable<Guid> GetGuids(int id)
{
using (SqlCommand _command = new SqlCommand("StoredProc"))
{
_command.Connection = new SqlConnection(conString);
_command.Connection.Open();
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.Add("#ItemID", SqlDbType.Int).Value = id;
return _command.ExecuteReader()
.Cast<DbDataRecord>()
.Select(r => (Guid)r["GuidColumn"]);
}
}
You need to produce the results yourself from the SqlDataReader
var results = GetGuids(instId);
foreach (var item in results)
{
}
public IEnumerable<Guid> GetGuids(int id)
{
using (SqlCommand _command = new SqlCommand("StoredProc"))
{
_command.Connection = new SqlConnection(conString);
_command.Connection.Open();
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.AddWithValue("#ItemID", id);
var guids = new List<Guid>();
using (SqlDataReader reader = _command.ExecuteReader())
{
while (reader.Read()
{
guids.Add( (Guid)reader["GuidColumn"]);
}
}
}
}
Related
I have a method that returns a string from a database based on what i pass to it. The method works but i need to display the result in a textbox on a form. But when i run it the result i keep getting is "System.Collections.Generic.List`1[System.String]". I cant work this out. I try looping through the interest variable with for loop but this doesn't work either.
var interest = GetValue2(tvp, connectionString); -- this is my method that returns a string
textBox2.Text = string.Join(", ", interest.ToString());
this returns "System.Collections.Generic.List`1[System.String]" to textbox2. Any ideas what im missing here? thanks
PART 2:
public static List<String> GetValue2(DataTable tvp, String connectionString)
{
List<String> items = new List<String>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("[dbo].[...]", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#..";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "[dbo].[....]";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((String)rdr["id"]);
}
Console.ReadLine();
}
}
return items;
}
Could you share the GetValue2 method? Probably you are calling ToString() on List in that method and it that case the GetValue2 method returns
"System.Collections.Generic.List1[System.String]"
Example:
var test = new List<string>{"some text"};
test.ToString(); // returns System.Collections.Generic.List`1[System.String]
Update:
Because the method GetValue2 returns List<string> avoid calling .ToString() on interest
var interest = GetValue2(tvp, connectionString); // returns List<string>
textBox2.Text = string.Join(", ", interest);
There's also the issue of the List<string> items inside the GetValue2() not being populated.
Here's the corrected method:
public static List<String> GetValue2(DataTable tvp, String connectionString)
{
List<String> items = new List<String>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("[dbo].[...]", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#..";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "[dbo].[....]";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
items.Add((String)rdr["id"]);
}
}
}
return items;
}
Following is the C# code for querying the DB to get the list of users id:
int id;
con = new SqlConnection(Properties.Settings.Default.ConnectionStr);
con.Open();
id = 180;
SqlCommand command = new SqlCommand("Select userid from UserProfile where grpid=#id", con);
command.Parameters.AddWithValue("#id", id);
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Console.WriteLine(String.Format("{0}", reader["userid"]));
}
}
con.Close();
Output: 5629
Actually, the list of Users having grpid = 180 are 5629, 5684, 5694.
How can I read the results in a list or an array ?
simply:
List<int> results = new List<int>();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
results.Add((int)reader["userid"]));
}
}
// use results
However, you might find a tool like "Dapper" a useful time saver here
var results = con.Query<int>("Select userid from UserProfile where grpid=#id",
new { id }).AsList();
Simply define a List, and use like:
List<string> Users = new List<string>();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Users.Add(reader[0].ToString());
}
}
Try this :
var userIds = new List<int>();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
userIds.Add(reader.GetInt32(0));
}
}
You could use this extension method:
public static class DbExtensions
{
public static List<T> ToList<T>(this IDataReader reader, int columnOrdinal = 0)
{
var list = new List<T>();
while(reader.Read())
list.Add((T)reader[columnOrdinal]);
return list;
}
}
Now you can use it in this way:
List<int> userIdList;
using (var con = new SqlConnection(Properties.Settings.Default.ConnectionStr))
{
using(var command = new SqlCommand("Select userid from UserProfile where grpid=#id", con))
{
command.Parameters.AddWithValue("#id", id);
con.Open();
using (SqlDataReader rd = command.ExecuteReader())
userIdList = rd.ToList<int>();
}
}
I am trying to return data using IEnumerable with given fields, where I am calling the the method I want to reference the data with given field name and return that.
Example, here is the function
public IEnumerable<IDataRecord> GetSomeData(string fields, string table, string where = null, int count = 0)
{
string sql = "SELECT #Fields FROM #Table WHERE #Where";
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("#Fields", SqlDbType.NVarChar, 255).Value = where;
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}
Calling:
IEnumerable<IDataRecord> data = bw.GetSomeData("StaffCode, Perms", "BW_Staff", "StaffCode = 'KAA'");
What must I do to return the data this way or what way ?
string staffCode = data["StaffCode"].ToString();
string perms = data["Perms"].ToString();
Thanks for any help
your data variable is a collection of rows. You need to iterate over the collection to do something interesting with each row.
foreach (var row in data)
{
string staffCode = row["StaffCode"].ToString();
string perms = row["Perms"].ToString();
}
Update:
Based on your comment that you only expect GetSomeData(...) to return a single row, I'd suggest 1 of two things.
Change the signature of GetSomeData to return an IDataRecord. and remove "yield" from the implementation.
public IDataRecord GetSomeData(string fields, string table, string where = null, int count = 0)
{
string sql = "SELECT #Fields FROM #Table WHERE #Where";
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("#Fields", SqlDbType.NVarChar, 255).Value = where;
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
return (IDataRecord)rdr;
}
}
}
}
}
Or
var row = data.FirstOrDefault();
if (row != null)
{
string staffCode = row["StaffCode"].ToString();
string perms = row["Perms"].ToString();
}
Remarks:
Your implementation of GetSomeData is incomplete. You are not even using several of the parameters, most importantly the fields parameter. And conceptually in SQL you can't parameterize which fields get returned or which table gets used (etc.), but rather you need to construct a dynamic query and execute it.
Update 2
Here is an implementation of GetSomeData that constructs a proper query (in C# 6, let me know if you need it in an earlier version).
public IEnumerable<IDataRecord> GetSomeData(IEnumerable<string> fields, string table, string where = null, int count = 0)
{
var predicate = string.IsNullOrWhiteSpace(where) ? "" : " WHERE " + where;
string sql = $"SELECT { string.Join(",", fields) } FROM {table} {predicate}";
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}
And here is how you would use it.
IEnumerable<IDataRecord> data = bw.GetSomeData(new[] { "StaffCode", "Perms" }, "BW_Staff", "StaffCode = 'KAA'");
You can either enumerate it or call .FirstOrDefault, it's your choice. Each time you call GetSomeData, it will run the query.
Update 3
GetSomeData implemented with earlier versions of C#
public IEnumerable<IDataRecord> GetSomeData(IEnumerable<string> fields, string table, string where = null, int count = 0)
{
var predicate = string.IsNullOrEmpty(where) ? "" : " WHERE " + where;
string sql = string.Format("SELECT {0} FROM {1} {2}", string.Join(",", fields), table, predicate);
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}
I have this code:
var query = "SELECT * FROM Cats";
var conn = new SqlConnection(sqlConnectionString);
conn.Open();
var cmd = new SqlCommand(query);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var CatName = reader.GetString(0);
var CatDOB = reader.GetDateTime(1);
var CatStatus = reader.GetInt32(2);
}
I'd like to pull the rows out into an anonymous type collection, which I'd normally do using LINQ to iterate, but I an not sure if it's possible due to the way you have to call .Read() each time to get the next row.
Is there a way to do this?
You can create helper generic method and let compiler infer type parameter:
private IEnumerable<T> Select<T>(DbDataReader reader, Func<DbDataReader, T> selector)
{
while(reader.Read())
{
yield return selector(reader);
}
}
usage:
var items = SelectFromReader(reader, r => new { CatName = r.GetString(0), CarDOB = r.GetDateTime(1), CatStatus = r.GetInt32(2) });
You can even make the method an extension method on DbDataReader:
public static IEnumerable<T> Select<T>(this DbDataReader reader, Func<DbDataReader, T> selector)
{
while (reader.Read())
{
yield return selector(reader);
}
}
and use it like that:
var items = reader.Select(r => new { CatName = r.GetString(0), CarDOB = r.GetDateTime(1), CatStatus = r.GetInt32(2) });
Here is an example of doing it with dynamic (which I think is easier to work with) but some may feel does not adhere to the letter of your question.
Call it like this:
var result = SelectIntoList("SELECT * FROM Cats",sqlconnectionString);
You could (like I did) put it into a static class in a separate file for easier maintanence.
public static IEnumerable<dynamic> SelectIntoList(string SQLselect, string connectionString, CommandType cType = CommandType.Text)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = cType;
cmd.CommandText = SQLselect;
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read()) // read the first one to get the columns collection
{
var cols = reader.GetSchemaTable()
.Rows
.OfType<DataRow>()
.Select(r => r["ColumnName"]);
do
{
dynamic t = new System.Dynamic.ExpandoObject();
foreach (string col in cols)
{
((IDictionary<System.String, System.Object>)t)[col] = reader[col];
}
yield return t;
} while (reader.Read());
}
}
conn.Close();
}
}
}
It's possible, although not particularly neat. We'll need to create a new method that will allow us to create an empty sequence that allows for type inference off of a dummy value for starters:
public static IEnumerable<T> Empty<T>(T dummyValue)
{
return Enumerable.Empty<T>();
}
This lets us create a list of an anonymous type:
var list = Empty(new
{
CatName = "",
CatDOB = DateTime.Today,
CatStatus = 0
}).ToList();
(The item here isn't used.)
Now we can add our anonymous types to this list:
var cmd = new SqlCommand(query);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
list.Add(new
{
CatName = reader.GetString(0),
CatDOB = reader.GetDateTime(1),
CatStatus = reader.GetInt32(2),
});
}
Of course, using a named type would likely be easier, so I would suggest using one unless there is a real compelling reason not to do so. That is especially true if you plan to use the list outside of the scope it's created in.
Technically, it may not answer your question, but simply don't use a reader. Instead use a SqlDataAdapter to Fill a DataSet, if you can. Take the 0th Table of that DataSet, and select a new anonymous object from the Rows collection.
using System.Data; // and project must reference System.Data.DataSetExtensions
var ds = new DataSet();
using (var conn = DbContext.Database.GetDbConnection())
using (var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = sqlText;
conn.Open();
(new SqlDataAdapter(cmd)).Fill(ds);
}
var rows = ds.Tables[0].AsEnumerable(); // AsEnumerable() is the extension
var anons = rows
.Select(r => new { Val = r["Val"] })
.ToList();
I am using generic list to store the data that comes by querying the databse.I uses List of classes actually for multiple rows.
But my problem is my classes have almost more than 20 properties and most of the time i uses only its 2 or 3 properties.
So I want to know that what is the best way to keep the data coming from database.
Below is my code
List<ImageGalleryCollection> tempList = new List<ImageGalleryCollection1>();
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList, connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
tempList.Add(igc);
}
data.Close();
connection.Close();
return tempList;
In ImageGalleryCollection I have more that 20 properties and above i only uses two properties.I think it is very inefficient
Can you how your base class implementation? You can create another class with the most using attributes and use an object of that class inside your class.
IEnumerable<ImageGalleryCollection> GetImageGalleryCollection()
{
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList,connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
yield return igc;
}
data.Close();
connection.Close();
}
I would like to suggest you to write a extension method for SqlDataReader and make use of the method in linq to fetch required columns from the returned rows of reader.
Extension method:
public static class DataReaderExtension
{
public static IEnumerable<Object[]> DataRecord(this System.Data.IDataReader source)
{
if (source == null)
throw new ArgumentNullException("source");
while (source.Read())
{
Object[] row = new Object[source.FieldCount];
source.GetValues(row);
yield return row;
}
}
}
using it in linq:
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("Select * from tblUser"))
{
cmd.CommandType = CommandType.Text;
cmd.Connection = cn;
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
var result = (from row in dr.DataRecord()
select new
{
UserId = row[0],
UserName = row[1]
}).ToList();
}
}
}
This result list has only the required properties you select and helps to reduce the consumption of memory for unwanted properties.