Read value from excel - c#

I have a problem that I can't solve. I need to pass a value from one Selenium test to another. And the writing part works just fine. I saves the value to an excel file. The problem appears when I try to read it. I have a class which should be able to read the cell, but it won't.
Excel.cs
using System.IO;
using OfficeOpenXml;
using excel = Microsoft.Office.Interop.Excel;
namespace Core
{
public sealed class Excel
{
#region Private Properties
private static string InputPath { get; } = ConfigSettingProvider.InputSourcePath;
private static string OutputPath { get; } = ConfigSettingProvider.OutputSourcePath;
#endregion Private Properties
public static string ReadCell (int sheet, int x, int y) // (sheet, row, column)
{
FileInfo existingFile = new FileInfo("..\\..\\..\\Property\\Input\\data1.xlsx");
using (ExcelPackage package = new ExcelPackage(existingFile))
{
return package.Workbook.Worksheets[sheet].Cells[x, y].Value.ToString();
}
}
}
}
And then in Test Fixture class I am trying to call ReadCell method:
var cellPolicyNo = Excel.ReadCell(1, 1, 1);
But it simply won't even enter to read it. Immediately the exception is thrown: "The type initializer for 'Core.Excel' threw an exception". at Core.Excel.ReadCell(Int32 sheet, Int32 x, Int32 y)
What am I doing wrong? What should I change?
Thank you

You might want to take a look at this thread to see if it helps with answering your question.
How to read single Excel cell value
This is the more common way of reading from an excel file that is more reliable. There are also ways of doing it through OLEDB but that is for reading the entire excel file. Hopefully this helps as it is similar to code I have used in the past to read data from excel files.

It appears to be a static constructor problem, see here:
https://stackoverflow.com/questions/4398334/the-type-initializer-for-myclass-threw-an-exception#:~:text=The%20type%20initializer%20for%20%27CSMessageUtility.,-CSDetails%27%20threw%20an&text=means%20that%20the%20static%20constructor,static%20members%20of%20that%20class.
Btw, it might perhaps help to try a different toolkit, namely EPPlus (the last free version is 4.5.3.3). I've tried to read cells also with Excel Interop but it didn't work out so well. With EPPlus you can use LINQ on the range objects of Excel.

Related

Can I use OpenXmlPowerTools in a Flask App

Is it possible to use a C#/.Net based library like OpenXmlPowerTools in a Flask/Python 3 web-app?
My research tells me that I might be able to via a wrapper? or creating a microservice?
Would like to know your thoughts.
You can do this with pythonnet. I've created a sample class in C# that references the OpenXmlPowerTools and DocumentFormat.OpenXml assemblies and provides two methods:
using OpenXmlPowerTools;
using DocumentFormat.OpenXml.Packaging;
namespace CodeSnippets.OpenXmlWrapper
{
public class OpenXmlPowerToolsWrapper
{
public static string GetMainDocumentPart(string path)
{
using WordprocessingDocument wordDocument = WordprocessingDocument.Open(path, true);
return wordDocument.MainDocumentPart.GetXElement().ToString();
}
public static string FinishReview(string path)
{
using WordprocessingDocument wordDocument = WordprocessingDocument.Open(path, true);
var settings = new SimplifyMarkupSettings
{
AcceptRevisions = true,
RemoveComments = true
};
MarkupSimplifier.SimplifyMarkup(wordDocument, settings);
return wordDocument.MainDocumentPart.GetXElement().ToString();
}
}
}
The corresponding sample Python client looks like this, noting that clr is a module provided by pythonnet:
import clr
import shutil
clr.AddReference(
r"..\CodeSnippets.OpenXmlWrapper\bin\x64\Debug\net471\CodeSnippets.OpenXmlWrapper")
from CodeSnippets.OpenXmlWrapper import OpenXmlPowerToolsWrapper
wrapper = OpenXmlPowerToolsWrapper()
# Display contents before finishing review, showing that the document contains revision markup.
xml_with_revision_markup = wrapper.GetMainDocumentPart("DocumentWithRevisionMarkup.docx")
print("Document before finishing review:\n")
print(xml_with_revision_markup)
# Finish review, removing all revision markup.
print("\nFinishing review ...")
shutil.copyfile("DocumentWithRevisionMarkup.docx", "Result.docx")
xml_without_revision_markup = wrapper.FinishReview("Result.docx")
# Display contents after finishing review, showing that the revision markup was removed.
print("\nDocument after finishing review:\n")
print(xml_without_revision_markup)
You'll find the full source code in my CodeSnippets GitHub repo. Look at the CodeSnippets.OpenXmlWrapper and CodeSnippets.OpenXmlWrapper.PythonClient projects.
Depending on your use case, you will also be able to use the OpenXmlPowerTools directly. I just implemented a wrapper to provide a simplified interface.

