In a program that I've been working on, there are three steps to get the data into a Dictionary that's been created:
execute the SQL command
pull those results into a DataTable, then
pull the DataTable into the Dictionary
Code:
var myDr = myLookup.ExecuteReader();
dt.Load(myDr);
customerLookup = dt.AsEnumerable()
.ToDictionary(key => key.Field<string>("code"),
value => value.Field<string>("customerText"));
My question is, is it possible to “cut out the middleman,” so to speak, and pull the data from the SqlDataReater directly into the Dictionaries? Or is it necessary to pull it into a DataTable first? If what I'm looking to do is possible, can someone please post code for me to try?
Thanks very much!
You can just loop through the rows returned by the reader:
var customerLookup = new Dictionary<string, string>();
using (var reader = myLookup.ExecuteReader())
{
while (reader.Read())
{
customerLookup[(string)reader["code"]] = (string)reader["customerText"];
}
}
You should be aware that if there are any duplicate codes, subsequent code values will overwrite previous ones in the dictionary. You can use customerLookup.Add() instead if you'd rather an exception be thrown in such a case.
Not only can you, but you definitely should. The code, as you show it, shows a complete lack of knowledge of how .NET works.
Some of the code I am suggesting may be considered "overkill" for the question at hand, but it does demonstrate some best practices.
Dictionary<string, string> customerLookup = new Dictionary<string, string>();
using (var reader = myLookup.ExecuteReader())
{
int ordinalCode = reader.GetOrdinal("code");
int ordinalCustomerText = reader.GetOrdinal("customerText");
while (reader.Read())
{
//this code assumes the values returned by the reader cannot be null
customerLookup.Add(reader.GetString(ordinalCode), reader.GetString(ordinalCustomerText))
}
}
Yes, it's possible. You should use SqlDataReader.Read method.
Related
I have some problem with my code. I want to replace the ForEach loop with the help of LINQ here, is there any way or solution to solve my problem? My code is given bellow.
static public string table2Json(DataSet ds, int table_no)
{
try
{
object[][] tb = new object[ds.Tables[table_no].Rows.Count][];
int r = 0;
foreach (DataRow dr in ds.Tables[table_no].Rows)
{
tb[r] = new object[ds.Tables[table_no].Columns.Count];
int col = 0;
foreach (DataColumn column in ds.Tables[table_no].Columns)
{
tb[r][col] = dr[col];
if ((tb[r][col]).Equals(System.DBNull.Value))
{
tb[r][col] = "";
}
col++;
}
r++;
}
string table = JsonConvert.SerializeObject(tb, Formatting.Indented);
return table;
}
catch (Exception ex)
{
tools.log(ex.Message);
throw ex;
}
}
This question really asks 3 different things:
how to serialize a DataTable
how to change the DataTable serialization format and finally
how to replace nulls with empty strings, even though an empty string isn't a NULL.
JSON.NET already handles DataSet and DataTable instance serialization with a DataTableConverter whose source can be found here. You could just write :
var str = JsonConvert.SerializeObject(data);
Given this DataTable :
var dataTable=new DataTable();
dataTable.Columns.Add("Name",typeof(string));
dataTable.Columns.Add("SurName",typeof(string));
dataTable.Rows.Add("Moo",null);
dataTable.Rows.Add("AAA","BBB");
You get :
[{"Name":"Moo","SurName":null},{"Name":"AAA","SurName":"BBB"}]
DataTables aren't 2D arrays and the column names and types matter. Generating a separate row object with named fields is far better than generating an object[] array. It also allows makes it far easier for clients to handle the JSON string without knowing its schema in advance. With an object[] for each row, the clients will have to know what's stored in each location in advance.
If you want to use a different serialization format, you could customize the DataTableConverter. Another option though, is to use DataRow.ItemArray to get the values as an object[] and LINQ to get the rows, eg :
object[][] values=dataTable.Rows.Cast<DataRow>()
.Select(row=>row.ItemArray)
.ToArray();
Serializing this produces :
[["Moo",null],["AAA","BBB"]]
And there's no way to tell which item is the name and which is the surname any more.
Replacing DBNulls with strings in this last form needs an extra Select() to replace DBNull.Value with "" :
object[][] values=dataTable.Rows.Cast<DataRow>()
.Select(row=>row.ItemArray
.Select(x=>x==DBNull.Value?"":x)
.ToArray())
.ToArray();
Serializing this produces :
[["Moo",""],["AAA","BBB"]]
That's what was asked, but now we have no way to tell whether the Surname is an empty string, or just doesn't exist.
This may sound strange, but Arabic names may be one long name without surname. Makes things interesting for airlines or travel agents that try to issue tickets (ask me how I know).
We can get rid of ToArray() if we use var :
var values=dataTable.Rows.Cast<DataRow>()
.Select(row=>row.ItemArray
.Select(x=>x==DBNull.Value?"":x));
JSON serialization will work the same.
LINQ is not a nice fit for this sort of thing because you are using explicit indexes r and col into multiple "array structures" (and there is no easy/tidy way to achieve multiple, parallel enumeration).
Other issues
tb is repeatedly newed, filled with data and then replaced in the next iteration, so you end up capturing only the last row of input to the JSON string - that's a logical bug and won't work as I think you intend.
The inner foreach loop declares but does not use the iteration variable column - that's not going to break anything but it is redundant.
You will get more mileage out of using JSON.Net properly (or coding the foreach loops as for loops instead if you want to navigate the structures yourself).
I'm using Selenium Webdriver and am working with IE11. I'd like to access the window performance resources from an HTML page. From chrome I can do that easily with
IJavaScriptExecutor js = _webDriver as IJavaScriptExecutor;
ReadOnlyCollection<object> rList = ReadOnlyCollection<object>)js.ExecuteScript("return window.performance.getEntriesByType(\"resource\")");
and then a simple String object dictionary cast lets me get the details.
BUT the same code doesn't work for IE. There I am forced to cast the contents of what js is returning to a ReadOnlyCollection<IWebElement> - which is obviously not containing any of the info I want it to. Does anyone know what I ought to do to get the real info back?
Edit: I'm gonna leave my other answer because of how dumb I am.
Here's what I'm using now.
var resourceJson = ieDriver.ExecuteScript("var resourceTimings = window.performance.getEntriesByType(\"resource\");return JSON.stringify(resourceTimings)");
var resourceTimings = JsonConvert.DeserializeObject<System.Collections.ObjectModel.ReadOnlyCollection<object>>(resourceJson.ToString());
I'm stuck in the same boat, this is the best I could do:
var resNames = ieDriver.ExecuteScript("var keys = [];for(var key in window.performance.getEntriesByType(\"resource\")){keys.push(key);} return keys;");
Dictionary<string, Dictionary<string, object>> resTimings = new Dictionary<string, Dictionary<string, object>>();
foreach (string name in (System.Collections.ObjectModel.ReadOnlyCollection<object>)resNames)
{
var resource = new Dictionary<string, object>();
var resProperties = ieDriver.ExecuteScript(string.Format("var keys = [];for(var key in window.performance.getEntriesByType(\"resource\")[{0}]){{keys.push(key);}} return keys;", name));
foreach (string property in (System.Collections.ObjectModel.ReadOnlyCollection<object>)resProperties)
{
resource.Add(property, ieDriver.ExecuteScript(string.Format("return window.performance.getEntriesByType(\"resource\")[{0}].{1};", name, property)));
}
resTimings.Add(name, resource);
}
This works, but seems to take way too long. I'm sure there are a lot of optimizations to make. Don't know much js, but it seems it might go faster to offload the work there.
Well I found an answer of sorts
Basically I can query with strings like this to find the array description and access individual items.
"return window.performance.getEntriesByType(\"resource\").length"
"return window.performance.getEntriesByType(\"resource\")[0].name"
Not really the solution I expected, but it works
just messing around, trying to expand my bag o' tricks: I was just experimenting and want to do something like a Dictionary object with another inner Dictionary as the outside Dictionary's .Value
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>();
ObjectType is an enum
so...what then...either you're not suppose to do this or I just don't know how 'cause I started running into a wall when I was trying to figure out how to populate and retrieve data from it.
Purpose might help: I'm being passed an ObjectGUID and need to flip through a bunch of database tables to determine which table the object exists in. The method I've already written just queries each table and returns count (here are a couple examples)
// Domain Check
sql = string.Format(#"select count(domainguid) from domains where domainguid = ?ObjectGUID");
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ObjectType.Domain;
// Group Check
sql = string.Format(#"select count(domaingroupguid) from domaingroups where domaingroupguid = ?ObjectGUID");
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ObjectType.Group;
So, that's all done and works fine...but because the fieldname and table name are the only things that change for each check I started thinking about where I could re-use the repetitive code, I created a dictionary and a foreach loop that flips through and changes the sql line (shown below)...but, as you can see below, I need that ObjectType as kind of the key for each table/fieldname pair so I can return it without any further calculations
Dictionary<string, string> objects = new Dictionary<string,string>();
objects.Add("domains", "domainguid");
objects.Add("domaingroups", "domaingroupguid");
objects.Add("users", "userguid");
objects.Add("channels", "channelguid");
objects.Add("categorylists", "categorylistguid");
objects.Add("inboundschemas", "inboundschemaguid");
objects.Add("catalogs", "catalogguid");
foreach (var item in objects)
{
sql = string.Format(#"select count({0}) from {1} where {0} = ?ObjectGUID", item.Value, item.Key);
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ?????
}
This isn't all that important since my original method works just fine but I thought you StackOverflow geeks might turn me on to some new clever ideas to research...I'm guessing someone is going to smack me in the head and tell me to use arrays... :)
EDIT # Jon Skeet ------------------------------------------
Heh, sweet, think I might have come upon the right way to do it...haven't run it yet but here's an example I wrote for you
var objectTypes = new Dictionary<string, string>();
objectTypes.Add("domainguid", "domains");
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>();
dictionary.Add(ObjectType.Domain, objectTypes);
foreach(var objectType in dictionary)
{
foreach(var item in objectType.Value)
{
sql = string.Format(#"select count({0}) from {1} where {0} = ?ObjectGUID", item.Key, item.Value);
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return objectType.Key;
}
}
This chunk should hit the domains table looking for domainguid and if count > 0 return ObjectType.Domain...look right? Only problem is, while it might seem somewhat clever, it's like 2 dictionary objects, a couple strings, some nested loops, harder to read and debug than my first version, and about 10 more lines per check hehe...fun to experiment though and if this looks like to you then I guess it's one more thing I can add to my brain :)
also found this how to fetch data from nested Dictionary in c#
You can definitely do it, although you're currently missing a closing angle bracket and parentheses. It should be:
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>().
To add a given value you probably want something like:
private void AddEntry(ObjectType type, string key, string value)
{
Dictionary<string, string> tmp;
// Assume "dictionary" is the field
if (!dictionary.TryGetValue(type, out tmp))
{
tmp = new Dictionary<string, string>();
dictionary[type] = tmp;
}
tmp.Add(key, value);
}
If that doesn't help, please show the code that you've tried and failed with - the database code in your question isn't really relevant as far as I can tell, as it doesn't try to use a nested dictionary.
I am getting a datatable with customer data from a MySql databaes and a customer object from a web service.
I want to compare every value in the datatable with the values in the object and if there is one field that differs I want to perfrom some tasks.
I know I can get the values from the datatable with:
string mCompanyName = row["Company Name"].ToString();
string mCreatedDate = row["Created Date"].Tostring();
//etc..
Then I get the values from the web service
string wsCompanyName = customer.companyName;
string wsCreatedDate = customer.createdDate;
There are about 50 fields and doing
if( mCompanyName != wsCompanyName & mCreatedDate != wsCreatedDate and so on..) (or similar)
{
//Do something
}
seems to be a bit tedious and not very nice so how should I perform this? Is there a much better way to chuck it into a list and use some fancy LINQ?
Thanks in advance.
For cases like this I sometimes put them ("the objects") in something IEnumerable (make sure to "line them up") and use the SequenceEqual extension method. It performs standard Equals()'ity and is "cheap enough for my usage".
For instance:
var equal = (new object[] { row["A"], row["B"] })
.SequenceEqual(new object[] { x.A, x.B });
This requires LINQ, of course.
I'd put them in a Dictionary and search that way:
Dictionary<string, string> mData = new Dictionary<string, string>();
mData.Add("Company Name", row["Company Name"].ToString());
Dictionary<string, string> wsData = new Dictionary<string, string>();
wsData.Add("Company Name", customer.CompanyName);
Then loop through:
foreach (KeyValuePair<string, string> pair in mData)
{
if (wsData[pair.Key] == pair.Value)
{
// Do something
}
}
This way, for every entry in mData (the data from your database), it will look for an entry in wsData with the same name.
I wouldn't create individual variables for each piece of data. It would be difficult to maintain, and would not scale well (lots of copy and pastes).
I think this might help you, but it needs to modify this to use in your scenerio stackoverflow link
Is there an easy way to convert all the columns of the current row of a SqlDataReader to a dictionary?
using (SqlDataReader opReader = command.ExecuteReader())
{
// Convert the current row to a dictionary
}
Thanks
You can use LINQ:
return Enumerable.Range(0, reader.FieldCount)
.ToDictionary(reader.GetName, reader.GetValue);
Easier than this?:
// Need to read the row in, usually in a while ( opReader.Read ) {} loop...
opReader.Read();
// Convert current row into a dictionary
Dictionary<string, object> dict = new Dictionary<string, object>();
for( int lp = 0 ; lp < opReader.FieldCount ; lp++ ) {
dict.Add(opReader.GetName(lp), opReader.GetValue(lp));
}
I'm still not sure why you would need this particular transformation from one type of collection to another.
I came across this question on 3/9/2016 and ended up using the answer provided by SLaks. However, I needed to slightly modify it to:
dataRowDictionary = Enumerable.Range(0, reader.FieldCount).ToDictionary(i => reader.GetName(i), i=> reader.GetValue(i).ToString());
I found guidance from this StackOverflow question: convert dataReader to Dictionary
It's already an IDataRecord.
That should give you just about the same access (by key) as a dictionary. Since rows don't typically have more than a few handfuls of columns, the performance of the lookups shouldn't be that different. The only important difference is the type of the "payload", and even there your dictionary would have to use object for the value type, so I give the edge to IDataRecord.
GetValues method accepts & puts in, all the values in a 1D array.
Does that help?