I'm fairly new to C#, so please bear with me. I have a class FixData:
private class FixData
{
public int ID { get; set; }
public List<string> content { get; set; }
}
And there's also private List<FixData> IDList = new List<FixData>();
I'm querying data from sql database using IDs already stored in IDList andSqlDataReader and then trying to save it into IDList.content. But that's where the hard part starts. I don't really know how many rows or columns this data has and trying to read that from debugger made me so much more confused (in other words: I fail to read it). Despite this, I tried to save it in so many ways and so many times that I'm completly lost at this point. Here's the code:
foreach (var record in IDList)
{
SqlCommand nonQuerycmd = new SqlCommand(NonQuery, connection);
nonQuerycmd.Parameters.Add(new SqlParameter("ScenarioID", record.ID));
nonQuerycmd.ExecuteNonQuery();
SqlCommand cmd = new SqlCommand(FixQuery, connection);
sqlreader = cmd.ExecuteReader();
ArrayList rowList = new ArrayList();
while (sqlreader.Read())
{
object[] values = new object[sqlreader.FieldCount];
sqlreader.GetValues(values);
rowList.Add(values);
record.content = values.Cast<object>().Select(x => x.ToString()).ToList();
}
sqlreader.Close();
}
Could you please help me and point me to an explanation or link or something that could help me understand how I should solve this?
Edit
I've managed to scramble something, but I'm not sure if this works as it was intended to.
Take a look at the MSDN documentation for SqlDataReader class. It should get you started.
The examples and other classes linked to there should help with proper usage of SqlCommand and other classes as well.
Without knowing what's in your table, the best I can do is:
if (read.Read())
{
for(int i = 0; i < read.FieldCount; i++)
{
record.content.Add(Convert.ToString(read[i]));
}
}
This will add every field selected to record.content as a string. I just changed "while" to "if" because you will only be handling one row (I think). If you need more help, let us know more information about your data and what you need. If, for some reason, you are putting multiple fields from multiple rows, use while. If you only want one field from multiple rows, change if to while, take out the for/next and change [i] to [0]. Shannon is not Carnac the Magnificent.
Related
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.
public partial class Form1 : Form
{
public SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Users\Alexander\Desktop\Archivos.mdf;Integrated Security=True;Connect Timeout=30");
private void Readbtn_Click(object sender, EventArgs e)
{
con.Open();
Propiedades prop = new Propiedades();
List<string> myValues = new List<string>();
string line;
StreamReader file = new StreamReader(#"c:\temp\archivo.txt");
if ((line = file.ReadLine()) != null)
{
string[] fields = line.Split(',');
prop.matricula = fields[0].ToString();
prop.nombre = fields[1].ToString();
prop.sueldo = decimal.Parse(fields[2]);
for (int i = 0; i < fields.Length; i++)
{
listBox1.Items.Add(fields[i]);
}
}
SqlCommand cmd = new SqlCommand("INSERT INTO Archivos(Codigo, Nombre, Sueldo) VALUES (#Matricula, #Nombre, #Sueldo", con);
cmd.Parameters.AddWithValue("#Matricula", prop.matricula);
cmd.Parameters.AddWithValue("#Nombre", prop.nombre);
cmd.Parameters.AddWithValue("#Sueldo", prop.sueldo);
cmd.ExecuteNonQuery();
con.Close();
}
Hello Guys, I just need a little bit of help on modifying this code. I already have this to save the first line of the text file but I dont have any ideas on how to make it read the other lines. Also I would like to validate that if the SQL table contains already that info, it will launch an exception or message box letting the user know that the file already exist. Please help
There are a few things you need to do the the first that jumps out at me is that you want to use a while loop in place of your first if statement
while ((line = file.ReadLine()) != null)
{
string[] fields = line.Split(',');
prop.matricula = fields[0].ToString();
prop.nombre = fields[1].ToString();
prop.sueldo = decimal.Parse(fields[2]);
for (int i = 0; i < fields.Length; i++)
{
listBox1.Items.Add(fields[i]);
}
}
Also depending how you want to display the items in the list box you may want to use instead of that internal for loop:
listBox1.Items.Add(prop.matricula+','+prop.nombre+',prop.sueldo.toString());
I would recomend reading the file line by line, adding the values to a DataTable and then using SQL Bulk Insert to store it to the database.
It would be something like
int batchSize = 10000;
SqlBulkCopy bc = new SqlBulkCopy(con);
DataTable dtArchivos = new DataTable();
dtArchivos.Columns.AddRange(new []
{
new DataColumn("Codigo", typeof(string)),
new DataColumn("Nombre", typeof(string)),
new DataColumn("Sueldo", typeof(decimal)),
});
StreamReader file = new StreamReader(#"c:\temp\archivo.txt");
string line = file.ReadLine();
while (line != null)
{
string[] fields = line.Split(',');
DataRow dr = dtArchivos.NewRow();
dr["Codigo"] = fields[0].ToString();
dr["Nombre"] = fields[1].ToString();
dr["Sueldo"] = decimal.Parse(fields[2]);
dtArchivos.Rows.Add(dr);
if (dtArchivos.Rows.Count == batchSize)
{
bc.WriteToServer(dtArchivos);
dtArchivos.Clear();
}
line = file.ReadLine();
}
bc.WriteToServer(dtArchivos);
I added some logic to do batching, as a large file might cause the application to run out of memory due to the size of the DataTable.
First of all, consider using LINQ for your code - if you are not overly concerned about slightly more overhead, LINQ will make your life a whole lot easier. It will automatically cache the information for you, and can be more efficient for a lot of things, not to mention that the code is a lot easier to understand at times. For this, you would need to, in Visual Studio, create a ado.net model of the database, which shouldn't take too long. The precise way, I think, would be to
File -> Add Item -> Add new Item
or something of the like, then searching for ado.net entity. Then, just fill in the details that it asks. You can then use this entity (if you can't find out how to do it, then please reply so that I can fix it as need be) to add data, using the way that you specified.
Now, the reason why it may not be working code be a code problem. I think using the seperator may be the problem, and the following ammended code may be helpful:
StreamReader file = new StreamReader(#"c:\temp\archivo.txt");
while(file.Read()){
//YOUR CODE FOR CONCATENATING DATA HERE
}
The reason is that the reader would read until it has a return value of False or Null. Your code for some reason seems to be leaving after the first line, and this may solve it.
I'm really stuck, and being as many of you guys that post solutions on here are by and large brilliant (IMHO), I figured I'd see what the best of you could make of this problem.
Some background
I'm trying to create a list that must contain only distinct items in a specific sequence.
(it's a primary key and thus must be distinct (I didn't make it a primary key, but I have to work with what I'm given, you know how it goes).
For ease of understanding this requirement, think of creating a distinct list of recipe steps from a book of recipes. My problem is that the "cooks" of these "recipes" often change the order in which they create their masterpieces.
For instance:
Recipe 1
Whisk eggs using fork
Melt margarine in a skillet
Pour in the eggs
stir constantly
Plate
Add salt and pepper as desired
Recipe 2
Break eggs into bowl
Whisk eggs using fork
Melt margarine in a skillet over low heat
Pour in the eggs
stir constantly
Plate
Serve
Add salt and pepper as desired
Recipe 3
Whisk eggs using fork
Add salt and pepper as desired
Melt margarine in a skillet over low heat
Pour in the eggs
stir constantly
Plate
As you can tell "Add salt and pepper..." can't be number 2 in Recipe 3 and still be in the correct sequence in Recipes 1 and 2.
I think if I can ID the "offending" list item and add a period to the end of it, thus making it unique, this would work as a solution.
How do I do this in C# given a dataset (gotten by a SQL query) with duplicates in the correct sequence and placed into a List of type string? LINQ is not a requirement here, but I'm not afraid to use it if it provides a solution.
Specifically code (or psedo-code) that:
IDs the list item that needs to be duplicated and modified.
Determine WHERE in the newly created large list (assuming) is the newly modified list item to be placed.
If Your 1st question is going to be "show me your work", please be advised that I've done quite a bit of work on this, and the code is generally long.
I am happy to work from either pseudo-code or try your code with my dataset.
I'm also happy reading other solutions that may be pertinent.
Thanks, I look forward to seeing your solutions.
--edit: I'm starting to get the impression people don't like it if you don't post code.
So here it goes (I said above it was long). The code works but it doesn't solve the problem. It returns a distinct list in order with no duplicates.
(If the formatting below is bad please forgive)
public void GetNewRecipeItemsFromDB(string RequestedRecipeName)
{
string connString = string.Empty;
string strGetRecipeElements_Sql = "SQL that returns the dataset";
string connString = GetConnectionString();
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = strGetRecipeElements_Sql;
SqlDataReader reader = null;
try
{
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(strGetRecipeElements_Sql, conn);
DataSet RecipeItems = new DataSet();
adapter.Fill(RecipeItems, "RecipeItems");
reader = cmd.ExecuteReader();
List<string> RecipeItemList = new List<string>();
//Create an array with existing RecipeItems
int readerCount = 0;
while (reader.Read())
{
RecipeItems GSI = new RecipeItems();
GSI.RecipeItem = reader[0].ToString();
GSI.Sequence = Convert.ToInt32(reader[1].ToString());
GSI.Rank = Convert.ToInt32(reader[2].ToString());
RecipeItemList.Add(GSI.RecipeItem.ToString());
readerCount++;
}
string[] CurrentRecipeItemArray = new string[readerCount];
string[] UpdatedRecipeItemArray = new string[readerCount];
//RecipeItemList.Sort();
label1.Text = "";
textBox1.Text = "";
CurrentRecipeItemArray = RecipeItemList.ToArray();
for (int y = CurrentRecipeItemArray.Length - 1; y >= 0; y--)
{
textBoxDBOrginal.Text += CurrentRecipeItemArray[y].ToString() + Environment.NewLine;
}
string[] lines = textBoxDBOrginal.Text.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
List<string> UniqueRecipeItemsInvertedList = new List<string>();
if (lines.Length > 0)
{
//OK it's not null lets look at it.
int lineCount = lines.Length;
string NewCompare = string.Empty;
for (int z = 0; z < lineCount; z++)
{
NewCompare = lines[z];
if (!UniqueRecipeItemsInvertedList.Contains(NewCompare))
{
UniqueRecipeItemsInvertedList.Add(NewCompare);
}
}
}
UniqueRecipeItemsInvertedList.Reverse();
foreach (string s in UniqueRecipeItemsInvertedList)
{
if (!string.IsNullOrEmpty(s))
{
listBox7.Items.Add(s.ToString());
}
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.Errors.ToString());
}
conn.Close();
}
}
The answer was already on this site.
How to rename duplicates in list using LINQ
Code is:
IEnumerable<String> GetUnique(IEnumerable<String> list)
{
HashSet<String> itms = new HashSet<String>();
foreach(string itm in list)
{
string itr = itm;
while(itms.Contains(itr))
{
itr = itr + "_";
}
itms.Add(itr);
yield return itr;
}
}
I've come to the conclusion that although this can be done, and I got close, I just don't have the skills/knowledge to pull it off.
It amounts to:
Cycle through total number of recipes and place recipe name in a list
For each recipe name in list of recipes Get Recipe Steps And Sequence From DB and place in a sorted list (and this is an iffy bit).
At this point you have all the data you need if you just wanted the distinct items. ListName.Distinct()
Cycling through the SortedList to see if the key/value exists in the proper sequence continues to be my death knell. I kept running into key already exists / Key doesn't exists exceptions. If I can crack this nut, I'll have solved the problem.
I learned quite a bit about list<>, sortedList<> and List and the power of having your own class(es) and methods. For example: RecipeInfo.RecipeItemsList makes life so much easier.
I still haven't figured out why no one here wanted to touch this or why it was down graded. This experience will likely result in me hesitating before posting another question to stackoverflow.com.
Since Dictionary won't allow for duplicate entries (it throws an ArgumentException was unhandled exception), thus handling the "heavy lifting" of ensuring uniqueness and sequence order (still testing that one). I think I'm using GSI.Sequence wrong because there could be multiple sequences for receipe items. (This is NOT the answer, but a place I could put code. I hope I did it right) Hat tip to http://williablog.net/williablog/post/2011/08/30/Generic-AddOrUpdate-Extension-for-IDictionary.aspx
while(reader.Read())
{
RecipeItems GSI = new RecipeItems();
GSI.RecipeItem = reader[0].ToString();
GSI.Sequence = Convert.ToInt32(reader[1].ToString());
GSI.RecipeName = reader[2].ToString();
GSI.MaxSequence = Convert.ToInt32(reader[3].ToString());
if (dictionary.ContainsKey(GSI.RecipeItem))
{
dictionary.[GSI.RecipeItem] = GSI.Sequence);
}
else
{
dictionary.Add(GSI.RecipeItem, GSI.Sequence);
}
}
I think the final answer here ended up being something that I didn't necessarily foresee or desire. With approximately 94 unique items, you will end up with a list of about 428 unique recipe items over the course of 20 recipes. This would give me a list where I could have the appropriate recipe item in the right sequence. I still think my logic on this one is BAD, but it makes sense when you figure you have a couple of recipe items per recipe out of order and must be duplicated and than you multiply that times the number of recipes.
I've written a SQL select statement that returns multiple fields from 1 record in the table.
Here is my statement:
-- (item_num being the PK)
SELECT item_num,
category,
weight,
cost,
description
FROM inv
WHERE item_num = #inumber;
How do I save each field into a variable?
I've seen samples written in while loops but my statement returns ints and chars so I would like to save them to the respectable variables and not an array.
Please bear with me as I'm new to working with database with coding. I just need to better understand the format.
I've searched for the answer but couldnt have anything related. Maybe my approach is all wrong.
Your help is appreciated.
Ultimately, you should be using a Datareader and from this it is possible to assign the values directly to the variables;
using (var rdr = db.ExecuteReader(cmd))
{
myIntValue = (int) rdr["IntValue"];
myStringValue = rdr["StringValue"].ToString();
}
However, in your case, my suggestion would be to use a DTO and populate these accordingly from your Data Layer
public class MyDTO
{
public int MyInt { get; set; }
public string MyString { get; set; }
}
and return IEnumerable<MyDTO>
First of all, your returns will not be ints and chars. It'll be strings. At least, that's my experience. And you should be storing them all in a MySQL datareader. This will allow you to do something like:
List<string> weight = new List<string>();
while(dataReader.Read())
{
weight.Add(dataReader["weight"]);
}
What this does is it will create a list of strings from all the results of your query that were under the "weight" column. From there, if you need them to be chars and ints, you can convert them. But I suggest you read up on MySQL datareaders. My example above isn't perfect, but it's a start.
I find that my C# apps do a lot of queries with a lot of boilerplate that clutters up my code space. I also want to avoid repetition, but I'm not sure how I could write a method to do this generically.
I am accessing an Oracle database using ODP. I can't use Linq because our data warehouse people refuse to designate primary keys, and ODP support for Linq appears to be, well ... they'd rather have you use their platform.
I can't really return a List because every query returns different numbers of different types.
string gufcode = String.Empty;
double cost = 0.0;
OracleCommand GUFCommand2 = thisConnection.CreateCommand();
String GUFQuery2 = "SELECT GUF_ID, COST_RATE FROM SIMPLE_TABLE";
GUFCommand2.CommandText = GUFQuery2;
OracleDataReader GUFReader2 = GUFCommand2.ExecuteReader();
while (GUFReader2.Read())
{
if (GUFReader2[0/**GUF_CODE**/] != DBNull.Value)
{
gufcode = Convert.ToString(BUFReader2[0]);
}
if (GUFReader2[1/**COST_RATE**/] != DBNull.Value)
{
cost = Convert.ToDouble(GUFReader2[1]);
}
effortRatioDictionary.Add(bufcode, percentageOfEffort);
}
GUFReader2.Close();
But there's really a lot more terms and a lot more queries like this. I'd say 15 or so queries -some with as many as 15 or so fields returned.
Copy/pasting this boilerplate everywhere leads to a lot of fires: for example if I don't update everything in the copy paste I'll close the wrong reader (or worse) send a different query string to the database.
I'd like to be able to do something like this:
string gufQuery = "SELECT GUF_ID, COST_RATE FROM SIMPLE_TABLE";
List<something> gufResponse = miracleProcedure(gufQuery, thisConnection);
And so most of the boilerplate goes away.
I'm looking for something simple.
I think the main reason why you are not being able to abstract away a function is because the return data is going to be different everytime.
Which means the number of read is going to be different everytime as well.
You could just return GUFReader2 but then you will lose the ability to close it inside the function which you want.
I would say return an array (or list) of objects.
Inside the procedure, just read through every row and return the list while closing the connection.
Your calling function will always know what and in which sequence the expected data will be. It will have to cast the object data too, but you are doing that inside this procedure anyways.
Some hints:
Deriving from IDisposable allows for cleaner code with using statement.
IMO your magic method should look more like this :
List<T> list = doMagic("SIMPLE_TABLE", columns);
columns could be an array of small structs like this one :
struct Column
{
string Name;
Type DataType;
}
You might be able to use enums if you use the same tables/columns very often.
Or
You can take some inspiration from VertexDeclaration, VertexElement, VertexElementFormat and VertexElementUsage types that are in XNA : http://msdn.microsoft.com/en-us/library/bb197344.
This proven to be very helpful when dealing with a different number of 'inputs' in a random order.
In my case I've been able to build an easy to use, XNA-like framework for OpenGL with such stuff.
Regarding the return type of your list, refer to my 2nd suggestion.
Linq was the right answer. I give credit to David M, above, but I can't mark it as the correct answer since he only left a comment.
I was able to do a semi-generalized method using ArrayLists:
public static ArrayList GeneralQuery(string thisQuery, OracleConnection myConnection)
{
ArrayList outerAL = new ArrayList();
OracleCommand GeneralCommand = myConnection.CreateCommand();
GeneralCommand.CommandText = thisQuery;
OracleDataReader GeneralReader = GeneralCommand.ExecuteReader();
while (GeneralReader.Read())
{
for (int i = 0; i < GeneralReader.FieldCount; i++)
{
ArrayList innerAL = new ArrayList();
if (GeneralReader[i] != DBNull.Value)
{
innerAL.Add(GeneralReader[i]);
}
else
{
innerAL.Add(0);
}
outerAL.Add(innerAL);
}
}
GeneralReader.Close();
return outerAL;
}
And the code that calls this method looks like this:
thisConnection.Open();
List<ProjectWrapper> liProjectCOs = new List<ProjectWrapper>();
String ProjectQuery = "SELECT SF_CLIENT_PROJECT.ID, SF_CLIENT_PROJECT.NAMEX, SF_CHANGE_ORDER.ID, SF_CHANGE_ORDER.END_DATE, ";
ProjectQuery += "SF_CLIENT_PROJECT.CONTRACTED_START_DATE, SF_CHANGE_ORDER.STATUS, SF_CHANGE_ORDER.TYPE, SF_CLIENT_PROJECT.ESTIMATED_END_DATE, SF_CLIENT_PROJECT.CONTRACTED_END_DATE ";
ProjectQuery += "FROM SF_CLIENT_PROJECT, SF_CHANGE_ORDER ";
ProjectQuery += "WHERE SF_CHANGE_ORDER.TYPE = 'New' ";
ProjectQuery += "AND SF_CLIENT_PROJECT.ID = SF_CHANGE_ORDER.PROJECT";
ArrayList alProjects = GeneralQuery(ProjectQuery, thisConnection);
foreach( ArrayList proj in alProjects ) {
ProjectWrapper pw = new ProjectWrapper();
pw.projectId = Convert.ToString( proj[0] );
pw.projectId = Convert.ToString(proj[0]);
pw.projectName = Convert.ToString(proj[1]);
pw.changeOrderId = Convert.ToString(proj[2]);
pw.newEndDate = Convert.ToDateTime(proj[3]);
pw.startDate = Convert.ToDateTime(proj[4]);
pw.status = Convert.ToString(proj[5]);
pw.type = Convert.ToString(proj[6]);
if ( Convert.ToString(proj[7]) != "0" ) // 0 returned by generalquery if null
pw.oldEndDate = Convert.ToDateTime(proj[7]);
else
pw.oldEndDate = Convert.ToDateTime(proj[8]);
liProjectCOs.Add(pw);
There's a lot of obvious disadvantages here (although it is a lot better than what I was trying to do earlier). It is so much worse than Linq I renegotiated with our data warehouse people. There's a new guy there, and he was a lot more helpful.
Linq reduces the lines of code from above by a factor of 2. It is a factor of 4 from the non-encapsulated way I was doing it before that.