What is the replacement for TestContext.DataRow["MyColumnName"]

Using MSTest in a .Net Core Unit test project. I am attempting to use a csv datasource to provide the data for a test method.
Previously, I would use something like below in a .Net Framework test project:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", #"data.csv", "data#csv", DataAccessMethod.Sequential),
DeploymentItem("data.csv"),
TestMethod]
public void ValuesController_Post()
{
_controller.Post(TestContext.DataRow["body"]);
_valuesRepository.Verify(_ => _.Post(It.IsAny<string>()), Times.Once);
}
The key here being the DataRow property found in TestContext. This doesn't appear to exist in the .Net Core version of the TestContext.
How would I go about doing this in .Net Core?
Since moving to aspnet core, I've never been able to use the same [Datasource(...)] attribute to iterate through test data, my data-driven tests are always skipped.
Have you considered switching to another approach with [DataTestMethod] and [DynamicData] with a custom source that reads you file ?
Here's a good article on this :
https://www.meziantou.net/2018/02/05/mstest-v2-data-tests
Maybe another way would be to read the whole file at the begining of the test and then iterate through the dataset as One single unit test?
Hope this helps.
It took me an afternoon to fiddle with things, but I finally found a solution. Since you don't specify your test or CSV file, here is a quick example I could get working.
Long story short, I installed the CsvHelper NuGet package, because parsing CSV is dead easy right up to the point it is not. As Carl Verret pointed out, you need to use the [DynamicData(...)] attribute above your test method, and then parse the CSV using CsvHelper.
The CSV File (Example.csv)
A,B,IsLessThanZero
1,2,FALSE
3,-5,TRUE
Important: Make sure this CSV file is included in your test project and "Copy To Output Directory" is set to "Always" in the properties for the CSV file in Solution Explorer.
Data Transfer Object Used By CsvHelper
public class AdditionData
{
public int A { get; set; }
public int B { get; set; }
public bool IsLessThanZero { get; set; }
}
The Test Class
[TestClass]
public class ExampleTests
{
// HINT: Look in {Your Test Project Folder}\bin\{Configuration}\netcore3.1\FolderYourCsvFileIsIn for the CSV file.
// Change this path to work with your test project folder structure.
private static readonly string DataFilePath = Path.GetDirectoryName(typeof(ExampleTests).Assembly.Location) + #"\FolderYourCsvFileIsIn\Example.csv";
[TestMethod]
[DynamicData(nameof(GetData), DynamicDataSourceType.Method)]
public void AddingTwoNumbers(AdditionData data)
{
bool isLessThanZero = data.A + data.B < 0;
Assert.AreEqual(data.IsLessThanZero, isLessThanZero);
}
private static IEnumerable<object[]> GetData()
{
using var stream = new StreamReader(DataFilePath);
using var reader = new CsvReader(stream, new CsvConfiguration(CultureInfo.CurrentCulture));
var rows = reader.GetRecords<AdditionData>();
foreach (var row in rows)
{
yield return new object[] { row };
}
}
}
After building your solution, you will see a single test in Test Explorer. Running this single test runs all variants defined in your CSV file:

Adding TextBoxes to list

