I have the following code:
SqlDataReader reader = getAddressQuery.sqlReader;
while (reader.Read())
{
foreach (Object ob in reader)
{
someText.InnerText = someText.InnerText + " " + ob.ToString();
}
}
The code in the foreach loop does not execute. However, I can do this:
SqlDataReader reader = getAddressQuery.sqlReader;
while (reader.Read())
{
someText.InnerText = reader[0].ToString();
}
Which works.
Obviously I could achieve the same result using a regular for loop rather than a foreach loop, but I think the foreach syntax is clearer, so I use it when possible.
What has gone wrong here? Are foreach loops in c# not as flexible as in more high level languages?
Something like the following. Note that IDataReader derives from IDataRecord which exposes the members used to process the current row:
IEnumerable<IDataRecord> GetFromReader(IDataReader reader)
{
while(reader.Read()) yield return reader;
}
foreach(IDataRecord record in GetFromReader(reader))
{
... process it ...
}
Or even something like the following, to get an enumeration or list of strongly-typed entity objects from a reader:
IEnumerable<T> GetFromReader<T>(IDataReader reader, Func<IDataRecord, T> processRecord)
{
while(reader.Read()) yield return processRecord(reader);
}
MyType GetMyTypeFromRecord(IDataRecord record)
{
MyType myType = new MyType();
myType.SomeProperty = record[0];
...
return myType;
}
IList<MyType> myResult = GetFromReader(reader, GetMyTypeFromRecord).ToList();
UPDATE in response to Caleb Bell's comment.
I agree Enumerate is a better name.
In fact in my personal "common" library, I've now replaced the above by an extension method on IDataReader:
public static IEnumerable<IDataRecord> Enumerate(this IDataReader reader)
{
while (reader.Read())
{
yield return reader;
}
}
And the caller can get strongly-typed objects using:
reader.Enumerate.Select(r => GetMyTypeFromRecord(r))
The foreach exposes an IDataRecord, which puts you in a very similar boat to the while loop:
using (SqlConnection conn = new SqlConnection(""))
using (SqlCommand comm = new SqlCommand("select * from somewhere", conn))
{
conn.Open();
using (var r = comm.ExecuteReader())
{
foreach (DbDataRecord s in r)
{
string val = s.GetString(0);
}
}
}
If you want to see something more useful, you'll need to have some of your own code that extracts the values from the record into something more custom, as the other answer has suggested.
Either way you are going to need custom code, whether you have it inline or not or use a while loop or not depends on how often it's going to be written I suppose, any more than once and you should probably stick it in a helper method somewhere.
And to answer the somewhat question: the problem is not the foreach, it is your attempted usage of what it returns for you, as your comparable use of the while loop is not actually comparable.
you may do also that...
string sql = "select * from Users";
using (SqlConnection conn = GetConnection()){
conn.Open();
using (SqlDataReader rdr = new SqlCommand(sql, conn).ExecuteReader()){
foreach (DbDataRecord c in rdr.Cast<DbDataRecord>()){
Console.Write("{0} {1} ({2}) - ", (string)c["Name"], (string)c["Surname"], (string)c["Mail"]);
Console.WriteLine((string)c["LoginID"]);
}
}
}
Related
I want to read all records from "product" table and create objects from each records.
it only gets one records from the database, any ideas might help ?
public IReadOnlyList<Product> Search(string name)
{
var result = new List<Product>();
using (var conn = new SqlConnection(connectionString))
{
if (name == null)
{
var command = new SqlCommand("SELECT * FROM Product ", conn);
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], (string)reader["Name"],
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
(string)reader["Description"]);
result.Add(prod);
reader.NextResult();
}
reader.Close();
conn.Close();
return result;
};
}
}
If you have several result sets, you should loop over them, i.e. you should put one more outer loop, e.g.
using var reader = command.ExecuteReader();
do {
while (reader.Read()) {
var prod = new Product(
Convert.ToInt32(reader["ID"]),
Convert.ToString(reader["Name"]),
Convert.ToDouble(reader["Price"]), // decimal will be better for money
Convert.ToInt32(reader["Stock"]),
Convert.ToInt32(reader["VATID"]),
Convert.ToString(reader["Description"])
);
result.Add(prod);
}
}
while (reader.NextResult());
Note outer do .. while loop since we always have at least one result set.
You use NextResult which advances the reader to the next result set. This makes sense if you have multiple sql queries and you'd use it after the while-loop. Here it's just unnecessary and wrong.
You are already advancing the reader to the next record with Read.
If I get rid of it, this error occur : Unable to cast object of type
'System.DBNull' to type 'System.String.
You can use IsDBNull:
int nameIndex = reader.GetOrdinal("Name");
string name = reader.IsDBNull(nameIndex) ? null : reader.GetString(nameIndex);
int descIndex = reader.GetOrdinal("Description");
string description = reader.IsDBNull(descIndex) ? null : reader.GetString(descIndex);
var prod = new Product((int)reader["ID"],
name,
(double)reader["Price"],
(int)reader["Stock"],
(int)reader["VATID"],
description);
Use it for every nullable column, for the numeric columns you could use nullable types like int?.
You have an error in your code:
Remove the line reader.NextResult();
NextResult is used for moving to next result set not next record.
Definitely remove the NextResult(). That does NOT move between individual records in the same query. Read() does this for you already. Rather, NextResult() allows you to include multiple queries in the same CommandText and run them all in one trip to the database.
Try this:
public IEnumerable<Product> Search(string name)
{
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT * FROM Product ", conn))
{
if (!string.IsNullOrEmpty(name) )
{
command.CommandText += " WHERE Name LIKE #Name + '%'";
command.Parameters.Add("#Name", SqlDbType.NVarChar, 50).Value = name;
}
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], reader["Name"].ToString(),
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
reader["Description"].ToString());
yield return prod;
}
}
}
}
I'm currently executing my stored procedure below, and it works perfectly. But I can't specify the command timeout.
var results = await _dbContext.DbContext.Database.SqlQuery<GetOutputDto>(#"[dbo].[GetOutput] " + parameterString, list.ToArray()).ToListAsync();
Now I've change this to the below, and wondering what's the best way to convert the result to an object. I have over 30 properties, so setting each value would be quite tedious. Was wondering if there's a clean solution as Entity Framework solution.
using (var conn = new SqlConnection(_dbContextProvider.DbContext.Database.Connection.ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"[dbo].[GetOutput]", conn);
cmd.CommandTimeout = 60;
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in list)
{
cmd.Parameters.Add(item);
}
cmd.ExecuteNonQuery();
cmd.Connection.Close();
// How to get the result to entity in a clean manner.
}
Using System.reflection in those situation is really handy.
public static List<T> Convert<T>(IDataReader dr) where T : class, new()
{
List<T> list = new List<T>();
T obj = default(T);
while (dr.Read()) {
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
if (!object.Equals(dr[prop.Name], DBNull.Value)) {
prop.SetValue(obj, dr[prop.Name], null);
}
}
list.Add(obj);
}
return list;
}
using (var conn = new SqlConnection(_dbContextProvider.DbContext.Database.Connection.ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"[dbo].[GetOutput]", conn);
cmd.CommandTimeout = 60;
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in list)
{
cmd.Parameters.Add(item);
}
using ( var reader = cmd.ExecuteReader() ){
List<Entity> result = Convert<Entity>(reader); // convert to entity.
cmd.Connection.Close();
}
}
I would in all honesty send over as an array and convert to table type within SQL and do the dirty work on the server side. Also a good way to be able to specify the timeout can be done by either the connection strings within your config file or you can also pass that same parameter over to sql with a WAITFOR DELAY.
Cheers!
Not that hard, do it like this
note, this is lazy eval so it should perform well when there is user IO, still fairly fast in other cases, I've used it in data ETL projects with many records.
public static IEnumerable<dynamic>( /* params */)
{
// build command object here.
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());
}
}
// remember to close connection
}
From my simple DB framework https://gist.github.com/hoganlong/b7f5c5e8dde61ae3cd6f
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();
Consider the FetchData method below. It is designed to avoid duplicating the database query code every time you want to fetch some data from the database.
List<MyData> myData = new List<MyData();
FetchData((IDataReader reader) =>
{
myData.Add(new MyData(reader.GetString(0), reader.GetInt32(1)));
}, "usp_get_my_data");
It works, but it would be nice if this object creation could somehow live inside the FetchData method.
Is there a better approach?
Perhaps FetchData can be modified to return a list of some type X directly?
protected void FetchData(Action<IDataReader> processor, String query)
{
using (var connection = CreateConnection())
{
connection.ConnectionString = ConnectionString;
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = query;
using (IDataReader reader = command.ExecuteReader())
{
while (reader.read())
{
processor(reader);
}
}
}
}
}
Using generics maybe?
protected IEnumerable<T> FetchData<T>(Func<IDataReader, T> processor, String query)
{
using (var connection = CreateConnection())
{
connection.ConnectionString = ConnectionString;
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = query;
using (IDataReader reader = command.ExecuteReader())
{
while (reader.read())
{
yield return processor(reader);
}
}
}
}
}
Then you can use the enumeration however you want:
var myData = FetchData<MyData>(reader => new MyData(reader.GetString(0), reader.GetInt32(1)), "usp_get_my_data").ToList();
You might take a look at Dapper.Net which is single file ORM with three different helpers:
Execute a query and map the results to a strongly typed List
Execute a query and map it to a list of dynamic objects
Execute a Command that returns no results
Design patterns used to build this miro-ORM are so helpful. Dapper.Net is currently being used in StackOverflow.
i have code like this
public class People
{
public string name { get; set; }
}
public class Animal
{
public string age { get; set; }
}
class Test
{
public void DataPeopleList()
{
string sql = "SELECT * FROM People";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader rdr = cmd.ExecuteReader();
List<People> list = new List<People>();
while (rdr.Read()) {
People p = new People();
p.name = rdr["name"].ToString();
list.Add(p);
}
rdr.Close();
}
public void DataAnimalList()
{
string sql = "SELECT * FROM Animal";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader rdr = cmd.ExecuteReader();
List<People> list = new List<People>();
while (rdr.Read())
{
People p = new People();
p.name = rdr["age"].ToString();
list.Add(p);
}
rdr.Close();
}
}
i think is not good for me. can i write give class as parameter so when i want load data i just give query and class as parameter..example the code which i want like :
public void LoadData(string query, Type ClassName)
{
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader rdr = cmd.ExecuteReader();
List<ClassName> list = new List<ClassName>();
while (rdr.Read())
{
ClassName p = new ClassName();
//p.name = rdr["age"].ToString(); i dont have idea in this part
list.Add(p);
}
rdr.Close();
}
so I`m enough to call method like
public void DataAnimalList()
{
string sql = "SELECT * FROM Animal";
LoadData(sql,class Animal);
}
Can you give me an answer or hint..
Thanks in Advance
There are ways that you can accomplish something like this with reflection, but I strongly recommend you switch to using an ORM framework like ADO.NET Entity Framework, Linq to SQL or NHibernate instead.
You might want to read:
Why should you use an ORM?
What is so great about ORM?
Are there good reasons not to use an ORM?
You could use generics. I think it can be done like this:
public List<T> LoadData<T> (String query) {
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataReader rdr = cmd.ExecuteReader();
List<T> list = new List<T>();
while (rdr.Read())
{
T p = new T(rdr[0]);
list.Add(p);
}
rdr.Close();
return list;
}
Where you have to create a constructor for each class that accepts the data as a parameter. You could event modify the code a bit and use a DataAdapter to fill a DataTable, and then you could create a constructor that accepts DataRow as argument so you can instantiate classes with different number of elements.
But if you get a any more complicated with this, I would suggest using some ORM framework like Linq to SQL, Entity Framework, Nhibernate...
I feel, what you are thinking may not be that elegant solution. Because
1) You can use reflection but which can result in poor performance. So, better not to go for it until, it is badly needed.
2) Also, tomorrow, if the columns to deal with in the result set changes then anyway you need to go and tweak the code otherwise your code may fail.
So, better could be you can go for some ORM Framework as suggested by Ani.
If I got your question right then what you're looking for is Polymorphism. Try this:
void DataList(IType var)
{
if(IType is Animal)
{
//Do something for Animal
}
if(IType is People)
{
//Do something for People
}
}
Both Animal and People class would implement IType interface, which can be an empty interface.
A better design would be to change your code structure and move the DataList method to IType and implement this accordingly in your People and Animal class. More than one way to skin the cat, you see :)
You could have something like this where a delegate is used to populate the object from the data reader:
public List<T> LoadData<T>(string sql, Action<IDataReader, T> fill) where T : new()
{
using( SqlCommand cmd = new SqlCommand(sql, conn) )
using( SqlDataReader reader = cmd.ExecuteReader() )
{
List<T> items = new List<T>();
while( reader.Read() )
{
T item = new T();
fill(reader, item);
items.Add(item);
}
return items;
}
}
Then use it like this:
public List<Person> LoadPeople()
{
return LoadData<Person>("SELECT * FROM Person", (r, p) =>
{
p.Name = r["Name"].ToString();
});
}
But really you should be using an ORM.