I am getting an error when trying to create a list of objects. I am calling a web service which returns a different number of columns for certain products.
I have a class to store these values which hold all columns available, but when receiving data for products that are missing 1 column it fails with the error:
Column 'x' does not belong to table y.
Since I know column does not exist I need a replacement value if it does not exist. I have about 50 different columns so putting each one of them in an (if column exist) is something I would like to avoid!
Sample code, dprow is the Object template which holds all available columns
dprow.currency = row.Field<string>("currency");
dprow.categoryCode = row.Field<string>("categoryCode");
This is under the assumption that your row is a DataRow, but the concept applies regardless. You can write an extension method that will check if the column exists first:
public static class DataRowExtensions
{
public static T TryGetField<T>(this DataRow row, string fieldName)
{
return row.Table.Columns.Contains(fieldName) ? row.Field<T>(fieldName) : default(T);
}
}
Usage example: dprow.currency = row.TryGetField<string>("currency");
Take the table you get back from the web service (in my case I'm just saying create a brand new one) and then create a dictionary with all the columns that you want to have present, and what the default value should be. If that column isn't already in the table, then add it to the table with the default value.
DataTable dt = new DataTable();
Dictionary<string, object> columnDefaultValues = new Dictionary<string, object>(){
{"currency", "some default"},
{"categoryCode", "some other default"},
{"another", 43}
};
foreach (var keyPair in columnDefaultValues) {
if (!dt.Columns.Contains(keyPair.Key)) {
dt.Columns.Add(new DataColumn(keyPair.Key) { DefaultValue = keyPair.Value });
}
}
Related
I use a created query to return a row of values from a key, I have checked on the query executor but when running the program it is returning null. Please help me I'm starting to lose my mind.
I've tried debugging but the problem is coming from the fill data query and it is returning null.
// Add ref. to the Dataset
ViewMedia viewMediaSetInstance = new ViewMedia();
// Create an empty Table
ViewMedia.ViewMediaDataTable viewMediaTable = new ViewMedia.ViewMediaDataTable();
// Create the Adapter we are going to use to populate the Table
Model.ViewMediaTableAdapters.ViewMediaTableAdapter MediaTableAdapter = new Model.ViewMediaTableAdapters.ViewMediaTableAdapter();
//Use query
MediaTableAdapter.findByPublishYear(viewMediaTable, publishYear);
//It doesn't find the row
//I don't seem to find a solution even when the query returns the values in the executer.
//viewMediaTable.Rows.Count == null so doesn't get inside
if (viewMediaTable.Rows.Count > 0)
{
DataRow selectedUser = viewMediaTable.Rows[0];
media = new Media(Int32.Parse(selectedUser["MediaID"].ToString()), selectedUser["Title"].ToString(), Int32.Parse(selectedUser["PublishYear"].ToString()));
return media;
}
else
{
return null;
}
I'm expecting to return the row of data to display in a book display.
This is wonky; you've got strongly typed data rows with proper typed properties for everything, yet you're treating them as generic datarows with string column names, holding objects that you're tostring()ing and parsing. It was never intended to be used this way.
Your code should look more like this:
ViewMedia viewMediaSetInstance = new ViewMedia();
ViewMedia.ViewMediaDataTable viewMediaTable = new ViewMedia.ViewMediaDataTable();
Model.ViewMediaTableAdapters.ViewMediaTableAdapter MediaTableAdapter = new Model.ViewMediaTableAdapters.ViewMediaTableAdapter();
MediaTableAdapter.findByPublishYear(viewMediaTable, publishYear);
//don't need to access the .Rows property to get the count
if (viewMediaTable.Count > 0)
{
//don't need to access the .Rows property to get the data
ViewMediaRow selectedUser = viewMediaTable[0];
media = new Media(selectedUser.MediaID, selectedUser.Title, selectedUser.PublishYear);
return media;
}
else
{
return null;
}
I also disagree with your assertion in the code comments that datatable.Rows.Count is null. A datatable's .Count property is an integer regardless of whether a strongly or weakly typed datatable is in use; it can never be null
You don't need to create a dataset, if you enable creating a method that returns a datatable. Typically these methods are called GetDataByXXX and the fill methods are called FillByXXX (you've called your fill method findByPublishYear). It's configured on the following screen of the TA wizard:
image courtesy of nullskull.com
This can simplify your code to just the following (add a reference to Model.ViewMediaTableAdapters):
var mDT = new ViewMediaTableAdapter().GetDataByPublishYear(publishYear);
Media m = null;
if (mDT.Count > 0)
media = new Media(mDT[0].MediaID, mDT[0].Title, mDT[0].PublishYear);
return m;
Most critically here, DON'T access the first row via the .Rows collection (viewMediaDataTable.Rows[0]) because that returns a base type DataRow. Access it directly via the default property indexer on the strongly typed datatable (viewMediaDataTable[0]) as this will return an object of type ViewMediaRow which in turn has nicely named, typed properties for all the columns in the datatable
viewMediaDataTable[0].PublishYear; //ViewMediaRow is a strongly typed class that has this property as an int
viewMediaDataTable.Rows[0].PublishYear; //"DataRow does not contain a definition for 'PublishYear'"
Now, if any of the data items in the row is null and the setting of the column upon null is "throw exception" you will see an exception of type StrongTypingException- it happens when you have some column that is of a type that cannot be null (such as an int) but it's null because the database query didn't return a value for that row. You were hitting this with your code because som int column (for example) was DBNull.Value and the only thing a datarow will do whn you try to access an int column tha tis DBNull, is throw an exception. By specifying SELECT * in the query, you caused the query to return a value for the column; by not selecting a column the datatable will always treat it as null. You can still also encounter a strongtypingexception if the value of the data in the database is also null. In these cases, use the IsXXXNull() methods to determine if the property is null before trying to access it (causing a strongtypingexception)
Edit: Found the error, I was supposed to get all the columns from the query (using *) and then in the code take what I need otherwise null values get returned.
in my database i have many of tables one of Tables is nambed Company". and its columns names are as follows ("ComName","ComAddress","ComType" ..etc)
I have a function which get the columns name from Company table :
public List<string> GetColumnNames(string tablename)
{
var names = typeof(Company).GetProperties().Select(p => p.Name).ToArray();
return names.ToList();
}
Until now everything is good but what if i want to get the columns of other tables ? so i have to put the name of the new table (that is given in the function as "tablename") instead of "Company" in this sentence :
var names = typeof(Company).GetProperties().Select(p => p.Name).ToArray();
but it is string so its not working. any idea ?
typeof(Company).GetProperties() isn't fetching the names of the columns of your table at all though - it's fetching the names of the properties of the C# class/entity that you're using to represent that table. Do you already have a process to guarantee you have C# entities with property names that exactly match the column names of your tables? If so, you can do the same principle (but instead of using typeof(Company) you'd need to find the type by name, which there are various ways of doing). If not, you'll need to to actually query the database to find the column names.
As far as i know, you can use the Type parameter.
public List<string> GetColumnNames(Type tablename)
{
var names = tablename.GetProperties().Select(p => p.Name).ToArray();
return names.ToList();
}
I'm trying to change an object value from other class. Ideally, I want to pass the object as parameter into constructor/method. What I've read so far, object behaves as reference when passed as parameter, and parameter value are copied locally for use in the method's body. So here are few configuration that I tested:
Case #1. Failed
class Processor
{
DataTable table;
public Processor(DataTable table)
{
this.table = table;
}
public void InitializeTable()
{
if (table != null)
{
// Fill data into DataTable.
}
}
}
static void Main(string[] args)
{
DataTable mainTable = new DataTable();
Processor processor = new Processor(mainTable);
processor.InitializeTable();
// mainTable still empty
}
I thought Processor table holds the same reference to mainTable, but by the end of Main execution mainTable was still EMPTY while Processor table was filled.
Case #2. Failed
public Processor(ref DataTable table)
{
this.table = table;
}
I've tried using ref signature, but result still the same (mainTable was EMPTY).
Case #3. Failed
public void InitializeTable(DataTable table)
{
// Fill data into table
}
I've removed the constructor and feed mainTable into InitializeTable() method, and result still the same (mainTable was EMPTY).
Case #4. Works!
public void InitializeTable(ref DataTable table)
{
// Fill data into table
}
Finally this works! Feeding ref mainTable into InitializeTable now fills mainTable successfully. What are the explanation behind this? Why constructor didn't have the same reference to mainTable? Why ref keyword still needed when passing object as parameter already means passing its reference?
Credit goes to David:
Then that explains the behavior. You're re-assigning the local table
variable to a new DataTable instance (returned by .SelectArray()). So
it no longer refers to the same instance as the mainTable variable and
no longer modifies that instance.
This question already has answers here:
Modify Struct variable in a Dictionary
(5 answers)
Closed 7 years ago.
I have the following code that will modify a property inside a structure, and the structure is inside a hash table. Each item in hash table has key of (Int) data type and key of (struct Bag), here is the code i have:
struct Bag {
string apple_type;
string orange_type;
};
// Make a new hashtable that will have a key of (int) data type, value of (Bag)
public static Hashtable bags = new Hashtable();
Then i have a method that will read data from a data base reading rows and adding as long there is a row it will add an item (bag(object)) to the hashtable:
public void initHashtbl(){
OleDbConnection db_connector = new OleDbConnection(connection_string);
// Open connection to oracle
db_connector.Open();
OleDbCommand sql_commander = new OleDbCommand(sql_statement, db_connector);
OleDbDataReader data_Reader = sql_commander.ExecuteReader();
// Read data from sql db server and store it in memory ...
Bag tempBag = new Bag();
// row counter used for list view
int row_counter = 0;
while (data_Reader.Read()) { // Keep reading data from memory until there is no row
tempBag.apple_type = data_Reader[0].ToString();
bags.Add(row_counter, tempBag);
row_counter++;
}
for(int bag_item=0;bag_item < bags.Count;bag_item++){
// Get orange type value from another method that uses another sql statement from another table in db ..
((bag) bags[bag_item]).orange_type = getOrangeType(((bag) bags[bag_item]).apple_type);
}
}
How can i access the property of structure that is already inside hash table at later time if i wanted to access it?
Edit:
I'm getting this error:
"Cannot modify the result of an unboxing conversion."
Dictionary will not allow me to modify that directly
Neither will a Hashtable. This has nothing to do with Hashtable vs Dictionary. The problem is that your "value" in either case is a value type, so you can't modify it directly within the collection. If you really need a struct, then you'll have to create a new value and put it back into the hashtable:
bags.Add(1, new Bag() {apple_type="apple1",orange_type="orange1"});
//((Bag)bags[1]).apple_type="apple2";
var bag = (Bag)bags[1];
bag.apple_type = "appple2";
bags[1] = bag;
But mutable structs are generally bad, so I would either get the value of the struct right the first time (rather than modifying it ourside of the initial load loop) :
// row counter used for list view
int row_counter = 0;
while (data_Reader.Read()) { // Keep reading data from memory until there is no row
{
var appleType = data_Reader[0].ToString();
Bag tempBag = new Bag() {
apple_type = appleType,
orange_type = getOrangeType(appleType)
};
bags.Add(row_counter, tempBag);
row_counter++;
}
or use a class.
Note that the same code works exactly the same way whether you use a Hashtable or a Dictionary.
Also, since your "key" is just an incrementing number (and not tied to the value at all), you could just as well use a List<Bag> and access the items by index.
This code worked for me to read an item:
string orangeType = ((Bag) bags[0]).orange_type;
To modify an item, I think you must create a new Bag and replace the existing one like this:
bags[0] = newBag;
If you try to modify the properties of a bag in the HashTable, then you get the error you reported above.
Because of how value types work, the boxed Bag is a copy of the original, and "unboxing" it by casting back to Bag creates yet another copy. From the C# language spec:
When a value of a value type is converted to type object, an object
instance, also called a “box,” is allocated to hold the value, and the
value is copied into that box. Conversely, when an object reference is
cast to a value type, a check is made that the referenced object is a
box of the correct value type, and, if the check succeeds, the value
in the box is copied out.
Modifying the copy wouldn't change the original anyway, so it wouldn't make much sense to allow it.
Corrections:
To fix your error and to allow modifications to your HashTable of Bag's you should change your value type to reference type:
public class Bag
{
public string apple_type { get; set; }
public string orange_type { get; set; }
};
Trying to return a Linq group by query into a DataTable. Getting the error
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<AnonymousType#1>' to
'System.Collections.Generic.IEnumerable<System.Data.DataRow>'. An
explicit conversion exists (are you missing a cast?)
The I am querying a DataTable named Vendors where the data would be as follows:
Vendor Name
654797 Lowes
897913 Home Depot
800654 Waffle House
The Vendor is stored as char(6) in the DB and name as char as well... don't ask me why, I just work here :)
DataTable VendorsDT = New DataTable();
DataColumn VenName = VendorsDT.Columns.Add("Name", typeof (string));
DataColumn VenCode = VendorsDT.Columns.Add("Vendor", typeof(string));
IEnumerable<DataRow> Vendors = from row in alertsDT.AsEnumerable()
group row by new { Name = row.Field<string>("Name"), Vendor = row.Field<string>("Vendor") } into z
select new
{
Name = z.Key.Name,
Vendor = z.Key.Vendor,
};
VendorsDT = Vendors.CopyToDataTable();
This is not going to work, as you are projecting your original query of alertsDT into an anonymous type, which would not be able to be referenced by your IEnumerable<DataRow> Vendor variable, because your query is not a sequence of DataRows.
Given that you are performing a grouping, and that you have also already set up your VendorsDT table with the desired columns, the path of least resistance is to fix your Vendor variable type (use var for type inference) and then loop over the result to populate your second table.
var Vendors = /* your unchanged query omitted */
foreach (var item in Vendors)
{
VendorsDT.Rows.Add(item.Name, item.Vendor);
}
As a note, I've used your variable names, although it is convention in C# to use lower case letters to start local variable names, with upper case typically left for method names, properties, etc. So you would favor vendors over Vendors and vendorsDT (or vendorsTable) over VendorsDT, for example.
This is a more linq way of doing
var query = from row in alertsDT.AsEnumerable()
group row by new { Name = row.Field<string>("Name"), Vendor = row.Field<string>("Vendor") } into z
select new
{
Name= z.Key.Name,
Vendor= z.Key.Vendor,
};
VendorsDT = query.CopyToDataTable();
Here define the extension method 'CopyToDataTable()' as specified in the following MSDN article.
The CopyToDataTable method takes the results of a query and copies the data into a DataTable, which can then be used for data binding. The CopyToDataTable methods, however, only operate on an IEnumerable source where the generic parameter T is of type DataRow. Although this is useful, it does not allow tables to be created from a sequence of scalar types, from queries that project anonymous types, or from queries that perform table joins.