I am trying to add a group of textboxes to a list. I keep getting an error cannot convert from 'System.Windows.Forms.TextBox' to 'Microsoft.Office.Interop.Excel.TextBox'. Not sure why I am getting this error. Do I need to add a cast?
Here is my code. The textboxes I want to add are in getValues. I haven't worked a lot with windows forms so this error is a mystery for me.
class DiagnosticsExcelFile: ConfigForm
{
string fileName = "";
private ConfigForm configForm;
Excel.Application xlApp = null;
Excel.Workbook xlWorkBook = null;
Excel.Worksheet xlWorkSheet = null;
List<TextBox> list = new List<TextBox>();
public DiagnosticsExcelFile(ConfigForm config)
{
fileName = "Z:\\UCA\\Diagnostics.xlsx";
this.configForm = config;
this.list.Add(this.configForm.HighOffSetX); // get error here
}
private void getValues()
{
this.configForm.HighOffSetX.Text = "5.0";
this.configForm.HighOffSetY.Text = "5.0";
this.configForm.HighOffSetZ.Text = "5.0";
}
}
Define your list like:
List<System.Windows.Forms.TextBox> list = new List<System.Windows.Forms.TextBox>();
Currently your List is referring to TextBox from Microsoft.Office.Interop.Excel.TextBox.
Both Microsoft.Office.Interop.Excel= and System.Windows.Forms has a class TextBox, since you haven't shown your using directives, I assume you have
using Microsoft.Office.Interop.Excel;
and you don't have a using directive for System.Windows.Forms, therefore your code is considering your List to contain TextBoxes from Office Interop. (By the way, if you have both references, then you will get an error for ambiguous reference to TextBox)
By explicitly defining System.Windows.Forms.TextBox, your error will go away.
using TextBox = System.Windows.Forms.TextBox;
I usually find this approach the most efficient.
It doesn't require many code changes and allows to keep lines short.
You're not specifying a full namespace, but you probably have:
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
in your code file.
The compiler can't tell which TextBox you mean. You can fully qualify each type name, or alias one of the usings.
using xl = Microsoft.Office.Interop.Excel;
Then you would refer to the Excel Textbox as xl.TextBox. Specifying just TextBox would give you the System.Windows.Forms one that you expect.
You are hiding the TextBox type from System.Windows.Forms.TextBox (maybe with a using TextBox = Microsoft.Office.Interop.Excel.TextBox).
Try declaring your list with:
List<System.Windows.Forms.TextBox> list = new List<System.Windows.Forms.TextBox>();
Your definition is correct. The problem occurs when adding new members to the list. Try the following:
list.Add(HighOffSetX);
You dont need the whole "this" stuff at this point.

OFX File parser in C#

I am looking for an OFX file parser library in C#. I have search the web but there seems to be none. Does anyone know of any good quality C# OFX file parser. I need to process some bank statements files which are in OFX format.
Update
I have managed to find a C# library for parsing OFX parser.
Here is the link ofx sharp. This codebase seems to be the best case to startup my solution.
I tried to use the ofx sharp library, but realised it doesn't work is the file is not valid XML ... it seems to parse but has empty values ...
I made a change in the OFXDocumentParser.cs where I first fix the file to become valid XML and then let the parser continue. Not sure if you experienced the same issue?
Inside of the method:
private string SGMLToXML(string file)
I added a few lines first to take file to newfile and then let the SqmlReader process that after the following code:
string newfile = ParseHeader(file);
newfile = SGMLToXMLFixer.Fix_SONRS(newfile);
newfile = SGMLToXMLFixer.Fix_STMTTRNRS(newfile);
newfile = SGMLToXMLFixer.Fix_CCSTMTTRNRS(newfile);
//reader.InputStream = new StringReader(ParseHeader(file));
reader.InputStream = new StringReader(newfile);
SGMLToXMLFixer is new class I added into the OFXSharp library. It basically scans all the tags that open and verifies it has a closing tag too.
namespace OFXSharp
{
public static class SGMLToXMLFixer
{
public static string Fix_SONRS(string original)
{ .... }
public static string Fix_STMTTRNRS(string original)
{ .... }
public static string Fix_CCSTMTTRNRS(string original)
{ .... }
private static string Fix_Transactions(string file, string transactionTag, int lastIdx, out int lastIdx_new)
{ .... }
private static string Fix_Transactions_Recursive(string file_modified, int lastIdx, out int lastIdx_new)
{ .... }
}
}
Try http://www.codeproject.com/KB/aspnet/Ofx_to_DataSet.aspx. The code uses Framework 3.5 and transforms an ofx into a dataset, this may help with what you're trying to do.

FileVersionInfo.GetVersionInfo() incorrect in Console Application

I'm getting some serious weirdness using FileVersionInfo.GetVersionInfo() and was hoping somebody might be able to help.
The basics of the issue is that I am iterating through all the files in a folder calling GetVersionInfo() on each. There are about 300 files. This works ok for all but 2 of the files. For these DLLs I am getting comepletely incorrect info back from GetVersionInfo().
In order to eliminate all other variables, I extracted this call into a simple test app and it still got the same problem. However, if I built the test app as a Windows Application (it was a Console Application initially) then the data came back correct.
Just to clarify, the incorrect data coming back when running as a Console App is not simply null info like you would get if the file didn't contain version data. It contained reasonable data, but just the wrong data. It's as if it's reading it from a different file. I've looked for a file that contains matching version data, but can't find one.
Why is this simple call functioning differently if built as a Console Application rather than a Windows Application?
If anyone can help with this I would be very grateful.
Rgds,
Andy
-- Code Added
using System;
using System.Diagnostics;
namespace test
{
class Program
{
static void Main(string[] args)
{
string file = "C:\\ProblemFile.dll";
FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
string fileName = version.FileName;
string fileVersion = version.FileVersion;
Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
}
}
}
This behaviour seems weird indeed. Could it be that the Console application does not load the DLL from the same place as the WinForms application does? This would mean that GetVersionInfo uses some other API than just Win32 CreateFile (maybe going through some DLL resolver mechanism, side-by-side or whatever); remember that under the covers, version.dll will be executing your request, not the CLR itself.
Looking at FileVersionInfo through Reflector points in another direction yet:
public static unsafe FileVersionInfo GetVersionInfo(string fileName)
{
// ...
int fileVersionInfoSize = UnsafeNativeMethods.GetFileVersionInfoSize(fileName, out num);
FileVersionInfo info = new FileVersionInfo(fileName);
if (fileVersionInfoSize != 0)
{
byte[] buffer = new byte[fileVersionInfoSize];
fixed (byte* numRef = buffer)
{
IntPtr handle = new IntPtr((void*) numRef);
if (!UnsafeNativeMethods.GetFileVersionInfo(fileName, 0, fileVersionInfoSize, new HandleRef(null, handle)))
{
return info;
}
int varEntry = GetVarEntry(handle);
if (!info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(varEntry)))
{
int[] numArray = new int[] { 0x40904b0, 0x40904e4, 0x4090000 };
foreach (int num4 in numArray)
{
if ((num4 != varEntry) && info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(num4)))
{
return info;
}
}
}
}
}
return info;
}
As you can see there, some interesting dance is going on with code pages. What if the DLLs you inspected had several version information resources attached to them? Depending on the culture of the program calling into GetVersionInfo, I guess that the code page related calls could return other results?
Take the time to check the resources of the DLLs and make sure that there is only one language/code page for the version information. It might point you at the solution, I hope.
Sure the "files" you're seeing aren't . and .. ? If you iterate through all files, you'll always see entries for . (current dir) and .. (up dir). GetVersion Info might well return anything for these. You'd have to filter these entries out manually by name.
File and Assembly versions are 2 different things.
Are you sure you are not expecting the other?
Update: Tried this. Didn't work.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[DllImport("COMCTL32")]
private static extern int InitCommonControls(int nExitCode);
static void Main(string[] args)
{
InitCommonControls(0);
string file = "C:\\ProblemFile.dll";
FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
string fileName = version.FileName;
string fileVersion = version.FileVersion;
Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
}
}
}

Categories