I read How to read single Excel cell value and tried it myself. But when I gets to
string s = (myExcelWorkSheet.Cells[3, "E"] as Excel.Range).Value2.ToString();
Everything was terminated and the form was shown.
//Everything worked fine here.
string s = (myExcelWorkSheet.Cells[3, "E"] as Excel.Range).Value2.ToString();
//Everything after this was all skipped!
Why is this, and how can I fix it?
The problem with reading excel cell is that if there is nothing in it, the cell object is Null. Thus, it does not have .Value2 neither .Value.
To find a way how to avoid the check for Null, you may use Convert.ToString() which evaluates the Null to an empty string and thus does not return an error:
for (int i = 1; i < 5; i++)
{
string a = Convert.ToString(wk.Cells[i, 1].Value2);
Console.WriteLine(a);
}
When the cell has a value, you need the ToString().
And when the cell doesn't has a value, then you don't need the ToString()!
Otherwise the whole program will skip out and everything after that was NEVER executed!!!
So I guess it's just a problem of if the system was trying to cast a null value into a string or not!!!
Related
I have a C# program that reads in an excel workbook, and then builds an XML file that can be ran against an XSLT. The problem that has cropped up is that one of the fields is a number, and when reading it out of the excel sheet the value is being changed. Here is the example:
The excel spreadsheet is read in, and the data is loaded into a data table. One of the ways I do this is by taking the spreadsheet document I create, and pass the cell reference into this method here:
dataRow[columnIndex] = GetCellValue(spreadSheetDocument, cell);
private static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
//This process uses the OpenXML SDK to get individual cells values to populate the DataTable
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = "";
//One of the things that needed to be accounted for was empty cells
try
{
value = cell.CellValue.InnerXml;
}
catch (Exception)
{
value = "";
}
//Setting cell data type right now just sets everything to strings
//Later, the better option will be to work on the Date Conversions and Data Types here
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
else
{
return value;
}
}
So as an example, if the cell that it is reading in is 115, then the output looks like this:
114.99999999999999
Then at other times if the value is 125 then the output looks like this:
125.00000000000001
the inconsistency in the output is a bit perplexing. Was hoping maybe I could get some insight into what is causing this, rather than just fixing it in the XSLT later on.
So I found a workaround, more than an actual solution. Apparently this is a bug in the OpenXML SDK. I found the initial documentation that pointed me in this direction here
What I found as a way to work around was this:
private static string GetCellValue(SpreadsheetDocument document, Cell cell)
{
//This process uses the OpenXML SDK to get individual cells values to populate the DataTable
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
string value = "";
//One of the things that needed to be accounted for was empty cells
try
{
value = cell.CellValue.InnerXml;
}
catch (Exception)
{
value = "";
}
//Checking to see if this string contains a decimal with values on either side
if (Regex.IsMatch(value, regexpattern))
{
value = Math.Round(Double.Parse(value), 0, MidpointRounding.AwayFromZero).ToString();
}
//Setting cell data type right now just sets everything to strings
//Later, the better option will be to work on the Date Conversions and Data Types here
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
else
{
return value;
}
}
I'm using Regex to determine if this bug is being encountered and then compensating using some rounding. It is interesting to note that only appears to happen with integers.
Thanks!
This question is basically the same as this one, although the answer to that person's problem turned out to be a simple trailing space.
My issue is that I'm retrieving data from a web API as dictionary and then trying get the values out of it. I'm using TryGetValue because not every item in the dictionary will necessarily contain every key. For some reason, whilst I can get the value of one key with no problems at all when it's present, for another key TryGetValue always evaluates to false and therefore doesn't return the value, even though I can see in debug that the key is present.
So, this block always retrieves the value of the "System.Description" key if it's present:
string descriptionValue = "";
if (workItem.Fields.TryGetValue("System.Description", out descriptionValue))
{
feature.Description = descriptionValue;
}
However, this almost identical block NEVER retrieves the value of the "CustomScrum.RoadmapGroup" key:
int RoadmapGroupValue = 0;
if (workItem.Fields.TryGetValue("CustomScrum.RoadmapGroup", out RoadmapGroupValue))
{
feature.RoadmapGroup = RoadmapGroupValue;
}
As you can see in this screenshot, the dictionary DOES contain a key with a name exactly matching my TryGetValue statement:
If I put a breakpoint on the code which should be run if the TryGetValue statement evaluates to true (feature.Description = descriptionValue;) it never gets hit.
The feature.RoadmapGroup variable gets set to 0 for every item in the dictionary.
I've been staring at this for the last two hours at least and I can't see what I'm doing wrong.
Here's a scenario where your cast goes wrong.
private void foo()
{
Dictionary<string, object> dict = new Dictionary<string, object>();
object obj = new object();
obj = "1";
dict.Add("CustomScrum.RoadmapGroup", obj);
object val;
var result = dict.TryGetValue("CustomScrum.RoadmapGroup", out val);
int value = (int)val;
}
TryGetValue() returns true, but the last line (the cast), throws System.InvalidCastException: 'Specified cast is not valid.', although if you use a breakpoint to see the dictionary content it looks like you have something that can be converted to an int. See below:
So I believe that when you add the value to the dictionary, you're not really adding an int but something that looks like an int.
EDIT
I just replaced int value = (int)val; with int value = Convert.ToInt32(val); which converts the value just fine. So you might want to try to use that and see if that works as well.
Are you sure that this "CustomScrum.RoadmapGroup" key is a string? If yes, then make sure that it doesn't contain any special unreadable character. You can just copy this value while debugging, put it in Watch window and check length/bytes representation, then do the same for hand-written string with the same content.
Its my first experience with excel file using C#.
As a demo, I wrote a single line in excel file and it worked but now I am trying to insert all the rows of DataTable but it gives the following error:
"HRESULT: 0x800A03EC"
Here is the code:
for (int i = 0; i < table.Rows.Count; i++)
{
xlWorkSheet.Cells[i, 1] = table.Rows[i]["Ref"].ToString();
xlWorkSheet.Cells[i, 2] = table.Rows[i]["Name"].ToString();
}
Please tell me how can I fix it?
Use Range.Value instead. Worksheet.Cells property is readonly:
for (int i = 0; i < table.Rows.Count; i++)
{
xlWorkSheet.Cells[i, 1].Value = table.Rows[i]["Ref"].ToString();
xlWorkSheet.Cells[i, 2].Value = table.Rows[i]["Name"].ToString();
}
Instead of a DataTable, put all the values into a object[,]
Then you can assign them in one swoop:
using (var targetRangeHeader = _excelApp.Range[TargetRangeTopAddress].WithComCleanup())
using (var targetRangeFirstDataCell = targetRangeHeader.Resource.Offset[1, 0].WithComCleanup())
using (var targetRange = targetRangeFirstDataCell.Resource.Resize[MyObjectArrayValues.Length, 1].WithComCleanup())
{
targetRange.Resource.Value2 = MyObjectArrayValues;
Using http://jake.ginnivan.net/vsto-com-interop
I wholeheartedly endorse Jeremy Thompson's answer to use a 2-dimensional object array. The performance will be orders of magnitude faster. I'm writing to explain why the code you've written doesn't work (and why you probably thought it should work).
In Visual Basic, there's a concept of "Default" property. When you write this in VBA, you're using that concept:
xlWorkSheet.Cells(i, 1) = someValue
The Cells property returns a Range object. In VBA, since you haven't used the Set keyword, this line is not reassigning the value of the object (and, if you added the Set keyword, it would fail to reassign the value of the object, since it's a read-only property, as Ria notes).
The absence of the Set keyword causes the assignment to go to the default prpoperty of the Range object returned by Cells(i, 1). The default property, of course, is the Value property. The above VBA sample, in other words, is equivalent to this:
Dim r As Range
Set r = xlWorkSheet.Cells(i, 1)
r.Value = someValue
Of course, you can also skip the r variable:
xlWorkSheet.Cells(i, 1).Value = someValue
Now, C# doesn't have the default property concept. You're therefore unable to write this code the shortcut way, which leads us to the code suggested in Ria's answer:
xlWorkSheet.Cells[i, 1].Value = someValue;
Or, with the real value instead of someValue:
xlWorkSheet.Cells[i, 1].Value = table.Rows[i]["Ref"].ToString();
The default property makes it easy for beginners to write code, and it helps code to be concise, but it's also confusing for people who are between the beginner stage and the advanced stage.
I have following section of code in my program:
object val;
val = arr[1].Trim(); // in my case i am getting value here is 1.00
now when I am assigning value to a datarow I am getting error
Expected int64 value.
datarow[Convert.ToString(drow["col"]).Trim().ToUpper()] = val;
I am not facing any issue when getting value other that 1.00.
What could be the exact problem? How can I solve it?
Suggestions and solutions are welcome
If that column in your datatable is expecting an Int64 you need to convert val (which is a string) to an Int64:
var val = arr[1].Trim(); // String at this point
long longVal = 0;
if(!long.TryParse(val,out longVal){
throw new InvalidOperationException("value wasnt an Int64!");
}
datarow[Convert.ToString(drow["col"]).Trim().ToUpper()] = longVal
arr[1] seems to be string, and applying .Trim() keeps it as a string, even if it's "1.00". If you need an integer, you need to parse it. However, it can't be parsed to an intteger, because it's actually a double.
As a proof of whether I'm right or not, you can try (Int64)double.Parse(val) and that should work. However, it's up to you to decide whether that's not an issue for your program. There's two possible issues:
val might not be parse-able to double, in which case you will get an exception
val might be a double, but not one that can be represented as an int (too large, or lose precision ex. "1.8" would become 1)
Hope this helps
I'm using some old code that runs a sql query as a reference.
At some point, it gets to something like:
sqlDataAdapter.Fill(dataSet);
DataRow dataRow = dataSet.Tables[0].Rows[0];
Object obj = dataRow[fieldName];
The old code does:
string output;
if (!string.IsNullOrEmpty(obj.ToString())) { output = obj.ToString(); }
else { output = "Not Available"; }
I changed them to:
output = obj as string ?? "Not Available"
But sometimes, it broke. As I suspected, it was happening breaking when the entry was an int. Casting as an int in those cases solved that problem.
Then another problem arose when there was no entry for obj[fieldName] of type int. When I stepped through the debugger, I was surprised to find that obj wasn't null. In VS, mousing over revealed it had a value {}.
What the heck is {}? How do I make a boolean test of it?
(In the old code, it appears .ToString() returns "" in this case and works as expected.)
{ and } are opening and closing braces and symbolic of the start and finish of an object. Hence an empty object with no special properties is depicted in shorthand as {}. The debugger uses this notation to help you visually distinguish between an empty object, an empty string and null.
If you hover over obj[fieldName] and there is no entry for fieldName, the debugger won't care about that, it'll show the value of obj. You'll have to use the immediate window or a watch/quickwatch. The debugger will only see you hovering over obj and assume you're referring to the array itself, not the contents of the array at the specified index.
In case anyone comes across again this problem.
Solution if val object is shown {} in debug mode
// Check if its not null or empty
if (!IsNullOrEmpty(val.ToString().ToArray()))
{
// Do something with val
dt.Rows.Add(val);
}
public static bool IsNullOrEmpty<T>(T[] array)
{
return array == null || array.Length == 0;
}