Excel.Workbook as a Global variable C# - c#

I am trying to read and write to several excel sheets and I have split the functions into different methods.
The problem I am encountering is I cannot carry over the Workbook/Worksheet names over into the second method.
First method: Open all the relevant excel documents i.e Parts list, Export list etc.
Second method: Copy data from Parts List to first sheet in Export list.
For example in the first method I may have
//Ws = Worksheet
//Wb = Workbook
//Workbooks and applications already defined
var PartsExportWs = PartsExportWb.Sheets[1].Name;
In the Second method I have:
public static void Parts
{
int PartsCounterX;
int TypicalCounterY = 4;
int NullCounter = 0;
var ConCatPartsCellValue = new System.Text.StringBuilder();
for (PartsCounterX = 1; NullCounter <= 3; ++PartsCounterX)
{
var PartsCellValue = PartsExportWs.Cells[TypicalCounterY, PartsCounterX].Value;
// etc ...
However, it errors out at PartsExportWs with the Description of "The name PartsExportWs does not exist in the current context"
I may be wrong but I am assuming that it is due to the fact it is not classed as a Global variable.
(If anyone has any suggestions it would be more than helpful. Even if it is on how to ask the question better!)

You cannot access a local variable from one method in the code from another method.
You either need to pass the variable as a parameter, or store it in a field.
(There is no such thing as a "global variable" in C#.)

Related

Issue on exporting semicolon delimited file to SQL Server

Well, I'm facing an issue on exporting my data to SQL Server as the subject says.
I have a semicolon delimited file, but also I have occurrences when I find semicolon inside the text, for example:
ID;DESCRIPTION;VALUE
1;TEXT1;35
2;TEXT;2;45
3;TE;XT3;50
So as you can see I have some garbage that I would like to remove, since this is shifting the columns.
I have some ideas, like make a standard count of semicolons, in this case it would be 2 semicolons by line and remove the extra ones.
In my case this is always happening in 1 column specifically the Address column and complement, so i know exactly what the number of the column is.
I cant ask people who dispose this file since the system is an old system and they can't put qualifiers like double quotes or simply change the delimiter.
I know I could do this via script task but I have few knowledge on programming, so I'm trying to look for another manner.
I'd like to say again that this problem is happening on source file so when I configure the flat file connection it already shift the column so I can't make any treatment like derived column or something else. I have to do the changes on the file itself before I load it in SSIS.
I've been looking for some days on any kind of forums and I don't see any similar questions and solutions for this problem, since most of the example files of people who asks, already have qualifiers or something like this, so I really appreciate if you can help me!
You mentioned you have little programming knowledge but a script is only solution that can handle delimiters in fields that are not enclosed. You are fortunate there is only a single problem field as it wouldn't be possible to parse ambiguous delimiters unless you have additional rules to determine where actual fields begin and end.
As long as you are certain there is only one field with embedded delimiters, one method is a data flow source Script component. Below are the steps to create one:
Add a script component to data flow and select Source for the type
Add the flat file connection manager to the script properties Connection Managers collection
Add each field as an output column under the script properties component Input and Outputs
Edit the script source and replace the template 'CreateOutputRows()' method code with the version code below.
See comments in the script indicating where customizations are needed for your actual file. This version will work with your sample file of 3 fields, with the second field having embedded delimiters.
public override void CreateNewOutputRows()
{
const char FIELD_DEMIMITER = ';';
////*** change this to the zero-based index of the problem field
const int BAD_FIELD_INDEX = 1;
////*** change this to the connection added to script componenent connection manager
var filePath = this.Connections.FlatFileSource.ConnectionString;
string record = "";
using (var inputFile = new System.IO.StreamReader(filePath))
{
record = inputFile.ReadLine();
if(record != null)
{
//count header record fields to get expected field count for data records
var headerFieldCount = record.Split(FIELD_DEMIMITER).Length;
while (record != null)
{
record = inputFile.ReadLine();
if(record == null)
{
break; //end of file
}
var fields = record.Split(FIELD_DEMIMITER);
var extraFieldCount = fields.Length - headerFieldCount;
if (extraFieldCount < 0)
{
//raise an error if fewer fields that we expect
throw new DataException(string.Format("Invalid record. {0} fields read, {1} fields in header.", fields.Length, headerFieldCount));
}
if (extraFieldCount > 0)
{
var newFields = new string[headerFieldCount];
//copy preceding good fields
for (var i = 0; i < BAD_FIELD_INDEX; ++i)
{
newFields[i] = fields[i];
}
//combine segments of bad field into single field
var sourceFieldIndex = BAD_FIELD_INDEX;
var combinedField = new System.Text.StringBuilder();
while (sourceFieldIndex <= extraFieldCount + BAD_FIELD_INDEX)
{
combinedField.Append(fields[sourceFieldIndex]);
if(sourceFieldIndex < extraFieldCount + BAD_FIELD_INDEX)
{
combinedField.Append(FIELD_DEMIMITER); //add delimiter back to field value
}
++sourceFieldIndex;
}
newFields[BAD_FIELD_INDEX] = combinedField.ToString();
//copy subsquent good fields
var targetFieldIndex = BAD_FIELD_INDEX + 1;
while (sourceFieldIndex < fields.Length)
{
newFields[targetFieldIndex] = fields[sourceFieldIndex];
++sourceFieldIndex;
++targetFieldIndex;
}
fields = newFields;
}
//create output record and copy fields
this.Output0Buffer.AddRow();
//*** change the code below to map source fields to the columns defined as script component output
Output0Buffer.ID = fields[0];
Output0Buffer.DESCRIPTION = fields[1];
Output0Buffer.VALUE = fields[2];
}
}
}
this.Output0Buffer.SetEndOfRowset();
}
Another thing you can do is import the text file into a single column (varchar(max)) staging table, and then use TSQL to parse the records and import them to your final destination table.

Trouble using ExcelRange.ToArray<>() in EPPlus

I am using EPPlus and don't seem to be able to get the ToArray<> method to work. I am trying to pull out an array of string variables of column headers in a worksheet.
My code...
public static string[] GetWshHeaders(string WbkNm)
{
using (ExcelPackage package = new ExcelPackage(new FileInfo(WbkNm)))
{
ExcelWorksheet wsData = package.Workbook.Worksheets.First();
int noHdrs = wsData.Dimension.Columns;
ExcelRange hdrs = wsData.Cells[1, 1, 1, noHdrs];
string[] wsHdrs = hdrs.ToArray<string>();
return wsHdrs;
}
}
Intellisense flags the hdrs variable in the line string[] wsHdrs = hdrs.ToArray();. The message is: 'ExcelRange' does not contain a definition for 'ToArray' and the best extension method overload 'Enumerable.ToArray(IEnumerable)' requires a receiver of type 'IEnumerable'.
I have played around with any number of variations of the above code but, well, I wouldn't be posting this question if I had hit upon the correct syntax.
Any help would be appreciated!
ExcelRange has ExcelRangeBase as a base class which is declared list this:
public class ExcelRangeBase : ExcelAddress, IExcelCell, IDisposable, IEnumerable<ExcelRangeBase>, IEnumerator<ExcelRangeBase>
{
....
}
So you are trying to use Linq to do an implicit cast from IEnumerable<ExcelRangeBase> to String[] which will not work. You need to use a Select to get the Value properties of each range object. And since each Value property is of type Object you will need to call its ToString() method:
string[] wsHdrs = hdrs
.Select(erb => erb.Value.ToString())
.ToArray();
The above will get you the array of strings you are looking for but keep in mind that you are loosing information since Value can be a mix of numeric types and string. Not a problem if you are only interested in, say, simply printing their content. If you plan to write them back in some way to excel you will have everything as string.

How to extract an unknown amount of text from a file

This sort of ties back to a question I had earlier about a regex to search for a method containing a particular string, and someone suggested I use this MS tool called Roslyn but it's not available for VS2010 since 2012 came out.
So I'm writing this small utility to keep a list of every file in my solution that contains a particular method declaration (something like 3k of the 25k files overload this method). Then I simply want to filter that list of files to only ones that contain += inside the body of the method.
static void DirSearch(string dir)
{
string[] files = Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories);
foreach (var file in files)
{
var contents = File.ReadAllText(file);
if (contents.Contains("void DetachEvents()"))
{
//IF DetachEvents CONTAINS += THEN...
WriteToFile(file);
}
}
}
This method iterates over all the folders and writes the file name to a text file if it contains the key method, but I have no idea how to extract just whatevers in the method body, since it's overloaded all 3K instances of the method are different.
Would the best approach to be get the index of the method name, then the index of each { and } until I encounter the next accessor modifier (signifying I've gotten to the end of DetachEvents)? Then I could just search between indexOfMethod and indexOfEndMethod for +=.
But it sounds really sloppy, I was hoping someone might have a better idea?
Do you have to do this in code? Is this a one time utility to identify the problem methods? Why not use something like Notepad++ and it's Find in Files capabilities. You can filter your find pretty easily and even apply regex (I think). From there you can copy the results which include the file name (i.e. someclassfile.cs) and get a list from there.
I wrote this really sloppy winform that lets the user type in the folder to the code base, the method name, and the flagrant text they're looking for. Then it loops over every file in the directory and calls this method on a string that contains all the text of the file. It returns true if the user-entered flagrant data is present, then the method that calls this adds the file its on to a list. Anyways, here's the major code:
private bool ContainsFlag(string contents)
{
int indexOfMethodDec = contents.IndexOf(_method);
int indexOfNextPublicMethod = contents.IndexOf("public", indexOfMethodDec);
if (indexOfNextPublicMethod == -1)
indexOfNextPublicMethod = int.MaxValue;
int indexOfNextPrivateMethod = contents.IndexOf("private", indexOfMethodDec);
if (indexOfNextPrivateMethod == -1)
indexOfNextPrivateMethod = int.MaxValue;
int indexOfNextProtectedMethod = contents.IndexOf("protected", indexOfMethodDec);
if (indexOfNextProtectedMethod == -1)
indexOfNextProtectedMethod = int.MaxValue;
int[] indeces = new int[3]{indexOfNextPrivateMethod,
indexOfNextProtectedMethod,
indexOfNextPublicMethod};
int closestToMethod = indeces.Min();
if (closestToMethod.Equals(Int32.MaxValue))
return false; //This should probably do something different.. This condition is true if the method you're reading is the last method in the class, basically
if (closestToMethod - indexOfMethodDec < 0)
return false;
string methodBody = contents.Substring(indexOfMethodDec, closestToMethod - indexOfMethodDec);
if (methodBody.Contains(_flag))
return true;
return false;
}
Plenty of room for improvement, this is mostly just a proof-of-concept thing that'll get used maybe twice per year internally. But for my purposes it worked. Should be a good starting-point for something more sophisticated if anyone needs it.

How to access individual fields within struct array

I'm trying to complete an assignment and I'm having trouble with the following (I have worked on this for the last 12 hours). Please help.
I have opened a file, saved the file into an struct array. Can access each element of the struct but don't know how I can access each individual field. i.e
Struct
//struct to hold the hand values
public struct CurrentHand
{
public char cardSuit;
public int cardValue;
}
I need to extract the cardValue into an array or variables so I can evaluate each record i.e. is the hand a pair or two pair etc. I have no idea how to do this. From what I have found its not possible to access each field, is this true?
//Open file and load data into array
public static void LoadHandData(CurrentHand[] handData, string fileName)
{
string input = ""; //temporary variable to hold one line of data
string[] cardData; //temporary array to hold data split from input
//Open the file and read the contents
StreamReader readHand = new StreamReader(fileName);
input = readHand.ReadLine(); //one record
cardData = input.Split(' '); //split record into fields
for (int counter = 0; counter < handData.Length; counter++)
{
handData[counter].cardSuit = Convert.ToChar(cardData[counter *2]);
handData[counter].cardValue = Convert.ToInt16(cardData[counter *2 +1]);
}
readHand.Close();
}
To obtain an array containing the values of the cards you hold in your hand, you can do:
var values = handData.Select(x=>x.cardValue).ToArray();
var seeds = handData.Select(x=>x.cardSuit).ToArray();
By the way, your struct should be called Card or something like that, since an Hand is supposed to be a collection of cards. The name you gave to it is just confusing and makes your code less readeable.
Your problem is not clear to me. anyway you can access invidual fields using .
try this...
CurrentHand.cardValue
using above you can get and set value for CurrentHand structure.

Read data from combined Excel columns/rows using C#

I'm trying to read data from an Excel document in C# using Microsofts COM Interop.
So far, I'm able to load the document and read some data from it. However, I need to read data from two different columns and output these as json (for a jquery ajax call)
I've made a quick prototype of how my Excel document is structured with the hope that it's a bit easier to explain ;-)
The method I have is called GetExcelDataByCategory(string categoryName) where the categoryName parameter would be used to find which column to get the data from.
So, i.e., if I'm making the call with "Category 2" as parameter, I need to get all the values in the C columns rows and the corresponding dates from the A column, so the output will look like this:
Which then needs to be transformed/parsed into JSON.
I've searched high and low on how to achieve this, but with no luck so far :-( I'm aware that I can use the get_Range() method to select a range, but it seems you need to explicitly tell the method which row and which column to get the data from. I.e.: get_Range("A1, C1")
This is my first experience with reading data from an Excel document, so I guess there's a lot to learn ;-) Is there a way to get the output on my second image?
Any help/hint is greatly appreciated! :-)
Thanks in advance.
All the best,
Bo
This is what I would do:
using Excel = Microsoft.Office.Interop.Excel;
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open("path to book");
Excel.Worksheet xlSheet = xlWorkbook.Sheets[1]; // get first sheet
Excel.Range xlRange = xlSheet.UsedRange; // get the entire used range
int numberOfRows = xlRange.Rows.Count;
int numberOfCols = xlRange.Columns.Count;
List<int> columnsToRead = new List<int>();
// find the columns that correspond with the string columnName which
// would be passed into your method
for(int i=1; i<=numberOfCols; i++)
{
if(xlRange.Cells[1,i].Value2 != null) // ADDED IN EDIT
{
if(xlRange.Cells[1,i].Value2.ToString().Equals(categoryName))
{
columnsToRead.Add(i);
}
}
}
List<string> columnValue = new List<string>();
// loop over each column number and add results to the list
foreach(int c in columnsToRead)
{
// start at 2 because the first row is 1 and the header row
for(int r = 2; r <= numberOfRows; r++)
{
if(xlRange.Cells[r,c].Value2 != null) // ADDED IN EDIT
{
columnValue.Add(xlRange.Cells[r,c].Value2.ToString());
}
}
}
This is the code I would use to read the Excel. Right now it reads every column that has the heading (designated by whatever is in the first row) and then all the rows there. It isn't exactly what you asked (it doesn't format into JSON) but I think it is enough to get you over the hump.
EDIT: Looks like there are a few blank cells that are causing problems. A blank cell will be NULL in the Interop and thus we get errors if we try to call Value2 or Value2.ToString() since they don't exist. I added code to check to make sure that the cell isn't null before doing anything with it. It prevent the errors.
for Excel-parsing and creation you can use ExcelDataReader: http://exceldatareader.codeplex.com/
and for json you can use json.net: http://json.codeplex.com/
Both are fairly easy to use. Just have a look at the websites.

Categories

Resources