简体   繁体   中英

ASP.NET SqlDataReader and Lists

I am getting data via a SqlDataReader and now looping through the results and putting the results in the list. I am trying to add 2 columns to each list, but I am unsuccessful.

Here is my code:

for (int i = 0; i < reader.FieldCount; i++) 
{
     List<string> costs = new List<string>();

     if (reader.GetName(i).ToString().Contains("TotalCost")) 
     {
         costs.Add(reader.GetValue(i).ToString());
     }

     if (reader.GetName(i).ToString().Contains("SqftCost")) 
     {
         costs.Add(reader.GetValue(i).ToString());
     }   

     jobList.Add(costs);
}

But this puts the two columns in separate lists, I really need the 2 columns in one list.

The reason I am doing it like this is because I have columns that are called TotalCost101, SqftCost101, TotalCost102, SqftCost102, TotalCost104, SqftCost104. So each column that contains TotalCost and SqftCost should be in its own list. I hope this makes sense, anyone got any ideas on how to put these 2 columns in their own list. So at the end I will have a bunch of lists with 2 values.

I updated my code so I now use a Class instead of a List

for (int i = 0; i < reader.FieldCount; i++)
{
    CostMatrix costs = new CostMatrix();

    if (reader.GetName(i).ToString().Contains("TotalCost"))
    {
        costs.TotalCost = reader.GetValue(i).ToString();
    }

    if (reader.GetName(i).ToString().Contains("SqftCost"))
    {
        costs.sqftCost = reader.GetValue(i).ToString();
    }

    jobList.Add(costs);
}

Here is the current output:

<d3p1:CostMatrix>
    <d3p1:TotalCost>550</d3p1:TotalCost>
    <d3p1:sqftCost i:nil="true"/>
</d3p1:CostMatrix>
<d3p1:CostMatrix>
    <d3p1:TotalCost i:nil="true"/>
    <d3p1:sqftCost>0.41</d3p1:sqftCost>
</d3p1:CostMatrix>

What I am looking for is:

<d3p1:CostMatrix>
    <d3p1:TotalCost>550</d3p1:TotalCost>
    <d3p1:sqftCost>0.41</d3p1:sqftCost>
</d3p1:CostMatrix>

Honestly, I would use an object.

public class Price
{
     public decimal Sqft { get; set; }
     public decimal Total { get; set; }
}

You have an object that actually represents something tangible. You're clearly indicating what type of price is applicable. This will avoid confusion with other people working on the project and for you, with an expressive usage. Nothing is being obfuscated into a Tuple or string .

Then when you use the data reader, you could do something along these lines:

public static T GetValueOrNull<T>(this IDataReader reader, string column)
{
     int ordinal;
     if(!string.IsNullOrEmpty(column) && !reader.IsDBNull(reader.GetOrdinal(column)))
          if(int.TryParse(reader.GetOrdinal(column).ToString(), out ordinal))
               return (T)reader.GetValue(ordinal);

      return default(T);
}

You can basically tell this, "which column" then assign it to that property. This could also be handled by some form of object relational mapper.

// Example:
List<Product> products = db.GetProducts();
var example = products.Where(o => o.Price.Sqft >= 5.00);
var sample = products.Where(o => o.Price.Total <= 5.00);

You can store Price inside of another object, allowing a web-site to filter a product based on how multiple types of price values, for instance. It has other benefits as well, it will also document your code nicely, to know how pricing may be implemented.

Not search for a collection of strings , how would that persist throughout your application? A List<string> would be hard to find all implementations for price, unless seeking a data attribute. But these are a bunch of reasons.

Hope this clarifies a bit.

Based on your update, you could do:

public class CostMatrix
{
     public ConcurrentList<decimal> Total { get; set; }
     public ConcurrentList<decimal> Sqft {get; set; }
}

Your object would have two separate list, then as you read through the table column by column and row by row, you could simply add. So if you used the above static method it would be:

using(var connection = new SqlConnection(dbConnection))
using(var command = new SqlCommand(query, dbConnection))
using(var reader = new SqlDataReader())
     while(reader.Read())
     {
          Total.Add(GetValueOrNull<decimal>(reader, "TotalCost");
          Sqft.Add(GetValueOrNull<decimal>(reader, "Sqft1");
     }

I placed ConcurrentList because your implementation may use async . Wasn't sure, if not you can use a normal List . But a List isn't thread safe by default, you'd need a ConcurrentList .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM